Turno MCP

A Model Context Protocol server for the Turno (TurnoverBnB) v2 API. Lets any MCP-enabled assistant — Claude Desktop, claude.ai, Cursor, ChatGPT, etc. — call all 49 Turno API tools on your behalf. Fully stateless: your credentials are never persisted server-side.

Setup — 4 steps

  1. Get your Secret Key from Turno
    In the Turno dashboard, open API → Tokens → "Create New Token". The Secret Key is the long eyJ… JWT shown once on creation — copy it before leaving the page; Turno won't display it again.
  2. Get your Partner ID
    Same page — scroll all the way to the bottom for the line "Here is your Partner ID:" followed by a UUID. Both values are required on every API call.
  3. Exchange them for a bearer
    Open the enrollment form →

    We validate the credentials with a live Turno /userinfo call, then return a self-contained signed JWT bearer that embeds them (encrypted with an HKDF-derived AES-256-GCM key). Nothing is persisted server-side. The bearer is valid for 24 hours — re-issue it anytime via the form or the /token endpoint (tab below).

  4. Wire the bearer into your MCP client
    Endpoint: https://turno.nlma.io/mcp

    Settings → Connectors → Add custom connector. Paste the plain endpoint URL — claude.ai auto-discovers our OAuth config, opens a consent form where you paste your Turno credentials, and manages token refresh forever after:

    https://turno.nlma.io/mcp

    No ?token= suffix. claude.ai walks through OAuth 2.1 + PKCE automatically via our metadata and never asks you again after the first consent.

    In ~/.claude_desktop_config.json (Mac) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

    {
      "mcpServers": {
        "turno": {
          "command": "npx",
          "args": [
            "mcp-remote",
            "https://turno.nlma.io/mcp",
            "--header",
            "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.PASTE_WHOLE_JWT_HERE"
          ]
        }
      }
    }

    Restart Claude Desktop after saving.

    In ~/.claude/settings.json under mcpServers:

    "turno": {
      "type": "http",
      "url": "https://turno.nlma.io/mcp",
      "headers": {
        "Authorization": "Bearer eyJhbGciOiJIUzI1NiJ9.PASTE_WHOLE_JWT_HERE"
      }
    }

    Restart Claude Code after saving.

    In .cursor/mcp.json at your project root (or ~/.cursor/mcp.json globally):

    {
      "mcpServers": {
        "turno": {
          "command": "npx",
          "args": [
            "mcp-remote",
            "https://turno.nlma.io/mcp",
            "--header",
            "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.PASTE_WHOLE_JWT_HERE"
          ]
        }
      }
    }

    For testing or non-MCP-aware tooling:

    npx mcp-remote https://turno.nlma.io/mcp \
      --header "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.PASTE_WHOLE_JWT_HERE"

    Skip the /enroll form and mint a bearer directly from your Turno credentials. Standard RFC 6749 client_credentials:

    curl -X POST https://turno.nlma.io/token \
      -H 'Content-Type: application/json' \
      -d '{
        "grant_type":  "client_credentials",
        "client_id":   "YOUR_TBNB_PARTNER_UUID",
        "client_secret": "YOUR_TURNO_SECRET_KEY"
      }'
    # → { "access_token": "eyJ…", "token_type": "Bearer", "expires_in": 86400 }

    HTTP Basic auth (Authorization: Basic base64(partner_id:secret_key)) also works. Use this from CI scripts or any tool that manages OAuth token refresh for you.

Revoking a bearer

There's no kill-switch on our end because there's no state to kill. To invalidate a bearer:

Status

Endpoint: https://turno.nlma.io/mcp · Health: /health