MCP server
The API server includes a Model Context Protocol (MCP) server at the path /mcp. MCP is an open protocol through which AI assistants and other MCP clients discover the tools that a server offers and call those tools. This page describes the role of the MCP server, the transport, sessions and authentication, the three tools, the relation between MCP search and REST search, and the current limits. Integrators need this page to connect an AI assistant to Foundation4, and administrators need this page to issue API keys for MCP clients.
Role of the MCP server
The MCP server lets an AI assistant find the pipelines that an API key can read and retrieve fragments from those pipelines. The assistant uses the fragments as context for an answer that the assistant's own language model generates. Foundation4 LLMs and agents take no part in an MCP exchange. The MCP server provides the retrieval step of retrieval-augmented generation (RAG) for an AI assistant outside Foundation4.
The MCP server runs inside the API server process. Each tool calls the same operation as a REST endpoint, reads the same data and requires the same permissions. MCP tools lists the input schema, the output and the errors of each tool.
Transport
The MCP server uses the streamable HTTP transport of MCP. A client sends JSON-RPC messages to one URL, $FOUNDATION4_URL/mcp, without a trailing slash. The path /mcp/ returns HTTP 404.
| Method | Purpose |
|---|---|
POST | Sends one JSON-RPC request, notification or response |
GET | Opens an event stream on which the server can send messages within a session |
DELETE | Ends a session |
Every POST carries the header Content-Type: application/json and an Accept header that lists both application/json and text/event-stream. A request without these headers returns HTTP 406 or HTTP 415 with a plain-text body.
The server answers every JSON-RPC request with the content type text/event-stream, as server-sent events (SSE). One event holds the JSON-RPC response in the data field, and the server then closes the stream. Within a session, an event with an empty data field and a retry value of 3000 milliseconds precedes the response. A notification receives HTTP 202 with an empty body. Responses carry the x-request-id header described in API conventions.
Host names
The MCP server accepts a request only when the Host header of the request names localhost, 127.0.0.1 or ::1, with any port. The MCP library applies this check by default to protect servers that run on a workstation against Domain Name System (DNS) rebinding. A request addressed to any other host name returns HTTP 403 with the plain-text body Forbidden: Host header is not allowed. The refused host names include the host name of the Foundation4 ingress or HTTPRoute and the name of the API server Service. The REST API has no such restriction.
An MCP client therefore reaches the MCP server through a port forward to the client's computer, at the URL http://localhost:8080/mcp. API and dashboard access describes the port forward.
Sessions
The MCP server supports the MCP protocol revisions 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25 and 2026-07-28. The initialize request names the revision that the client uses. The server answers with the same revision when the server supports that revision, and with 2025-11-25 otherwise.
For the revisions up to 2025-11-25, the server keeps a session for each client:
- Start. The response to
initializecarries the session identifier in theMcp-Session-Idheader. Any other request without a session identifier returns HTTP 422 with the bodyUnexpected message, expect initialize request. - Use. Every later request carries the
Mcp-Session-Idheader. The client sends thenotifications/initializednotification afterinitialize, as the protocol requires. - End. A session ends with a
DELETErequest, after 5 minutes without a message, or when the API server replica restarts. A request for an unknown or ended session returns HTTP 404 with the bodyNot Found: Session not found, and the client starts a new session withinitialize. - Replica. A session exists only in the memory of the API server replica that created the session. With more than one API server replica, a request that reaches another replica returns HTTP 404, as for an ended session.
For the revision 2026-07-28, which removes sessions from the protocol, the server answers each request independently and returns no session identifier. Each request then carries the per-request protocol metadata and headers that the revision defines, including the client capabilities in _meta. MCP tools shows such a request.
Authentication
The MCP server uses the API keys of the REST API. Every HTTP request to /mcp, including initialize, notifications, GET and DELETE, carries the x-api-key and x-api-key-secret headers described in Authenticate. A request without valid headers returns HTTP 401 with the JSON error body of the REST API, before the MCP server reads the request. Errors lists the authentication messages.
A session does not replace authentication. Each tool call runs with the permissions of the API key in the headers of the HTTP request that carries the call, and every request of a session can carry a different key.
Tools
The MCP server offers three tools. Each tool corresponds to a REST operation and requires the permissions of that operation, described in Access control.
| Tool | Result | REST equivalent | Required permissions |
|---|---|---|---|
get_pipelines | The pipelines that the API key can read, optionally filtered by a text in the name, 10 per call by default | GET /pipelines | Read on each pipeline returned |
get_pipeline_classifications | The classifications of a pipeline and the inheritance pairs | GET /pipelines/{id}/classifications | Read on the pipeline, and * in the allow-list |
search_knowledge | Fragments ranked by a similarity, maximal marginal relevance (MMR) or full-text search | POST /pipelines/{id}/search | Read and execute on the pipeline |
A key for an AI assistant that searches one pipeline holds read and execute permission (5) on that pipeline. A pipeline outside the key's permissions is missing from the listing, and the other two tools return the error Pipeline not found for that pipeline. With an allow-list other than *, get_pipeline_classifications returns an error.
Each tool is annotated as read-only and non-destructive. Each result carries the tool output twice: as structured content, and as the same JSON in a text block for MCP clients that read text only.
The listing returns five fields for each pipeline: pipeline_id, name, description, embedding_model_id and default_text_splitter_id. The embedding_model_id field holds the pipeline's vector embedding identifier, the value that the REST API returns as default_vector_embedding_id, and not an embedding model identifier. The listing reports neither the embedding models of the pipeline nor whether full-text search is enabled.
MCP search and REST search
The search_knowledge tool runs the searches of POST /pipelines/{id}/search, described in Search and retrieval. The tool accepts a subset of the REST request:
| Aspect | REST search | search_knowledge |
|---|---|---|
| Pipeline | Path parameter | pipeline_id argument |
| Search types | similarity, mmr, search | similarity, mmr, search |
| Parameters | params object of the search type | The same params object, with the same defaults |
| Embedding model | params.embedding, or the pipeline's default embedding model | embedding_model_id argument, or the pipeline's default embedding model. params.embedding is ignored. |
| Classifications | String, array or object, with hierarchical or exact matching | Array of strings, with hierarchical matching. Names that the pipeline does not define are ignored. |
| Metadata filter | filters | Not available |
| Distance strategy | distance_strategy, cosine by default | Always cosine |
| Query embedding | Similarity and MMR search | Every search type |
| Full-text search on a pipeline without full-text search | HTTP 501 | An internal error |
hybrid and history | HTTP 501 | An internal error |
| Result | JSON array of fragments | results array of fragments with the same fields |
| Errors | HTTP status and JSON error body | JSON-RPC error, or a tool result marked as an error |
The parameters k, fetch_k, lambda_mult and threshold have the meanings and defaults that Search and retrieval describes. When a call includes a params object, a parameter that the object omits takes the value 0, so the call sets k, and for MMR search also fetch_k, explicitly. Scores follow the score semantics of REST search: a distance for similarity and MMR search, and a rank for full-text search. Search requests describes the parameters and the classification forms of REST search.
The tool computes a query embedding before every search, including full-text search. A full-text search through MCP therefore requires the pipeline's embedding model to answer and adds the latency of one embedding call. A full-text-only pipeline, which has no embedding model, cannot be searched through MCP: every call returns the error Missing embedding model ID, and the pipeline is searched through the REST API instead.
Current limits
The following limits apply in addition to the search differences above:
- Retrieval only. The tools list pipelines, read classifications and search. No tool adds or expires documents, changes an object, runs an agent or queries an LLM. These operations use the REST API.
- Tools only. The server declares the tools capability only. The server offers no MCP resources or prompts, so
resources/listandprompts/listreturn empty lists, and the server sends no notifications. - Header authentication. The MCP server does not implement the OAuth-based authorization of the MCP specification and publishes no authorization metadata. An MCP client connects only when the client can send custom HTTP headers with every request.
- Listing pages.
get_pipelinesreturns no total count and no page information. A client reads further pages with theoffsetargument until a call returns fewer pipelines thanlimit.
Example exchange
The following exchange starts a session, completes the initialization and runs a similarity search. The commands use the shell variables described in Authenticate, with FOUNDATION4_URL set to http://localhost:8080 through a port forward, and $PIPELINE_ID holding the identifier of the support-kb pipeline from First search.
The initialize request names the protocol revision and the client:
curl -i -X POST "$FOUNDATION4_URL/mcp" \
-H "x-api-key: $FOUNDATION4_API_KEY" \
-H "x-api-key-secret: $FOUNDATION4_API_SECRET" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": {"name": "curl", "version": "1.0"}}}'
The response has status 200. The following output is shortened to the status line, the session header and the events:
HTTP/1.1 200 OK
content-type: text/event-stream
mcp-session-id: f2b58b89-1909-471c-97f1-abeceeb3e88b
data:
id: 0
retry: 3000
data: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{}},"serverInfo":{"name":"rmcp","version":"3.1.4"},"instructions":"Foundation4.ai"}}
The result names the negotiated revision, the tools capability and the server information. Set a shell variable from the mcp-session-id header:
export MCP_SESSION_ID=<value of the mcp-session-id header>
The notifications/initialized notification completes the initialization:
curl -i -X POST "$FOUNDATION4_URL/mcp" \
-H "x-api-key: $FOUNDATION4_API_KEY" \
-H "x-api-key-secret: $FOUNDATION4_API_SECRET" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $MCP_SESSION_ID" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{"jsonrpc": "2.0", "method": "notifications/initialized"}'
The response has status 202 and an empty body. The tools/call request runs search_knowledge:
curl -X POST "$FOUNDATION4_URL/mcp" \
-H "x-api-key: $FOUNDATION4_API_KEY" \
-H "x-api-key-secret: $FOUNDATION4_API_SECRET" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: $MCP_SESSION_ID" \
-H "MCP-Protocol-Version: 2025-11-25" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "search_knowledge",
"arguments": {
"pipeline_id": "'"$PIPELINE_ID"'",
"query": "How do customers get their money back?",
"classifications": ["internal"],
"type": "similarity",
"params": {"k": 1}
}
}
}'
The response has status 200 and the following events:
data:
id: 0/0
retry: 3000
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"results\":[{\"classification\":\"internal\",\"document_id\":\"01a0ed0d-df3f-7f80-9a1b-2c3d4e5f6a7b\",\"expired_at\":null,\"id\":\"01a0ed0d-e007-7c3d-8e4f-6a7b8c9d0e1f\",\"metadata\":{\"product\":\"billing\"},\"order_id\":0,\"page_content\":\"Refunds for annual plans are prorated. Support agents must record the refund reason in the ticket before approving a refund.\",\"score\":0.6444976753595035,\"version\":1790683504447128}]}"}],"structuredContent":{"results":[{"classification":"internal","document_id":"01a0ed0d-df3f-7f80-9a1b-2c3d4e5f6a7b","expired_at":null,"id":"01a0ed0d-e007-7c3d-8e4f-6a7b8c9d0e1f","metadata":{"product":"billing"},"order_id":0,"page_content":"Refunds for annual plans are prorated. Support agents must record the refund reason in the ticket before approving a refund.","score":0.6444976753595035,"version":1790683504447128}]},"isError":false}}
id: 1/0
The result object of the second event, formatted, shows the three parts of a tool result:
{
"content": [
{
"type": "text",
"text": "{\"results\":[{\"classification\":\"internal\",\"document_id\":\"01a0ed0d-df3f-7f80-9a1b-2c3d4e5f6a7b\",\"expired_at\":null,\"id\":\"01a0ed0d-e007-7c3d-8e4f-6a7b8c9d0e1f\",\"metadata\":{\"product\":\"billing\"},\"order_id\":0,\"page_content\":\"Refunds for annual plans are prorated. Support agents must record the refund reason in the ticket before approving a refund.\",\"score\":0.6444976753595035,\"version\":1790683504447128}]}"
}
],
"structuredContent": {
"results": [
{
"classification": "internal",
"document_id": "01a0ed0d-df3f-7f80-9a1b-2c3d4e5f6a7b",
"expired_at": null,
"id": "01a0ed0d-e007-7c3d-8e4f-6a7b8c9d0e1f",
"metadata": {"product": "billing"},
"order_id": 0,
"page_content": "Refunds for annual plans are prorated. Support agents must record the refund reason in the ticket before approving a refund.",
"score": 0.6444976753595035,
"version": 1790683504447128
}
]
},
"isError": false
}
The fragment fields are the fields of a REST search result, in alphabetical order. The score is the distance that REST search returns for the same query, shown at full precision; Search requests shows the same fragment rounded to 0.6445. A failed tool call returns a JSON-RPC error object with a code and a message instead of result, as described in MCP tools.