MCP (Model Context Protocol)
-
Inbound HTTP
Optional embedded MCP HTTP transport, stateless by default.
-
Safety
DNS rebinding protection, host/origin allowlists, optional API key.
-
Defaults
default_top_k,default_modefor tri-brid retrieval.
Keep Stateless
mcp.stateless_http=true is recommended; clients provide full context each call.
Allowlists
Use mcp.allowed_hosts and mcp.allowed_origins with wildcards like *:* only in development.
Auth
Set mcp.require_api_key=true and pass Authorization: Bearer $MCP_API_KEY in production.
Configuration (Selected)
| Field | Default | Meaning |
|---|---|---|
mcp.enabled | true | Enable embedded MCP HTTP server |
mcp.mount_path | /mcp | Path prefix |
mcp.public_base_url | http://127.0.0.1:58012 | Externally reachable origin MCP clients connect to; the server appends the mount path |
mcp.stateless_http | true | Stateless handling per request |
mcp.json_response | true | Prefer JSON over text |
mcp.enable_dns_rebinding_protection | true | Prevent DNS rebinding |
mcp.allowed_hosts | localhost:* | Allowed Host header values |
mcp.allowed_origins | http://localhost:* | Allowed Origin values |
mcp.require_api_key | false | Enforce API key on requests |
mcp.default_top_k | 20 | Default top_k for search/answer tools |
mcp.default_mode | tribrid | Retrieval mode when not provided |
Status Endpoint
Structured, fail-closed search errors
The MCP search tool never returns a partial result. When retrieval cannot complete, it answers with isError=true and a structured payload (MCPSearchToolResult in server/models/tribrid_config_model.py) that carries exactly one of:
- result
- The successful
ChunkMatch[]rows. An empty list is a valid successful result — a corpus with no hits is not an error. - error
- One of the typed failure details the HTTP API already returns, so agent clients branch on stable codes instead of parsing prose:
| Error detail | code | Meaning |
|---|---|---|
DependencyUnavailableDetail | dependency_unavailable | A required runtime dependency (Postgres, Qdrant, Neo4j, embedding provider) is unavailable. |
RequiredRetrievalLegFailureDetail | required_retrieval_leg_failed | A requested retrieval leg failed at execution time (for example, the configured embedding model is missing). |
RetrievalContractMismatchDetail | embedding_contract_mismatch / sparse_contract_mismatch | The corpus's stored index contract conflicts with the current configuration; re-index. |
The same errors surface as typed HTTP failures on the probe route: POST /api/mcp/probe answers 503 for the first two and 409 for a contract mismatch, with the detail under detail — so HTTP clients and MCP clients share one error vocabulary. The integration tests prove the fail-closed contract end to end over the real mounted transport (tests/integration/test_dependency_outage_asymmetric.py, tests/integration/test_required_retrieval_leg_contract.py): a Neo4j outage or a contract mismatch returns the same typed detail as /api/search, never partial rows.
If you're not sure
Treat isError=true as "retry after remediation or re-index", never as "the search came back empty". A genuine empty result arrives with isError=false and result: [].
flowchart LR
Client["MCP Client"] --> HTTP["MCP HTTP\n(mount /mcp)"]
HTTP --> RAG["Tri-brid Retrieval"] Legacy stdio
python_stdio_available indicates whether the stdio transport can be launched by clients (no daemon).