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
POSTwithContent-Type: application/json. Both single-message and batched-array payloads are accepted. GET /{api_prefix}/_mcpreturns a health probe:{"status":"ok","protocol":"mcp-streamable-http","api":"..."}.DELETE /{api_prefix}/_mcpcloses the session referenced byMcp-Session-Idand always returns HTTP204(idempotent).- All JSON-RPC errors are returned inside the response body — HTTP status stays
200on 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:
- Client POSTs
initialize. The response sets theMcp-Session-Idheader to a server-generated UUID. - Every subsequent POST and DELETE must echo that UUID in the same header.
- Sessions expire after 1 hour of inactivity; every valid touch refreshes the TTL.
- A
DELETE /{api_prefix}/_mcpwith 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
resultobject.
- 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 carriesresource_id,title,path_template,required_parents,capabilities(subset ofget_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 Introspection —json_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. Supportedopvalues: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_typeis one oftext,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)
| Code | Message | When returned |
|---|---|---|
-32700 | Parse error | Request body is not valid JSON. |
-32600 | Invalid Request | Payload is neither object nor array, or a batched item is malformed. |
-32600 | Unsupported Media Type | Content-Type is not application/json (HTTP 415). |
-32601 | Method not found: <method> | Unknown JSON-RPC method. |
-32002 | Server not initialized | Missing or expired Mcp-Session-Id. |
-32000 | Too many initialize calls | Per-IP rate limit exceeded (see Rate limiting). |
-32000 | Session store unavailable | Backing session store is unreachable; transient — retry. |
-32000 | Runtime context missing | Router/environment context could not be loaded for this API. |
Tool-level errors (inside result.structuredContent)
error_code | Hint summary |
|---|---|
unknown_resource | resource_id does not exist; call discover_resources. |
missing_parent_id | A nested resource_id template requires parents values not provided. |
access_denied | The folder behind this resource_id is not readable by the supplied API key. |
invalid_operator | Filter op is not one of the supported values. |
unknown_tool | params.name does not match any tool in tools/list. |
internal_error | Unexpected tool-level failure; retry or contact support. |
HTTP-level
| Status | Body | When 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. |
Related Endpoints
- Router Introspection — flat catalog of REST routes; complementary to
tools/listfor clients that prefer plain HTTP. - Schema Introspection — JSON Schema for a specific folder; equivalent to the
describe_resourcetool. - Search — REST equivalent of the
search_recordstool. - List Resources — REST equivalent of
query_records. - Get Resource — REST equivalent of
get_record.