{"slug": "strands-shell-give-your-agent-a-shell-without-giving-it-keys-to-your-machine", "title": "Strands Shell: Give your agent a shell without giving it keys to your machine", "summary": "Strands Shell, a Bourne-compatible shell that runs in-process without fork or syscalls, launches to give AI agents isolated shell access with sub-millisecond cold starts. The tool provides over 50 built-in commands and mediates file, network, and credential access through a kernel, preventing agents from accessing host resources beyond declared binds. Available for Python and Node.js, it aims to replace Docker or cloud sandboxes for agentic workflows requiring speed and security.", "body_md": "[Documentation](https://strandsagents.com/docs/user-guide/shell-sdk/)\n◆ [MCP Server](#mcp-server)\n◆ [Python](#python)\n◆ [Node.js](#nodejs)\n\nAgents run shell commands in tight loops: Install deps, run tests, grep for errors, iterate. Those loops need speed and isolation.\n\nStrands Shell is a Bourne-compatible shell that runs in-process. `grep`\n\n, `sed`\n\n, `jq`\n\n, `curl`\n\n, `find`\n\n, 50+ commands and it does this without fork, exec, syscalls, or cold starts. You declare what the agent can reach (files, URLs, credentials) and everything else doesn't exist to the agent.\n\n| Docker | Cloud sandbox | Strands Shell | |\n|---|---|---|---|\nCold start |\n~200ms | ~1s (network) | <1ms |\nIsolation |\nContainer namespace | MicroVM | In-process VFS |\nNetwork |\niptables / sidecar | Platform policy | URL allowlist + SSRF guard |\nSecrets |\nEnv vars (agent can read them) | Platform-specific | Injected per-request, agent never sees them |\nSetup |\nDocker daemon | API key + network | `pip install strands-shell` |\nPlatforms |\nLinux | Cloud-only | macOS, Linux, WASM |\n\nDrop this into your MCP client config:\n\n```\n{\n  \"mcpServers\": {\n    \"shell\": {\n      \"command\": \"uvx\",\n      \"args\": [\"strands-shell\", \"--mcp\"]\n    }\n  }\n}\n```\n\nThat's it and your agent gets `shell`\n\n, `read_file`\n\n, `write_file`\n\n, `list_dir`\n\n. All mediated through the Kernel.\n\n```\npip install strands-shell\npython\nimport strands_shell\n\nshell = strands_shell.Shell(\n    binds=[strands_shell.Bind(\"/my/project\", \"/workspace\", mode=\"copy\")],\n    credentials=[strands_shell.Cred(\"https://api.example.com/\", env_var=\"API_TOKEN\")],\n    allowed_urls=[\"https://api.example.com/\"],\n)\n\nout = shell.run(\"grep -rn TODO /workspace\")\nprint(out.stdout)\nnpm install @strands-agents/shell\njs\nimport { Shell } from '@strands-agents/shell'\n\nconst shell = await Shell.create({\n  binds: [{ source: '/my/project', destination: '/workspace', mode: 'copy' }],\n})\nconst out = await shell.run('grep -rn TODO /workspace')\nconsole.log(out.stdout)\nflowchart TB\n    agent[\"Your agent code\\n(Strands, LangGraph, Pydantic AI, etc)\"]\n    agent -->|\"MCP / Python / Node.js\"| shell\n\n    subgraph shell [\"Strands Shell\"]\n        direction TB\n        subgraph kernel [\"Kernel (mediation boundary)\"]\n            vfs[\"VFS: isolated filesystem\"]\n            net[\"Network: SSRF guard + allowlist\"]\n            creds[\"Credentials: injected per-URL\"]\n            limits[\"Limits: timeout, output, fds\"]\n        end\n        engine[\"Shell engine: parser, 25 builtins, 33 commands, Lua 5.4\"]\n    end\n```\n\nWritten in Rust, with native bindings for Python (PyO3) and Node.js (napi-rs). State persists across `run()`\n\ncalls (env vars, working directory, functions). The filesystem is shared.\n\n```\nshell = strands_shell.Shell(\n    binds=[\n        strands_shell.Bind(\"/host/project\", \"/workspace\", mode=\"copy\"),\n        strands_shell.Bind(\"/tmp/output\", \"/output\", mode=\"direct\"),\n    ],\n    credentials=[\n        strands_shell.Cred(\"https://api.example.com/\", env_var=\"API_TOKEN\"),\n    ],\n    allowed_urls=[\"https://api.example.com/\", \"https://pypi.org/\"],\n    timeout=30.0,\n    env={\"PROJECT\": \"demo\"},\n    limits=strands_shell.Limits(\n        max_output=1 << 20,\n        max_file_size=10 << 20,\n    ),\n)\n```\n\n⚠️ The agent can read and modify host files in real time. Use only for designated output directories. Never direct-bind directories containing secrets, credentials, or configuration you don't want the agent to modify.`mode: \"direct\"`\n\nmounts are live.\n\nYou can load all of this from a config file instead:\n\n```\n[[bind]]\nmode = \"copy\"\nsource = \"/host/project\"\ndestination = \"/workspace\"\n\n[[cred]]\nurl = \"https://api.openai.com/v1/\"\nmethods = [\"POST\"]\nkind = \"bearer\"\napi_key_env = \"OPENAI_API_KEY\"\n\n[[mcp]]\nname = \"my-tools\"\ncommand = \"/path/to/mcp-server\"\nargs = [\"--stdio\"]\n```\n\nThe built-in [MCP](https://modelcontextprotocol.io/) server exposes the shell over JSON-RPC on stdio, working with anything that speaks MCP.\n\n```\nuvx strands-shell --mcp                          # bare in-memory sandbox\nuvx strands-shell --config sandbox.toml --mcp    # with mounts + credentials\n```\n\nIf you declare `[[mcp]]`\n\nservers in your TOML config, they show up as Lua modules inside the shell. Call `require(\"my_tools\")`\n\nand you get a table of the server's tools.\n\nStrands Shell is a mediation layer, not a security sandbox.It enforces what the agentshouldaccess via Kernel-mediated deny-by-default. It does NOT protect against: memory-safety exploits in the shell engine itself, timing side-channels, or an attacker who controls the host process. For multi-tenant or adversarial workloads, run each Shell instance inside a container or microVM.\n\nThe Kernel mediates everything; it runs in the same process as your code, not in a VM. If your threat model is \"untrusted tenant running arbitrary code,\" put Strands Shell inside a container too. For \"my agent shouldn't access things I haven't explicitly allowed,\" the Kernel handles it.\n\n**Default-deny. You allowlist what the agent can reach:**\n\n- Files: only bound paths exist, everything else is hidden.\n- Network:\n`curl`\n\nblocks private ranges (RFC1918, link-local, loopback, IMDS) by default while letting public URLs pass through. Use`allowed_urls`\n\nto permit specific internal hosts. - Secrets: the Kernel injects credentials per-URL at request time, ensuring the agent never holds them. The Kernel never re-injects on redirects, even back to the same host.\n- Syscalls: there are none; no\n`fork`\n\n, no`exec`\n\nbecause the shell is pure userspace.\n\nIf you bypass any of these, report it. See [SECURITY.md](/strands-agents/shell/blob/main/SECURITY.md).\n\n**Limits (best-effort):** timeouts, output caps, fd limits, inode limits. These catch runaway agents but won't stop someone actively trying to break out. OS-level isolation for that.\n\n**Multi-tenant:** a Shell instance is single-owner. If you're serving multiple agents, create one Shell per session. Construction is cheap (no containers, no VMs, just an in-memory VFS), so spinning up per-request is the intended pattern.\n\nOut of the box, the shell is an empty sandbox — no files, no network, no credentials. When you grant access, follow least privilege:\n\n**Prefer** Copy-on-create isolates the agent from your live files. Use`mode: \"copy\"`\n\nover`mode: \"direct\"`\n\nfor source code.`direct`\n\nonly for output directories where the agent needs to persist results.**Scope binds narrowly.** Bind`/my/project/src`\n\nrather than`/my/project`\n\nor`/`\n\n. The agent doesn't need your`.git/`\n\n,`.env`\n\n, or`node_modules/`\n\n.**Allowlist URLs explicitly.** Don't use`allowed_urls: [\"https://\"]`\n\n— this disables SSRF protection entirely. List the specific API endpoints the agent needs.**Set timeouts.** The default has no per-command timeout. Set`timeout`\n\nto bound runaway commands (30s is reasonable for most agent loops).**Use limits.** Set`max_output`\n\nto prevent agents from filling memory with unbounded command output (1MB is a good default).\n\n25 builtins, 33 commands, and a Bourne-compatible shell with pipes, loops, functions, and subshells.\n\nThe commands agents use constantly: `grep`\n\n, `find`\n\n, `cat`\n\n, `head`\n\n, `tail`\n\n, `jq`\n\nfor reading and searching. `sed`\n\n, `sort`\n\n, `tr`\n\n, `cut`\n\nfor transforming output. `cp`\n\n, `mv`\n\n, `rm`\n\n, `mkdir`\n\nfor managing files. `curl`\n\nfor HTTP (SSRF-guarded, credentials auto-injected). `lua`\n\nfor scripting when shell gets awkward.\n\nThe [full command reference](https://strandsagents.com/docs/user-guide/shell-sdk/commands/) has the inventory with implementation status, supported flags, and known gaps vs GNU coreutils.\n\nRead and write files without going through a shell command:\n\n```\nshell.write_file(\"/workspace/note.txt\", b\"hello\")\ndata = shell.read_file(\"/workspace/note.txt\")\nentries = shell.list_files(\"/workspace\")\nshell.remove_file(\"/workspace/note.txt\")\n```\n\nSee [CONTRIBUTING.md](/strands-agents/shell/blob/main/CONTRIBUTING.md). Bug reports and design questions are just as useful as PRs.\n\n[Discord](https://discord.com/invite/strands) if you want to talk about it.\n\nApache-2.0\n\nIf you find a security issue, report it privately instead of opening a public issue. Bypasses of filesystem mediation, SSRF protection, or credential injection qualify. See [SECURITY.md](/strands-agents/shell/blob/main/SECURITY.md).", "url": "https://wpnews.pro/news/strands-shell-give-your-agent-a-shell-without-giving-it-keys-to-your-machine", "canonical_source": "https://github.com/strands-agents/shell", "published_at": "2026-06-17 16:09:32+00:00", "updated_at": "2026-06-17 16:23:18.614161+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-safety"], "entities": ["Strands Shell", "Strands", "MCP", "Python", "Node.js", "Rust", "PyO3", "napi-rs"], "alternates": {"html": "https://wpnews.pro/news/strands-shell-give-your-agent-a-shell-without-giving-it-keys-to-your-machine", "markdown": "https://wpnews.pro/news/strands-shell-give-your-agent-a-shell-without-giving-it-keys-to-your-machine.md", "text": "https://wpnews.pro/news/strands-shell-give-your-agent-a-shell-without-giving-it-keys-to-your-machine.txt", "jsonld": "https://wpnews.pro/news/strands-shell-give-your-agent-a-shell-without-giving-it-keys-to-your-machine.jsonld"}}