π€Connect an Agent to Spindrift
Spindrift serves its command registry over the Model Context Protocol at
https://spindrift-control.lolwtf.dev/mcp, so an agent can drive the platform β list apps, dispatch a build, deploy, roll back β through the same commands the UI dispatches. Unlike Runbooks/Connect an Agent to the Wiki this surface is authenticated and it writes. Every tool is an act.Mint a token
The MCP endpoint takes an agent token, never the browser session cookie. They are both rows in
sessionsand thekindcolumn keeps them apart: a cookie presented as a bearer token is refused, and an agent token presented as a cookie is refused. That is deliberate β a cookie losesHttpOnly,SecureandSameSite=Laxthe moment it is copied into a config file, so the credential that lives in a file is a different credential.Sign in with your passkey, then Settings β Identity β Agent tokens β Mint an agent token.
The value is shown once and never again β the row holds only its SHA-256. Copy it from the panel before dismissing it. It lasts ninety days.
The same card lists the tokens you hold by mint date and revokes any of them. Revoking an agent token does not touch your browser session, which is the reason they are separate rows.
Connect
Claude Code:
claude mcp add --transport http spindrift https://spindrift-control.lolwtf.dev/mcp --header "Authorization: Bearer $TOKEN"Anything reading a JSON config file:
{ "mcpServers": { "spindrift": { "type": "http", "url": "https://spindrift-control.lolwtf.dev/mcp", "headers": { "Authorization": "Bearer THE-TOKEN" } } } }
The tools
One tool per command, generated from
apps/spindrift/src/commands/registry.ts. There is no allow-list to keep in step: a command that exists in the registry is a tool, and a tool that is not a command cannot be written.tools/listis the current answer and this page deliberately does not restate it.A command's refusal comes back as a tool result carrying its code and sentence β
NOT_DEPLOYABLE,STALE_EDIT,INVALID_INPUTwith the failing fields β not as a transport error, so an agent reads the same sentence an operator reads off a disabled button.Every tool is an act and there are no read-only tokens. What stands between an agent and a destructive command is the client's own per-call confirmation, so connect this to a client that asks.
Check it by hand
curl -s https://spindrift-control.lolwtf.dev/mcp \ -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools | length'A
401means the token is not an agent token β a browser cookie will get exactly this. A405means the request was not a POST; this endpoint has no SSE stream to open.
Changing it
The endpoint is
apps/spindrift/src/web/mcp-route.ts, tested inapps/spindrift/test/web/mcp-route.test.ts. It holds no domain logic:tools/listrenders each command's Zod input as JSON Schema andtools/callisdispatch. Adding a tool means adding a command.The credential lives in
apps/spindrift/src/auth/session.tswith its crossed-key tests inapps/spindrift/test/auth/agent-token.test.ts. The three commands behind it areapps/spindrift/src/commands/agent-tokens.tsand the card isapps/spindrift/src/web/views/auth/agent-tokens.tsx.
Linked references 2
Runbooks/Connect an Agent to Spindrift β and Spindrift is one at spindrift-control.lolwtf.dev/mcp, authenticated and writing
Runbooks/Connect an Agent to Spindrift β mint an agent token and point an MCP client at spindrift-control.lolwtf.dev/mcp to drive the platform