Part 4 of 6 Other agents, other clients
An agent, with nothing installed
By the end of this chapter, Keanu's coding agent reads and files the team's work through Subroutine's tools, from a machine with nothing of Subroutine's on it: no Python, no package, no configuration file of ours.
This is the case where somebody else runs the instance and has given you an address and a token. Everything the agent needs is those two things.
The work so far
Laurence's instance is serving at https://tasks.example.com. It holds two workspaces, because the team's work and the studio's are kept apart, and Keanu works in both, with one credential for the command line:
uv tool install subroutine
subroutine init --workspace metacortex --instance-name MetaCortex
printf 'public_url = "https://tasks.example.com"\n' >> ~/.config/subroutine/config.toml
subroutine serve
subroutine add "Fix the deploy script"
subroutine document create "Deploys go out on Tuesdays, never on Fridays" --type decision --body "A Friday deploy means a weekend on call, and we have had two."
subroutine document create "Do not retry the payment webhook in the handler" --type dead_end --body "It doubles charges under load. The queue does it, once."
subroutine document create "The staging database is restored nightly from a redacted dump" --type design --body "Names and card fragments are replaced before it leaves production."
subroutine document create "The checkout drops about one in four hundred sessions" --type finding --body "Measured over a fortnight. Cause unknown."
subroutine document create "Move the queue to a managed service" --type decision --status draft --body "Not settled."
subroutine workspace create studio "Studio"
subroutine user create keanu --name "Keanu Reeves" --workspace metacortex
subroutine user add keanu --role member --workspace studio
subroutine token create --username keanu --title "Keanu's desktop" | tee /tmp/for-keanu
What your machine needs
Claude Code and Git, and nothing of ours. The marketplace is a repository, so adding it clones one:
Not checked: the build does not run this.
claude plugin marketplace add simonholliday/subroutine
claude plugin install subroutine-remote@subroutine
There are two plugins and they are not interchangeable. subroutine-remote reaches an instance at an address, which is this chapter. The other one, in Install it, and tell your agent, starts Subroutine as a program on your own machine, and needs it installed there.
Two fields, and where they go
The plugin takes the address, ending in /mcp, and the token. Set them in a terminal: run claude, then /plugin inside the session, and choose the plugin. Once set, every session reads them, editor included.
That terminal is not optional. /plugin is not in the VS Code extension, and claude plugin has no configure subcommand, so a plugin can be installed from the editor and cannot be set up there. Nothing says so: the install reports success, the fields are never asked for, and the only sign is that no tools appear. Claude Code's documentation covers plugins and marketplaces.
Then start a new session. Tools are attached when a session begins, so one that was open while you configured the plugin keeps the tool list it started with, and will show no tools however right the settings are.
The address says which workspace
Where your account reaches more than one workspace, nothing can guess which of them your agent means, so the address says:
Not checked: the build does not run this.
https://tasks.example.com/mcp?workspace=metacortex
Ask which workspace your work is in at the same time as you ask for the token. Without it the tools are all there and the first read is refused:
export ACME_TOKEN=$(grep -o 'sr_[A-Za-z0-9_-]*' /tmp/for-keanu | tail -1)
{
"mcpServers": {
"metacortex": {
"url": "https://tasks.example.com/mcp",
"headers": {
"Authorization": "Bearer ${ACME_TOKEN}"
}
}
}
}
subroutine_list()
An agent can recover by naming a workspace on every call, but it will do that all session and the next session will start over. One word in the address settles it for good.
A credential that reaches one workspace and no other has nothing to be ambiguous about, and needs nothing in the address. Whoever issues it chooses: subroutine token create pins a token to a workspace when it is told to.
The configuration that works
{
"mcpServers": {
"metacortex": {
"url": "https://tasks.example.com/mcp?workspace=metacortex",
"headers": {
"Authorization": "Bearer ${ACME_TOKEN}"
}
}
}
}
That is the whole of it, and it is not particular to Claude Code: any client that takes a URL and a header can reach the instance. There is no session to establish and nothing to install at either end. The plugin is a convenience that fills those two fields in for you.
Ask the agent who it is on that instance, which is what subroutine_whoami is for:
subroutine_whoami()
And the read that was refused a moment ago now answers:
subroutine_list()
Whose name its work carries
The token is yours, so its work is yours. An agent holding the credential you were given files and changes things as you, and Subroutine has no way to tell that a person was not at the keyboard. That is fine for one person working with one agent, and it is worth knowing rather than discovering in a history.
An account of the agent's own is the answer, and it is issued by whoever runs the instance rather than by you. It comes later in this guide, with the rest of what an instance owner does.
A credential that only reads
Ask for the narrowest credential that does the job. If the agent is to report on work rather than change it, the token can say so, and Laurence issues one:
subroutine token create --username keanu --profile observer --title "Keanu's reporting agent" | tee /tmp/for-reading
Point a client at that one instead:
export READING_TOKEN=$(grep -o 'sr_[A-Za-z0-9_-]*' /tmp/for-reading | tail -1)
{
"mcpServers": {
"metacortex": {
"url": "https://tasks.example.com/mcp?workspace=metacortex",
"headers": {
"Authorization": "Bearer ${READING_TOKEN}"
}
}
}
}
It reads the same work, and a write is refused rather than quietly dropped:
subroutine_add(text="Something it should not be able to file")
What a client gets without the plugin
The plugin brings a skill, which is the practice rather than the tools: when to file work before starting it, how to ask what can be started rather than what merely exists, and the difference between a comment and a document. It ships with the plugin, so it reaches wherever the plugin does.
The instance offers four documents to every client, as MCP resources, whatever brought it: agent-guide, written for an agent arriving with nothing; worked-examples; vocabulary, this installation's own words; and conventions, the decisions this workspace has taken. A client with no skill has those to work from.
What binds it, before its first write
conventions is the one of those four worth knowing by name. It is what this workspace has settled, assembled by the instance when a client asks, so an agent arriving with no history is handed the rules it is about to be held to instead of discovering them by breaking one.
It is a resource rather than a tool, which means a client reads it the way it reads a file. Here is the same material through a tool, which is how you would ask for it from a client that does not do resources, and how the build can check it:
subroutine_list(filter={"type.in": "decision,spec,design,dead_end", "status.eq": "active"})
Two things this workspace holds are not in that answer, and both absences are the point.
The finding is missing. The checkout drops about one in four hundred sessions is true, and it binds nobody: it describes rather than decides, which is why Decisions, findings and dead ends found that a finding never reaches an item's Read first either. The same boundary, in a second place.
The draft decision is missing too. Move the queue to a managed service is written down and not settled, and a rule nobody has agreed to should not arrive looking like one. Writing it is the act, so a document is in force from the moment it exists unless its author says otherwise, which is what --status draft says.
Titles, not bodies. A decision's title states its conclusion, so the index reads on its own and a client fetches the one it needs with subroutine_show. A resource that inlined every document would spend the context it is trying to save.
One caveat, and it is the reason the resource exists. The call above names active, which is a status key a workspace may rename; the resource asks by category instead, so it keeps working where this filter would quietly start returning nothing. Reach for subroutine://conventions where your client can, and treat the call above as the way in for a client that cannot.
Claude on the web
An instance in claude.ai, or in a desktop app as a connector, is not built, and neither plugin runs there. It is a different problem rather than a bigger one: a connector's traffic comes from Anthropic's servers rather than from your machine, so the instance would have to be reachable from the public internet, and the credential could not be a token you paste, because it is not your machine holding it.
So somebody who does not use a terminal has one route, and it is the other chapter's: install the program on their own machine and let the agent start it there.