claude mcp add husk -- npx -y @husk-ai/mcpThat is the whole install. There is no second step, no account, and no config file to edit by hand.
Restart Claude Code and the model has a Linux machine: a shell, a filesystem that persists across tool calls, and a way to publish a port.
Verify it
npx -y @husk-ai/mcp --help # options, printed to stderrThe protocol itself is easy to check by hand — the server speaks JSON-RPC over stdio, so two lines of input list its tools:
Transports
Stdio. That is the whole list.
Nothing is created until it is used
Adding the server has to cost nothing, or people uninstall it. So:
- No machine at startup.
- No machine on
tools/list. - The first tool call creates one, and only one even under concurrent calls.
The machine is named mcp-<session> and created with idleTimeoutSec: 3600. When the
client disconnects — SIGINT, SIGTERM, or stdin closing — the machine is destroyed, unless
you passed --keep.
What the model is told
The first tool result of a session is prefixed with one honest line about the machine. A model that believes it is contained when it is not will take risks it otherwise would not, so this is not optional and not buried.
On a container provider:
[husk] docker container cmp_8v9z, isolated from the host. /work persists for this session.On the local provider:
[husk] local computer cmp_8v9z on real Linux via wsl:Ubuntu. This is a guarded working
directory, NOT a sandbox: file tools are confined to /work and destructive commands are
refused, but shell commands can still reach anything your user can on the host --
filesystem, kernel and network. Keep secrets and untrusted input out of it.On anything else:
[husk] computer cmp_8v9z on ssh. /work persists for this session.The server also sends MCP instructions at initialisation:
Husk gives you a Linux computer. Use
shellfor anything a command line can do; the filesystem at/workpersists across calls in this session. Callcomputer_infoonce before assuming a runtime or tool is installed.
Options
--session <key> reuse one machine across calls (default: "mcp")
--provider <name> docker | podman | local | ssh | fly (default: best available)
--flavor <name> base | python | node | full (default: base)
--network <mode> none | egress | full (default: egress)
--memory <mb> memory ceiling
--cpus <n> cpu ceiling
--keep leave the machine running after the client disconnects
-h, --help--session also reads $HUSK_SESSION. Two sessions, two machines, two filesystems, one
client:
claude mcp add husk-api -- npx -y @husk-ai/mcp --session api --flavor python
claude mcp add husk-web -- npx -y @husk-ai/mcp --session web --flavor nodestdout is the protocol
stdout carries JSON-RPC and nothing else. Every log line goes to stderr. A stray
console.log corrupts the stream and the client dies silently, which is why
HuskMcpServer takes its logger by injection and defaults it to a stderr logger.
If you are debugging, read stderr:
npx -y @husk-ai/mcp 2>/tmp/husk-mcp.logWhich provider you get
Whatever auto picks, unless you name one. Run husk doctor to see which that is before
you wonder why the model is reporting a Windows path.
If Docker is running, you get a container and the isolation note says so. If it is not,
you get the local provider and the note says that instead — which is the point of the
note.
Other clients
Any MCP client that can launch a stdio server works the same way. Cursor and Zed take a JSON block rather than a CLI command:
{
"mcpServers": {
"husk": {
"command": "npx",
"args": ["-y", "@husk-ai/mcp", "--session", "cursor", "--flavor", "python"]
}
}
}The seven tools listed above are the whole surface, whatever the client. Their
behaviour, their dangerous flags and their arguments are the same ones the agent loop
uses — see tools in a husk.yaml and
Approvals.
Next
- Isolation — what the machine the model just got is actually worth.
- Concepts — the interface behind those seven tools.
- CLI reference — the
husk mcpsubcommand, which takes a different flag set from the standalone binary.