MCP Server

Method: GET | POST | DELETE /{api_prefix}/_mcp Auth: Flux API Key — same read permission model as /_search and /{resource} endpoints. Anonymous traffic is accepted on public APIs.

Each Flux API prefix exposes an embedded Model Context Protocol server over Streamable HTTP. The server publishes a fixed read-only tool catalog — discovery, schema, single fetch, list, and search — backed by the same data and permissions as the REST endpoints.


URL Pattern

{GET|POST|DELETE} https://{environment_key}.fxns.io/{api_prefix}/_mcp

Example:

POST https://7c9h4pwu.fxns.io/blog/_mcp

The MCP endpoint is per-API: every prefix gets its own catalog, scoped to that prefix's connected folders. There is no global /_mcp.


Transport

  • Protocol: MCP Streamable HTTP 2025-03-26.
  • Wire format: JSON-RPC 2.0 over POST with Content-Type: application/json. Both single-message and batched-array payloads are accepted.
  • GET /{api_prefix}/_mcp returns a health probe: {"status":"ok","protocol":"mcp-streamable-http","api":"..."}.
  • DELETE /{api_prefix}/_mcp closes the session referenced by Mcp-Session-Id and always returns HTTP 204 (idempotent).
  • All JSON-RPC errors are returned inside the response body — HTTP status stays 200 on successful transport, regardless of any JSON-RPC error inside.

Server identification

The initialize response carries:

{
  "protocolVersion": "2025-03-26",
  "capabilities": {"tools": {}},
  "serverInfo": {"name": "foxnose-flux-mcp", "version": "0.1.0"}
}

Sessions

The server is stateful per logical conversation. A session lifecycle:

  1. Client POSTs initialize. The response sets the Mcp-Session-Id header to a server-generated UUID.
  2. Every subsequent POST and DELETE must echo that UUID in the same header.
  3. Sessions expire after 1 hour of inactivity; every valid touch refreshes the TTL.
  4. A DELETE /{api_prefix}/_mcp with the session id removes it explicitly.

Without a valid session, every JSON-RPC method other than initialize returns:

{
  "jsonrpc": "2.0",
  "id": ...,
  "error": {
    "code": -32002,
    "message": "Server not initialized",
    "data": {"hint": "Call initialize first and reuse Mcp-Session-Id header."}
  }
}

Batched calls — initialize plus other methods in a single POST array — work the same way; the server issues the id at the start of the batch and applies it to all subsequent messages in that batch.


Rate limiting

The initialize method is rate-limited per client IP using a fixed 60-second window. The default cap is 60 initializations per minute per IP. Other methods (tools/list, tools/call, ping) are not rate-limited at this layer.

Client IP is resolved in this order: CF-Connecting-IP header → first hop of X-Forwarded-For → request socket address.

When the cap is exceeded, the server responds with:

{
  "jsonrpc": "2.0",
  "id": ...,
  "error": {
    "code": -32000,
    "message": "Too many initialize calls",
    "data": {"hint": "Slow down; sessions are rate-limited per client IP."}
  }
}

HTTP status remains 200.


JSON-RPC Methods

  • Name
    initialize
    Type
    method
    Description

    Negotiates protocol version and issues a session. No params required.

  • Name
    ping
    Type
    method
    Description

    Health probe inside the session. Returns an empty result object.

  • Name
    tools/list
    Type
    method
    Description

    Returns the tool catalog with input JSON schemas. Stable per server version.

  • Name
    tools/call
    Type
    method
    Description

    Invokes a tool. Params: { name: string, arguments: object }. The result is wrapped in an MCP tool envelope (see Tool results).

Notifications (JSON-RPC messages without an id) return HTTP 202 with no body. Unknown methods return JSON-RPC error -32601 "Method not found".


Tool Catalog

Five tools, all read-only. The exact names, descriptions, and input schemas are returned by tools/list. Names below match params.name for tools/call.

  • Name
    discover_resources
    Type
    tool
    Description

    List available Flux resources and capabilities for this API prefix. Input: {}. Output (structuredContent): { api, resources: [...], usage_rules: [...] } where each resource carries resource_id, title, path_template, required_parents, capabilities (subset of get_one, get_many, search), description, schema_ref.

  • Name
    describe_resource
    Type
    tool
    Description

    Return the JSON schema and searchability for a resource. Input: { resource_id, parents? }. Output mirrors Schema Introspectionjson_schema, searchable_fields, non_searchable_fields, actions, path, etc.

  • Name
    get_record
    Type
    tool
    Description

    Fetch one record by id. Input: { resource_id, record_id, parents?, locales?, populate? }.

  • Name
    query_records
    Type
    tool
    Description

    List records by filters / sort / cursor. Input: { resource_id, parents?, filters?, sort?, limit?, cursor?, locales?, populate? }. Filters use { field, op, value } triplets. Supported op values: eq, ieq, ne, not_eq, gt, gte, lt, lte, in, not_in, contains, icontains, startswith, istartswith, endswith, iendswith, between, includes, iincludes.

  • Name
    search_records
    Type
    tool
    Description

    Full search by text and/or vector. Input: { resource_id, query, search_type?, parents?, filters?, limit?, cursor?, locales?, populate? }. search_type is one of text, semantic, hybrid, vector_boosted.

Tool results

Every successful tools/call returns:

{
  "content": [
    {"type": "text", "text": "<JSON-encoded structured content>"}
  ],
  "structuredContent": { /* the tool's structured payload */ },
  "isError": false
}

On a tool-level error (unknown_resource, missing_parent_id, access_denied, invalid_operator, etc.), isError is true and structuredContent is { error_code, message, hint }. This is not a JSON-RPC error — the result field is still populated and error is absent. JSON-RPC errors are reserved for protocol-level problems (missing session, unknown method, transport failures).


Enablement

The MCP endpoint is enabled by default for every Flux API prefix and can be turned off per API. When disabled, every method (GET, POST, DELETE) on /{api_prefix}/_mcp returns HTTP 404:

{ "detail": "MCP endpoint is disabled for this API" }

A deploy-wide kill-switch (MCP_ENABLED=false on the Flux server) takes precedence over the per-API flag; in that case the legacy message "MCP endpoint is disabled" is returned instead.

Data endpoints (search, list, get) are unaffected — only /_mcp is gated by this flag. Toggle the per-API flag via Management API — APIs (mcp_enabled field); the change propagates to Flux within seconds via the router cache invalidation channel.


Example: end-to-end session

A typical client opens a session, lists tools, calls one, then closes the session.

# 1. Initialize and capture the session id from the response header.
SID=$(curl -sS -i -X POST "https://7c9h4pwu.fxns.io/blog/_mcp" \
  -H "Authorization: Secure <access_key>:<signature>" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
  | awk -F': ' 'tolower($1)=="mcp-session-id" {gsub(/\r/,"",$2); print $2}')

# 2. Reuse the session id on every subsequent request.
curl -sS -X POST "https://7c9h4pwu.fxns.io/blog/_mcp" \
  -H "Authorization: Secure <access_key>:<signature>" \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SID" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

curl -sS -X POST "https://7c9h4pwu.fxns.io/blog/_mcp" \
  -H "Authorization: Secure <access_key>:<signature>" \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SID" \
  -d '{
        "jsonrpc":"2.0","id":3,"method":"tools/call",
        "params":{
          "name":"search_records",
          "arguments":{
            "resource_id":"articles",
            "query":"machine learning",
            "search_type":"hybrid",
            "limit":3
          }
        }
      }'

# 3. Close the session.
curl -sS -X DELETE "https://7c9h4pwu.fxns.io/blog/_mcp" \
  -H "Authorization: Secure <access_key>:<signature>" \
  -H "Mcp-Session-Id: $SID"

Response (tools/call)

{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "content": [
      {"type": "text", "text": "{\"items\":[...],\"page\":{...}}"}
    ],
    "structuredContent": {
      "items": [
        {
          "id": "kDLT9jjrcAzh",
          "data": {"title": "...", "content": "...", "status": "published"},
          "_sys": {"key": "kDLT9jjrcAzh", "relevance": 0.503, "created_at": "..."}
        }
      ],
      "page": {"has_more": false, "next_cursor": null, "previous_cursor": null, "returned": 1, "limit": 3},
      "execution_info": {"applied_search_type": "hybrid", "search_mode": "hybrid"}
    },
    "isError": false
  }
}

Batched alternative — combine initialize and tools/call in one POST:

Batched request body

[
  {"jsonrpc":"2.0","id":1,"method":"initialize","params":{}},
  {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"discover_resources","arguments":{}}}
]

Errors

JSON-RPC body errors (HTTP 200)

CodeMessageWhen returned
-32700Parse errorRequest body is not valid JSON.
-32600Invalid RequestPayload is neither object nor array, or a batched item is malformed.
-32600Unsupported Media TypeContent-Type is not application/json (HTTP 415).
-32601Method not found: <method>Unknown JSON-RPC method.
-32002Server not initializedMissing or expired Mcp-Session-Id.
-32000Too many initialize callsPer-IP rate limit exceeded (see Rate limiting).
-32000Session store unavailableBacking session store is unreachable; transient — retry.
-32000Runtime context missingRouter/environment context could not be loaded for this API.

Tool-level errors (inside result.structuredContent)

error_codeHint summary
unknown_resourceresource_id does not exist; call discover_resources.
missing_parent_idA nested resource_id template requires parents values not provided.
access_deniedThe folder behind this resource_id is not readable by the supplied API key.
invalid_operatorFilter op is not one of the supported values.
unknown_toolparams.name does not match any tool in tools/list.
internal_errorUnexpected tool-level failure; retry or contact support.

HTTP-level

StatusBodyWhen returned
202(empty)JSON-RPC notification (no id) acknowledged.
204(empty)Successful DELETE of a session.
400{"jsonrpc":...,"error":{"code":-32700,...}}Malformed JSON body on POST.
404{"detail":"MCP endpoint is disabled"}Global MCP_ENABLED=false deploy-wide kill-switch.
404{"detail":"MCP endpoint is disabled for this API"}Per-API toggle is off (see Enablement).
405{"detail":"Method not allowed"}Method other than GET, POST, DELETE.
415{"jsonrpc":...,"error":{"code":-32600,"message":"Unsupported Media Type"}}Content-Type is not JSON.

  • Router Introspection — flat catalog of REST routes; complementary to tools/list for clients that prefer plain HTTP.
  • Schema Introspection — JSON Schema for a specific folder; equivalent to the describe_resource tool.
  • Search — REST equivalent of the search_records tool.
  • List Resources — REST equivalent of query_records.
  • Get Resource — REST equivalent of get_record.

Was this page helpful?