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 #

PathMethodBehaviour
/mcp/*GETReturns 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/*POSTJSON-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.txtGETRendered 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.

MethodBehaviour
initializeHandshake. Returns {protocolVersion, serverInfo: {name: "husaria"}, capabilities}.
tools/listEnumerate registered tools.
tools/callExecute 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/listEnumerate registered resources.
resources/readFetch 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.

ToolReturns
husaria.schemaFull Snapshot as JSON — every table, column, FK, relationship, action, delegation the engine knows about.
husaria.llms-txtRendered llms.txt — same content as GET /llms.txt, plumbed through the MCP content-array format.
husaria.list-actionsNames of registered webhook actions. Names only — no auth config, no webhook URLs, nothing the LLM doesn't need.
husaria.list-delegationsNames of registered REST delegations. Same posture.

Resources #

URIContent
husaria://llms.txtSame as the husaria.llms-txt tool.
husaria://schema/jsonSame 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_PORT unset → no listener at all.
  • Not a write surface (yet). Read-only tools by design. Write tools land once they can route through internal/graphql/execution with 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 generic host: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_SECRET set): MCP requires the admin secret in X-Husaria-Admin-Secret. A missing or wrong secret returns 401 Unauthorized. If you've issued a helper-specific secret via HUSARIA_HELPER_ADMIN_SECRET, use that instead; otherwise the helper falls back to HUSARIA_ADMIN_SECRET.
  • Open mode (HUSARIA_ADMIN_SECRET unset): 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.