Integrations
MCP for LLM tooling
MCP — Model Context Protocol bridge #
Husaria ships an optional JSON-RPC 2.0 bridge for LLM tooling at /mcp/*, plus a rendered /llms.txt document for schema discovery. Both live on a separate listener configured by HUSARIA_HELPER_PORT — not on the main engine's HTTP surface.
The MCP bridge is intended for cluster-internal LLM agents that need to introspect the schema, the actions catalogue, and the delegations catalogue without traversing the production CORS / auth chain Husaria mounts on /v1/graphql.
Why a separate port #
The main engine listener mounts production middleware (CORS, rate-limit, JWT, RLS context). The helperserver is operator tooling. Mixing them on one port would mean every operator-tooling request paid for production middleware, and every production request paid for the helperserver's auth setup. The trade-off is to run two listeners and let the orchestrator route each.
When HUSARIA_HELPER_PORT is unset, no helper listener is created. Pay zero overhead unless you opt in.
Configuration #
export HUSARIA_HELPER_PORT=8090
export HUSARIA_HELPER_CACHE_TTL=30s
# Optional — issue a separate admin secret to LLM tooling so
# the main HUSARIA_ADMIN_SECRET isn't shared with the agent.
export HUSARIA_HELPER_ADMIN_SECRET=$(openssl rand -hex 32)
When HUSARIA_HELPER_ADMIN_SECRET is unset, the helperserver falls back to HUSARIA_ADMIN_SECRET.
HTTP surface #
| Path | Method | Behaviour |
|---|---|---|
/mcp/* | GET | Returns a tiny service descriptor JSON: {service, version, transport}. Status 200. Lets a human poking at the URL know the endpoint is alive — leaks nothing sensitive. |
/mcp/* | POST | JSON-RPC 2.0 envelope. Every method runs through adminAuthorised BEFORE the body is parsed; 401 fires for anonymous probes without leaking an RPC envelope. |
/llms.txt | GET | Rendered llms.txt document — a structured catalogue of the schema for LLM consumers. Unauthenticated by llmstxt.org spec — the URL itself is meant to be discoverable by crawlers. Don't expose this endpoint on the open internet if your schema names are sensitive. |
Body cap on POST /mcp/*: 1 MiB. Stops a buggy or hostile client from walking the goroutine pool with giant payloads.
Authentication #
/mcp/* requires the helper-port admin secret. Constant-time check via hmac.Equal. Both X-Husaria-Admin-Secret and X-Admin-Secret headers are accepted. 401 fires before JSON parsing — anonymous probes never see an RPC envelope and can't probe whether RPC even exists.
/llms.txt is unauthenticated by spec — the llmstxt.org convention is that this URL is publicly discoverable. If your schema and field names are sensitive, keep the helper port off the public internet (cluster-internal ClusterIP service, VPN-only ingress, etc.) — the admin-secret gate is not what's protecting /llms.txt.
JSON-RPC methods #
Protocol version: 2025-06-18. Server identifies itself as husaria.
| Method | Behaviour |
|---|---|
initialize | Handshake. Returns {protocolVersion, serverInfo: {name: "husaria"}, capabilities}. |
tools/list | Enumerate registered tools. |
tools/call | Execute by name. Result is an MCP content array. Tool errors come back with isError=true in the result — not as RPC-level errors. The LLM can read the failure and reason about it. |
resources/list | Enumerate registered resources. |
resources/read | Fetch a resource by URI. |
notifications/* | Accepted with HTTP 202, no response body. |
Bad method names → -32601 (method not found). Bad params → -32602 (invalid params).
Tools #
All four are read-only. Write tools (husaria.query, husaria.run-sql) are deliberately out of scope until they can route through the executor with proper role enforcement.
| Tool | Returns |
|---|---|
husaria.schema | Full Snapshot as JSON — every table, column, FK, relationship, action, delegation the engine knows about. |
husaria.llms-txt | Rendered llms.txt — same content as GET /llms.txt, plumbed through the MCP content-array format. |
husaria.list-actions | Names of registered webhook actions. Names only — no auth config, no webhook URLs, nothing the LLM doesn't need. |
husaria.list-delegations | Names of registered REST delegations. Same posture. |
Resources #
| URI | Content |
|---|---|
husaria://llms.txt | Same as the husaria.llms-txt tool. |
husaria://schema/json | Same as the husaria.schema tool. |
The duplication between "tool" and "resource" is intentional — different MCP clients prefer different access patterns.
Where the snapshot comes from #
The helper's Snapshot is the same server.InitializationResult the main engine builds at startup, refreshed on the cadence of HUSARIA_HELPER_CACHE_TTL. Schema reloads (action / delegation extension reloads, console schema-reload requests) propagate into the helper at the next cache miss — there is no separate notification path.
What it is not #
- Not on the main engine's HTTP surface. Runs on its own port.
HUSARIA_HELPER_PORTunset → no listener at all. - Not a write surface (yet). Read-only tools by design. Write tools land once they can route through
internal/graphql/executionwith proper role enforcement. - Not an SSE transport. HTTP transport today. The protocol layer is identical, so an SSE upgrade later is a transport-only change.
- Not a public endpoint. Admin-secret gated; intended for cluster-internal LLM tooling. Don't expose it on the open internet.
Sample MCP client setup #
Tip. The console Settings tab generates these snippets for you with the running host + port baked in. Open
/console, go to Settings, scroll to Optional features — when the helper port is enabled the panel renders ready-to-paste configs with a Copy button on each. The rest of this section is the same content, parametrised on a generichost:port.
Claude Code (~/.claude.json) #
{
"mcpServers": {
"husaria": {
"type": "http",
"url": "http://host:port/mcp",
"headers": {
"X-Husaria-Admin-Secret": "<HUSARIA_ADMIN_SECRET>"
}
}
}
}
Or interactively: claude mcp add and follow the prompts.
Cursor (~/.cursor/mcp.json) #
{
"mcpServers": {
"husaria": {
"url": "http://host:port/mcp",
"headers": {
"X-Husaria-Admin-Secret": "<HUSARIA_ADMIN_SECRET>"
}
}
}
}
Fetching /llms.txt directly #
curl http://host:port/llms.txt
/llms.txt is unauthenticated by spec, so no X-Husaria-Admin-Secret header is needed.
Auth posture #
- Secured mode (
HUSARIA_ADMIN_SECRETset): MCP requires the admin secret inX-Husaria-Admin-Secret. A missing or wrong secret returns401 Unauthorized. If you've issued a helper-specific secret viaHUSARIA_HELPER_ADMIN_SECRET, use that instead; otherwise the helper falls back toHUSARIA_ADMIN_SECRET. - Open mode (
HUSARIA_ADMIN_SECRETunset): MCP accepts unauthenticated callers. NetworkPolicy / sidecar restriction is the only fence — don't expose the helper port to the open internet in open mode.
After the agent calls initialize, husaria.schema and husaria.llms-txt become available. The LLM can then ask informed questions about your database surface without needing direct GraphQL access.