Step 6: Deploy it, and call it

    A deployed tool answers MCP over Streamable HTTP at POST /api/mcp/<tool> and plain REST at POST /api/v1/tools/<tool>/search. Same data, same rules, same answer. Both authenticate with Authorization: Bearer <key>, and a key opens exactly one tool.

    Over MCP your agent sees two tools: search_<tool> and get_<tool>. You never write a schema for them. It is generated from your contract, so the model picks allergen values from an enum of your list and literally cannot ask for a value you never defined.

    Searching the menu, over REST
    POST /api/v1/tools/menu/search
    Authorization: Bearer cc_live_...
    Content-Type: application/json
    
    {
      "query": "something warm",
      "exclude": { "allergens": ["peanut"] },
      "limit": 5
    }
    What comes back
    {
      "results": [
        {
          "id": "9f1c...",
          "fields": {
            "name": "Tomato soup",
            "description": "Slow-roasted tomatoes finished with a swirl of cream",
            "price": 6.5,
            "allergens": ["dairy"]
          },
          "origin": { "kind": "imported" },
          "unknown_safety_fields": [],
          "receipts": {
            "allergens": {
              "method": "rule",
              "confidence": 1,
              "contract_version": 3,
              "decided_by": null
            }
          }
        }
      ],
      "next_cursor": null,
      "meta": {
        "release": 7,
        "contract_versions": { "allergens": 3 },
        "applied_exclusions": { "allergens": ["peanut"] },
        "total": 1,
        "ranking": "query",
        "warnings": []
      }
    }

    Read meta before you read the results. applied_exclusions is the server telling you what it actually enforced, which is the caller's request merged with whatever the key pins. release and contract_versions are how a logged answer stays explainable months later.

    1. 1

      query

      Words, up to 200 characters. Ranked by meaning and by matching text together.

    2. 2

      similar_to

      The id of a result. Returns records like it, using the anchor and facet fields from the release.

    3. 3

      filters

      Ordinary narrowing: a value, a list, or a {gte, lte} range on numbers and dates.

    4. 4

      exclude

      Safety exclusions, enforced in the query. Offered only on safety fields. Read the next section.

    5. 5

      sort

      Up to three fields. Missing values always sort last, either direction.

    6. 6

      limit and cursor

      Page size, and the opaque cursor from next_cursor. Send the same other parameters with it.

    7. 7

      include_receipts

      On by default. Turn it off for a smaller payload once you trust the pipeline.

    Adding it to Claude Code
    claude mcp add --transport http menu https://cloudcrane.in/api/mcp/menu \
      --header "Authorization: Bearer cc_live_..."
    Adding it to any client that takes a JSON list of servers
    {
      "mcpServers": {
        "menu": {
          "url": "https://cloudcrane.in/api/mcp/menu",
          "headers": { "Authorization": "Bearer cc_live_..." }
        }
      }
    }
    1. 1

      Claude Desktop, Cursor, Windsurf

      The JSON above. Some clients spell the block servers rather than mcpServers, and some want a type of http alongside the url.

    2. 2

      n8n

      MCP Client Tool node, transport HTTP Streamable, Bearer authentication. If it connects but lists no tools, check the transport really is Streamable: this endpoint offers no SSE stream, and a client that silently falls back to SSE finds nothing.

    3. 3

      Docker MCP Gateway

      Add it as a remote server with the url and an Authorization header, which the gateway passes through to the endpoint.

    4. 4

      Dify and Flowise

      Add the endpoint as an MCP server with a Bearer token.

    5. 5

      LangChain

      MultiServerMCPClient with transport streamable_http and a headers dict.

    6. 6

      OpenAI Agents SDK

      MCPServerStreamableHttp with the url and headers.

    Your own tool's snippet is in the app. Each of these appears on the tool's page once you deploy it, with your url and a real key already filled in, so none of it has to be copied off a web page and edited.