MCP server

Native Model Context Protocol server at https://mcp.turtleqr.com/mcp. 12 tools. JSON-RPC 2.0 over HTTP POST. Bearer token authentication.

Back to Docs hub or jump to REST API reference.

What it does

The MCP server lets AI agents operate TurtleQR as a native capability. Your agent can create codes, read scan counts, change destinations, and export analytics without leaving the conversation. The tools proxy directly to the REST API: anything you can do in the dashboard you can do from a chat.

Supported clients: Claude Desktop, Claude.ai (via extension), Cursor, ChatGPT (via custom GPT), and any MCP-compatible client that can make HTTP POST requests.

Get an API key

The MCP server authenticates with a TurtleQR API key. Generate one in the dashboard:

  1. Sign in at app.turtleqr.com.
  2. Go to API keys.
  3. Click Create API key. Give it a label (e.g., "Claude desktop"). Copy the token when it appears; it is shown once.

The token starts with qr_live_ (production) or qr_test_ (test workspaces).

Connect Claude Desktop

Add this block to ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, or %APPDATA%Claudeclaude_desktop_config.json on Windows:

{
  "mcpServers": {
    "turtleqr": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://mcp.turtleqr.com/mcp",
        "--header",
        "Authorization:Bearer qr_live_YOUR_KEY_HERE"
      ]
    }
  }
}

Restart Claude Desktop. Try: "Create a TurtleQR code pointing to https://example.com."

The mcp-remote package bridges Claude's local stdio transport to the remote HTTP endpoint. It does not require installation; npx fetches and caches it on first run.

Connect Cursor

Add to your Cursor MCP config at ~/.cursor/mcp.json (create the file if it does not exist):

{
  "mcpServers": {
    "turtleqr": {
      "url": "https://mcp.turtleqr.com/mcp",
      "headers": {
        "Authorization": "Bearer qr_live_YOUR_KEY_HERE"
      }
    }
  }
}

Cursor supports direct HTTP transports without mcp-remote. Reload the Cursor window after saving. You should see TurtleQR listed under Settings > MCP.

Connect ChatGPT (custom GPT)

ChatGPT does not yet support the MCP transport natively, but you can expose the same tools via a custom GPT action that calls the MCP endpoint directly.

  1. In ChatGPT, open Explore GPTs > Create.
  2. Go to Configure > Actions > Add action.
  3. Set the server URL to https://mcp.turtleqr.com/mcp.
  4. Set the authentication type to API Key, key in header, header name Authorization, value Bearer qr_live_YOUR_KEY_HERE.
  5. Import the OpenAPI schema from mcp.turtleqr.com/openapi.json.

The custom GPT can then call any of the 12 tools directly from the chat.

Generic MCP client (JSON-RPC curl)

The protocol is JSON-RPC 2.0 over HTTP POST to https://mcp.turtleqr.com/mcp. No special client is required for testing.

# List available tools (no auth required for tools/list)
curl -s -X POST https://mcp.turtleqr.com/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq

# Confirm authentication and workspace scope
curl -s -X POST https://mcp.turtleqr.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer qr_live_YOUR_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": { "name": "whoami", "arguments": {} }
  }' | jq

# Create a QR code
curl -s -X POST https://mcp.turtleqr.com/mcp \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer qr_live_YOUR_KEY" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "create_qr",
      "arguments": {
        "targetUrl": "https://example.com/autumn-menu",
        "slug": "autumn-menu"
      }
    }
  }' | jq

Tool results are returned in the standard MCP content envelope: {"content":[{"type":"text","text":"..."}]}. Error results also include "isError": true.

Tool reference

12 tools are available. All tool calls require a valid Bearer token in the Authorization header except as noted.

create_qr

Create a new dynamic QR code. Returns the slug, short URL, and code id. The short URL resolves immediately at qr.turtleqr.com/{slug}.

Input schema

FieldTypeRequiredDescription
targetUrlstring (URI)YesThe URL the QR redirects to. Must be http(s).
slugstringNoCustom slug ([a-zA-Z0-9_-], max 100). Auto-generated if omitted.
fallbackUrlstring (URI)NoWhere scanners go after expiry or scan cap.
expiresAtISO 8601 datetimeNoExpiration timestamp. Pro+ only.
scanCapintegerNoMaximum scan count. Pro+ only.
typeenumNo (default: url)url | menu | event | coupon | social | business-page

Example invocation

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_qr",
    "arguments": {
      "targetUrl": "https://myshop.com/summer-sale",
      "slug": "summer-sale",
      "fallbackUrl": "https://myshop.com/"
    }
  }
}

list_qr_codes

List QR codes in the active workspace. Returns up to 100 codes, newest first. Each item includes slug, target URL, status, and timestamps.

Input schema

No input fields required.

Example invocation

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "list_qr_codes", "arguments": {} }
}

get_qr_code

Read a single code by id. Returns full details including expiration and scan cap.

Input schema

FieldTypeRequiredDescription
idstringYesThe code id, e.g. code_abc123.

update_qr_target

Change a code's destination without reprinting. The KV redirect cache is invalidated; new scans see the new value within 60 seconds globally.

Input schema

FieldTypeRequiredDescription
idstringYesCode id.
targetUrlstring (URI)NoNew destination URL.
fallbackUrlstring or nullNoNew fallback URL. Pass null to clear.
expiresAtdatetime or nullNoNew expiration. Pass null to clear. Pro+ only.
scanCapinteger or nullNoNew scan cap. Pass null to clear. Pro+ only.
statusstring enumNoactive | expired | suspended

Example invocation

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "update_qr_target",
    "arguments": {
      "id": "code_abc123",
      "targetUrl": "https://myshop.com/new-page"
    }
  }
}

delete_qr_code

Soft-delete a code. Status is set to deleted; scanners hit the workspace fallback. Recoverable by an admin.

Input schema

FieldTypeRequired
idstringYes

get_qr_image

Render a QR code as an image. Returns base64-encoded PNG or SVG text. Useful for the agent to display, attach, or save the QR image without requiring a separate download step. The QR encodes the short URL so the underlying target can change without reprinting.

Input schema

FieldTypeRequiredDescription
idstringYesCode id, e.g. code_abc123.
formatstring enumNo (default: png)png | svg
sizeintegerNo (default: 1024)Pixel size. Range: 128 to 2048.

get_qr_scans

List recent scans for a code. Returns up to limit scans, newest first. Each scan includes country, region, city, device, OS, and browser.

Input schema

FieldTypeRequiredDescription
idstringYesCode id.
limitintegerNo (default: 50)Min 1, max 500.

get_qr_analytics

Aggregated scan analytics: totals, time series, breakdowns by country, device, OS, browser, hour-of-day, and day-of-week. Use this for dashboard views or to answer questions like "how is my conference QR performing?"

Input schema

FieldTypeRequiredDescription
codeIdstringYesCode id.
rangestring enumNo (default: 30d)7d | 30d | 90d | all

Example invocation

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_qr_analytics",
    "arguments": { "codeId": "code_abc123", "range": "7d" }
  }
}

list_recent_scans

Paginated individual scan events, newest first. Use for activity feeds or browsing scan history. Each scan includes: scannedAt, country, region, city, device, os, browser, referer. Pass cursor (the previous response's nextCursor) to fetch the next page.

Input schema

FieldTypeRequiredDescription
codeIdstringYesCode id.
limitintegerNo (default: 50)Min 1, max 200.
cursorstringNoOpaque cursor from a previous response's nextCursor. Omit for the first page.

bulk_create_qr_codes

Create up to 50 QR codes in one call. Returns both successfully-created codes and individual failures, so the agent can report partial success. For a single code, prefer create_qr.

Input schema

FieldTypeRequiredDescription
codesarrayYes1 to 50 items. Each item: {targetUrl, slug?, label?}.

Example invocation

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "bulk_create_qr_codes",
    "arguments": {
      "codes": [
        { "targetUrl": "https://example.com/page-1", "label": "Page 1" },
        { "targetUrl": "https://example.com/page-2", "label": "Page 2" }
      ]
    }
  }
}

export_qr_scans_csv

Export scan history as CSV for one QR code. Returns the CSV text inline in the csv field, plus a suggested filename. The agent should write the csv field to a .csv file. Capped at 100,000 rows. Pro+ only.

Input schema

FieldTypeRequiredDescription
codeIdstringYesCode id.
rangestring enumNo (default: 30d)7d | 30d | 90d | all

whoami

Returns { workspaceId, authenticated, codeCount, server } for the authenticated API key. The server sub-object includes { name, version }. Useful as a connectivity probe and for confirming the active workspace.

Input schema

No input fields required.

Example invocation

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "whoami", "arguments": {} }
}

Troubleshooting

SymptomLikely causeFix
Error 401 Missing or invalid Bearer token. Check that your API key starts with qr_live_ or qr_test_ and is included as Authorization: Bearer ....
Error 403 Key does not have access to the requested resource. The code or workspace may belong to a different key. Call whoami to confirm the scoped workspace id.
Error 402 plan_limit_exceeded Requested action requires Pro+. Features like expiration dates, scan caps, password gates, and CSV export require a Pro plan. See pricing.
Error 409 slug_taken Custom slug is already in use. Omit the slug field to auto-generate one, or choose a different value.
Tool not listed in Claude Config file not saved or Claude not restarted. Verify the config path, confirm the JSON is valid, and restart Claude Desktop fully.
npx mcp-remote hangs Outbound HTTPS blocked by a firewall or proxy. Confirm that mcp.turtleqr.com is reachable on port 443 from the machine running Claude Desktop.

What is next for MCP

OAuth 2.1 (PKCE) is live for Claude.ai web. Use the /oauth/authorize flow at auth.turtleqr.com to grant access without sharing an API key. Self-hosted MCP clients (e.g. Cursor) can use a workspace API key via the Authorization: Bearer qr_live_* header.

Additional planned tools: visual customization (logo, color, frame), folder management, routing rule creation, and webhook management.