Deploy the agent behind the Gateway
Mount the conversational operator agent behind the Gateway as a chat API, and drive a session through an approval-gated write
Overview
@tetherto/mdk-plugin-agent mounts @tetherto/mdk-agent behind the Gateway as a chat API.
Enabling the plugin brings session, message, and approval routes with it, and every write the agent proposes pauses for an operator's
decision. The agent itself still reaches fleet data the way any AI agent does,
over an MCP server reachable at agent.mcp.url — standalone, or the Gateway's own auto-generated one; this plugin only gives a
human operator a chat surface to talk to it through.
This is one of two ways to run the agent. If you've already run it standalone, it's the same agent with HTTP on it, not a second product: the same provider, the same MCP client, the same approval gate, just reached over sessions instead of a REPL.
Prerequisites
- The Gateway is running
- A model is served on this machine, or reachable on another, because the plugin dials the provider rather than starting one
- An MCP tool server is reachable, so the agent has fleet tools to call
The Gateway never starts a model. config.agent.provider is a URL it dials, so a first session against a baseURL with nothing
behind it fails with ERR_AGENT_UNAVAILABLE. Serving a model locally with QVAC covers the install, the first-run
download, and the flags a lazily loaded model needs.
Mount the plugin
Either:
1.1 A Select it during onboarding
Run mdk onboard and select mdk-plugin-agent from its Gateway plugin catalog.
mdk onboard's wizard supports creating a compliant mdk.yaml from scratch, interactively.
Its entry carries a real repoPath (backend/plugins/agent), not a stub, so selecting it installs
a working plugin rather than a placeholder.
1.1 B Add it to an existing mdk.yaml
Already have one from a previous onboarding run? Add the plugin under spec.gateway.plugins by hand, with the model
provider, the MCP url, and the approval timeout under agent. Once published, that directory is
node_modules/@tetherto/mdk-plugin-agent; in this monorepo checkout it is
backend/plugins/agent. For example, a standalone gateway carrying just this plugin, reaching across
to the full-site example's MCP tool server:
apiVersion: mdk/v1
kind: Stack
metadata:
name: agent-gateway
spec:
workers: []
gateway:
port: 3847
plugins:
- package: "@tetherto/mdk-plugin-agent"
config:
agent:
provider: { kind: qvac, model: qwen3-4b, baseURL: http://127.0.0.1:11500/v1 }
mcp: { url: http://127.0.0.1:3008/mcp }
approvalTimeoutMs: 1200001.2 Run it
Once the mdk.yaml names the plugin, either way, run mdk run all.
A stack built for just this plugin declares no Workers, so all boots a Kernel it never actually uses
(it only proxies chat to full-site's separate MCP server) alongside the Gateway.
No auth plugin means every request binds to a single local operator, so a perimeter-trusted deployment gets the full chat and
approval flow with no identity setup at all. A missing config.agent block answers 503 ERR_AGENT_UNAVAILABLE instead of
failing to load.
Create a session and send a message
Use the port your mdk.yaml gave the gateway: 3847 in the example above. Note that
full-site's own gateway (3007) never carries the agent plugin; this is a separate gateway, reaching
across to full-site's MCP tool server on 3008.
curl -X POST http://localhost:<port>/agent/sessions
# {"sessionId":"..."}
curl -N -X POST http://localhost:<port>/agent/sessions/<id>/messages \
-H 'Content-Type: application/json' \
-d '{"text":"how many miners are on the site?"}'The response streams as text/event-stream. A read-only question ends in tool_call, tool_result, token, and done
events, each stamped with the turn's turnId and a monotonic seq.
Approve a write
A write action pauses the turn instead of running it:
event: pending_approval
data: {"type":"pending_approval","name":"act_device","args":{"ref":"whatsminer-0","action":"reboot"},"approvalId":"..."}Decide it from the paused stream's approvalId:
curl -X POST http://localhost:<port>/agent/sessions/<id>/approvals/<approvalId> \
-H 'Content-Type: application/json' \
-d '{"approved":true}'Approving resumes the same stream: the tool runs for real, and the turn continues to its token and done events. Rejecting,
or letting the approval window expire, resolves to false, and the write never runs.
Troubleshooting
- Sessions and messages work, but the agent never calls a tool.
agent.mcp(or itsurl) is missing from the config — see the plugin's troubleshooting entry for the fix - Every other failure (
503,404,409,400) maps to a specific cause and fix in the plugin's error reference npm cifails to resolve a stack that mounts only this plugin.@tetherto/mdk-plugin-agentdeclares@tetherto/mdk-agentas a required dependency, not an optional peer, so the agent package must be installed alongside it fornpm cito resolve.
Next steps
- Read the agent plugin's route reference: session, message, and approval routes, plus the manifest's
setupfields - Understand the underlying agent: the model, its fleet tools, and the eval battery that scores it
- Submit and approve write actions from a React app, for the UI-driven shape of this same approval gate