Fabriq
1.x
Docs/Fabriq/MCP Adapter
Open

Reading3 min
Updated2 Aug 2026
Sourcev1/(ai-agents)/mcp.mdx

The forgeext/agentmcp extension is the common interface: it surfaces the agent toolkit's tools over MCP (JSON-RPC 2.0) so any agent — Claude, a Python agent, another service — can drive recall, the read primitives, guarded writes, and a live watch stream. Go agents call the toolkit in-process; external agents reach the same handlers over HTTP. One brain, two front doors.

Wiring01

The extension depends on the fabriq extension and builds its toolkit in Start. It is auth-agnostic — the host attaches auth via forwarded route options, exactly like the live-query gateway:

app.RegisterExtension(forgeext.New(reg, forgeext.WithWorker(true)))
app.RegisterExtension(agentmcp.NewMCP(fabriqExt,
    agentmcp.WithEmbedder(myEmbedder),
    agentmcp.WithWritePolicy(agent.WritePolicy{Allow: map[string][]command.Op{
        "note": {command.OpCreate, command.OpUpdate},
    }}),
    agentmcp.WithRouteOptions(pkgAuth.RequirePermission("agent:use")), // host auth
))
Warning

The endpoint introduces no authority of its own. With no auth attached via WithRouteOptions, recall, writes, and graph_traverse are open to any caller the router admits. Always attach the host's auth middleware in production.

The JSON-RPC surface02

A single POST endpoint (default /api/v1/agent/mcp) speaks minimal JSON-RPC 2.0:

  • tools/list → the tool descriptors (name, description, inputSchema).

  • tools/call → run a tool by name with its arguments.

curl -X POST /api/v1/agent/mcp -d '{
  "jsonrpc": "2.0", "id": 1,
  "method": "tools/call",
  "params": { "name": "recall", "arguments": {
    "query": "overheating pumps", "budget": 8000, "entities": ["asset"]
  }}
}'

The tool surface (six tools): recall, vector_similar, search, graph_traverse, get, and remember. Protocol errors (unknown method, bad params, unparseable body) return a JSON-RPC error object; tool execution errors (a denied write, a failed embed) return a normal result with isError: true and the reason — so the agent sees them and can react, exactly as MCP intends.

Note

The JSON-RPC dispatcher is transport-neutral (dispatch.go imports no Forge): it is a pure

Unsupported
<code>
  []byte → []byte
</code>
handler over a
Unsupported
<code>
  *agent.Toolkit
</code>
, which is why the full request/response path is unit-testable without a running server. The Forge shell is the thin controller that pipes the request body through it.

Watch over SSE03

Request/response tools can't stream, so watch is a separate Server-Sent Events endpoint (default /api/v1/agent/mcp/watch). POST a subscribe scope and the connection streams query.Deltas as SSE events until the client disconnects:

curl -N -X POST /api/v1/agent/mcp/watch -d '{"entity":"asset","scope":"tenant"}'
event: asset.created
data: {"aggId":"…","version":1,"type":"asset.created", …}

It reuses fabriq's SSE writer and the conflated subscription stream; teardown is context-scoped — when the client disconnects, the request context cancels and the hub releases the subscription. In-process Go agents get the same stream as a channel via Toolkit.Watch.

What's integration-gated04

The dispatcher, the read/write tools, and the SSE route are exercised by unit/HTTP tests with in-memory fakes. The pieces that require a live stack (Postgres + Redis) — the extension's Start (fabriq.Open), the embedding worker's consume loop, and the live Watch → Redis delta stream — are verified in the integration suite. See Deployment and Observability.