Querying via MCP
Moʻolelo exposes a single tool — search — over the Model Context Protocol. Any MCP-compatible client (Claude Desktop, custom LLM agents, IDE plugins) can call it directly without writing a Vespa query or talking to the database.
What the search tool does
Behind the scenes the tool calls the internal query-coordinator service, which:
- Runs hybrid retrieval (BM25 + dense embedding) against the search index.
- Filters the candidate set against the calling user's source-system permissions.
- Detokenizes any vault-tokenized fields the caller is authorized to see.
- Records one audit row for the query.
The agent gets back a ranked list of chunks with scores and per-result provenance.
Tool input
| Field | Type | Required | Notes |
|---|---|---|---|
query | string | yes | Natural-language or keyword query |
limit | integer | no | Max results (default 50, max 200) |
rank_profile | string | no | default, hybrid, bm25, or semantic |
sources | array of string | no | Restrict to e.g. ["github", "confluence"] |
tenant | string | no | Tenant ID; defaults to the server's tenant |
subject | string | no | Calling user's canonical ID; recorded in audit |
reason | string | no | Rationale for the call; recorded in audit |
Tool output
A SearchOutput JSON object plus a textual summary. Each hit carries:
chunk_id— stable Vespa document ID.source,instance,record_type— where it came from in the source system.text— the chunk text (post-detokenization).score— relative ranking score; comparable within one response, not across rank profiles.structural_metadata— heading path, file path, line range, etc., for rendering provenance.
Connecting from Claude Desktop
Two transports are supported: stdio (one process per Claude Desktop instance) and http (Streamable HTTP, the standard production transport).
HTTP — recommended for self-hosted deployments:
In Claude Desktop's mcp.json:
{
"mcpServers": {
"moolelo": {
"url": "https://search.moolelo.example/mcp/"
}
}
}Where search.moolelo.example is the ingress in front of the mcp-server Service.
stdio — for local development only:
{
"mcpServers": {
"moolelo-local": {
"command": "/usr/local/bin/mcp-server",
"env": {
"MCP_SERVER_TRANSPORT": "stdio",
"MCP_SERVER_COORDINATOR_TARGET": "localhost:7443",
"MCP_SERVER_COORDINATOR_INSECURE": "true"
}
}
}
}Connecting from a custom agent
Use any MCP client SDK (Python, Go, TypeScript). The Go SDK example:
client := mcp.NewClient(&mcp.Implementation{Name: "my-agent"}, nil)
session, _ := client.Connect(ctx, &mcp.StreamableClientTransport{
Endpoint: "https://search.moolelo.example/mcp/",
}, nil)
result, _ := session.CallTool(ctx, &mcp.CallToolParams{
Name: "search",
Arguments: map[string]any{
"query": "rate limiting middleware",
"limit": 10,
"sources": []string{"github"},
},
})Authentication
The MCP transport itself is authenticated by your ingress / API gateway — typically OAuth2 against your IDP. The MCP server reads the calling identity from the gateway-supplied headers (or the SPIFFE workload ID for service-to-service) and forwards it as the Caller on every coordinator request.
Source-system permissions are applied by the coordinator. An MCP client can never see records the calling user wouldn't see directly in GitHub / Confluence / SharePoint. See Security model for the full contract.
Latency and limits
- The MCP server applies a hard cap of 200 hits per call (configurable via
MCP_SERVER_MAX_LIMIT). - Each coordinator call is bounded at 10 seconds; partial results are not returned on timeout.
- The coordinator scales horizontally on CPU; for very high QPS, increase its replica count via the Helm values.

