Connect to the MemQL MCP server
The mcp node exposes the MemQL engine tool surface to MCP hosts (Claude Code,
and -- once OAuth lands, #1556 -- Claude Desktop / claude.ai). It speaks two
transports:
- stdio (default) -- the MCP host launches
memql-mcpas a local subprocess. Local only; there is no network listener and no bearer auth (the trust boundary is "you started the process"). - Streamable HTTP (
MEMQL_MCP_TRANSPORT=http) -- a network listener (POST/GET/DELETE /mcp) so a host can reach a hosted deployment over the network. Bearer auth is mandatory and default-deny: no valid token -> 401 before any engine work runs.
The capability tier (MEMQL_MCP_MODE) and acting role (MEMQL_MCP_ROLE) gates
are documented in build-tags.md;
this page is about connecting.
Connect Claude Code to a deployed cluster (remote HTTP)
A deployed cluster serves the MCP head at https://mcp.example.com/mcp (your
MCP host) at the authoring capability tier.
1. Mint a bearer token
The HTTP transport is wrapped with the per-node identity verifier, which
accepts only JWKS-verifiable identity JWTs. A service-account JWT
(class="service_account", #691) is the right credential for a machine client
like Claude Code:
# On the identity binary (locally: docker exec memql-identity ...; in the cloud:# an identity-side Job). Prints the JWT on stdout. 1-hour TTL.memql service-account-token mint --label claude-mcpA user session JWT (from the magic-link login) also works. See service-account-jwt.md for the full mint + rotation flow.
Not a PAT. A Personal Access Token (
mql_pat_...) is rejected by the per-node verifier -- PATs are validated only by the identity binary, and themcpnode is not the identity binary. Use a service-account or user JWT.
The role the token carries gates the tier (Gate B): the named read/exec tool
surface works for any authenticated role; define (authoring) and query
(inline) require the owner or developer role. Per-row authorization applies
to every tool on top of the tier.
2. Add the connector
claude mcp add --transport http memql-remote \ https://mcp.example.com/mcp \ --header "Authorization: Bearer <token from step 1>"Verify the tools resolve:
claude mcp list # memql-remote should show "connected"The token expires (1h for a service-account JWT); re-mint and re-add (or update the header) when it does.
Run + connect locally
stdio (the simple local path)
Build the engine-node binary and let the MCP host run it as a subprocess. stdio is the default transport, so no transport env is needed:
GOWORK=off make mcp # -> bin/memql-mcp (engine node, no product DSL) # Register it with Claude Code as a local subprocess. The binary runs the full# engine, so it needs the engine env (DB DSN + memql-secrets) the same way any node# does -- the simplest path is to point it at your local k3d cluster database# (via the postgres port-forward, make db).claude mcp add memql-local -- /absolute/path/to/bin/memql-mcpNo bearer auth on stdio: the protocol owns stdout (the binary redirects its own logs to stderr so the JSON-RPC wire stays clean), and the trust boundary is the local process.
Name the person the session acts as, or it runs no reflected tool. With no
bearer there is no token to take an identity from, so a stdio session is a
person only when MEMQL_MCP_USER names one (and holds a role only when
MEMQL_MCP_ROLE names one). A tool is called by an agent or by an
authenticated person (memql#5438), so a session with no identity is offered no
reflected DSL tool by tools/list and refused every one by tools/call:
claude mcp add memql-local \ -e MEMQL_MCP_USER=<your user id> -e MEMQL_MCP_ROLE=developer \ -- /absolute/path/to/bin/memql-mcpHTTP locally (exercise the remote path)
To test the network transport locally you also need a reachable identity verifier (HTTP mode refuses to start without one -- default-deny):
# Forward the identity service first: kubectl port-forward -n memql svc/identity 8085:8085MEMQL_MCP_TRANSPORT=http \MEMQL_MCP_HTTP_ADDR=:8090 \MEMQL_MCP_MODE=authoring \MEMQL_IDENTITY_VERIFIER_BASE_URL=http://localhost:8085 \ ./bin/memql-mcp# then: claude mcp add --transport http memql-local-http \# http://127.0.0.1:8090/mcp --header "Authorization: Bearer <local JWT>"The blessed local environment that provides identity + a database is the k3d
parity cluster (make up); mint a local token against the identity service via
its port-forward (localhost:8085).
A delegated app connects here too (epic memql#4358)
When the planner hands a Task to Claude Code or Codex on a user's own machine,
the engine mints a per-run class="service_account" JWT and sends it to the
cockpit along with https://mcp.<domain>/mcp. The cockpit writes the app's MCP
configuration naming both, runs the app, and deletes that file when the session
ends. From this server's point of view it is an ordinary service-account
bearer on the ordinary streamable-HTTP endpoint — MEMQL_MCP_MODE and the
role posture below apply unchanged.
The one thing worth knowing: its sub is the owning USER's id, not a
machine principal. That is deliberate — it makes row authz apply to the
delegated app exactly as it does to that user's browser, so the app sees what
they can see and nothing more. The credential cannot be revoked individually
(the verify path is JWKS-only and DB-free by design); its lifetime is
hard-capped at 8 hours instead, and a long run is handed a replacement rather
than a longer-lived bearer up front. See
local-apps.md.
Environment surface
| Variable | Default | Meaning |
|---|---|---|
MEMQL_MCP_TRANSPORT | stdio | stdio (local subprocess) or http (network listener). |
MEMQL_MCP_HTTP_ADDR | :8090 | Bind address for the HTTP transport. |
MEMQL_MCP_MODE | authoring | Capability tier (Gate A): sealed / authoring / inline. See build-tags.md. |
MEMQL_MCP_ROLE | (token claim) | The cluster role of the person the session acts as (Gate B) -- what a reflected tool's @requiresRank floor judges. On HTTP, empty = taken from the caller's verified token; a non-empty value pins a conservative deployment role. On stdio, empty = no role, which clears no rank floor. |
MEMQL_MCP_USER | (token claim) | The person the session acts as: the identity a reflected tool call runs under, and the owner session-authored constructs are scoped to. On HTTP, empty = taken from the caller's token. On stdio it is required to run a reflected DSL tool: a session with no identity is offered none and refused every tools/call (memql#5438). |
Auth + capability posture
- Default-deny. The HTTP transport refuses to start without a verifier, and
rejects any request without a valid identity JWT with
401. - Per-caller identity. On a deployed cluster the role/user are taken from the verified
token claims (the deployment does not pin
MEMQL_MCP_ROLE/USER), so each caller acts as themselves under the normal role + per-row-authorization rules. - Conservative tier first. A deployed cluster starts at
authoring;define/querystill requireowner/developer. Raise or lower the tier viaMEMQL_MCP_MODEas the posture is proven. - OAuth 2.1 for Claude Desktop / claude.ai custom connectors is a tracked follow-up (#1556); until then those hosts cannot use the bearer-header path that Claude Code uses.