--- title: "Bash tool (Python)" sidebarTitle: "Bash tool (Python)" description: "supermemory-bash. The SMFS idea wrapped as a single agent tool, for Python agents and serverless runtimes." icon: "/icons/hugeicons/command-line.svg" --- `supermemory-bash` is the SMFS idea wrapped as a single agent tool: `run_bash(command)`. The "filesystem" is your Supermemory container. Runs anywhere Python runs. AWS Lambda, Modal, Fly Machines, Cloud Run, your laptop. No mount, no FUSE, no local disk. Reach for the Bash Tool when your agent runs somewhere it can't mount a real filesystem. ## Install ```bash pip install supermemory-bash ``` Or with uv: ```bash uv add supermemory-bash ``` ## Quickstart ```python import asyncio import os from supermemory_bash import create_bash async def main() -> None: result = await create_bash( api_key=os.environ["SUPERMEMORY_API_KEY"], container_tag="user_42", ) bash = result.bash r = await bash.exec("ls /") print(r.stdout) asyncio.run(main()) ``` `create_bash` returns a `CreateBashResult` with: - `bash`: a `Shell` instance with `.exec(cmd)` - `tool_description`: a pre-written tool description ready to hand to the model - `configure_memory_paths(paths)`: scope which paths get extracted into Supermemory - `refresh()`: re-prime the path index after external writes ## Use it as a model tool ### Anthropic SDK Pass `tool_description` straight into Claude's tool definition and run a normal agent loop. Each `tool_use` block calls `bash.exec` and the result goes back as a `tool_result`. ```python import asyncio import os import anthropic from supermemory_bash import create_bash async def run_agent(user_message: str) -> str: result = await create_bash( api_key=os.environ["SUPERMEMORY_API_KEY"], container_tag="user_42", ) bash = result.bash client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"]) tools = [ { "name": "bash", "description": result.tool_description, "input_schema": { "type": "object", "properties": { "cmd": {"type": "string", "description": "The bash command to run."} }, "required": ["cmd"], }, } ] messages = [{"role": "user", "content": user_message}] for _ in range(10): response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=4096, tools=tools, messages=messages, ) if response.stop_reason == "end_turn": for block in response.content: if hasattr(block, "text"): return block.text return "" messages.append({"role": "assistant", "content": response.content}) tool_results = [] for block in response.content: if block.type == "tool_use": cmd = block.input.get("cmd", "") r = await bash.exec(cmd) output = r.stdout if r.stderr: output += f"\n[stderr]: {r.stderr}" if r.exit_code != 0: output += f"\n[exit_code]: {r.exit_code}" tool_results.append( { "type": "tool_result", "tool_use_id": block.id, "content": output or "(no output)", } ) messages.append({"role": "user", "content": tool_results}) return "(max steps reached)" asyncio.run(run_agent("What's in my notes about the Q3 launch?")) ``` ### OpenAI SDK Same idea with OpenAI's function-calling format. Define a single `bash` function, dispatch each `tool_calls` entry to `bash.exec`, and feed the output back as a `tool` message. ```python import asyncio import json import os from openai import OpenAI from supermemory_bash import create_bash async def run_agent(user_message: str) -> str: result = await create_bash( api_key=os.environ["SUPERMEMORY_API_KEY"], container_tag="user_42", ) bash = result.bash client = OpenAI() tools = [ { "type": "function", "function": { "name": "bash", "description": result.tool_description, "parameters": { "type": "object", "properties": {"cmd": {"type": "string"}}, "required": ["cmd"], }, }, } ] messages = [{"role": "user", "content": user_message}] for _ in range(10): response = client.chat.completions.create( model="gpt-4o", messages=messages, tools=tools, ) message = response.choices[0].message if not message.tool_calls: return message.content or "" messages.append(message.model_dump(exclude_none=True)) for call in message.tool_calls: args = json.loads(call.function.arguments or "{}") r = await bash.exec(args.get("cmd", "")) output = r.stdout if r.stderr: output += f"\n[stderr]: {r.stderr}" if r.exit_code != 0: output += f"\n[exit_code]: {r.exit_code}" messages.append( { "role": "tool", "tool_call_id": call.id, "content": output or "(no output)", } ) return "(max steps reached)" asyncio.run(run_agent("List my notes")) ``` ### Claude Agent SDK The [Claude Agent SDK](https://docs.claude.com/en/api/agent-sdk/overview) ships with built-in `Bash`, `Read`, and `Write` tools. If your agent runs somewhere SMFS can be mounted (a long-lived process on macOS or Linux), point those built-ins at an SMFS mount and you don't need `supermemory-bash` at all — the agent just sees your container as a directory. See [Mount SMFS](/smfs/mount) for setup, or the [provider guides](/smfs/overview#use-smfs-with-your-sandbox-provider) for sandbox-specific instructions. ## Memory The Bash Tool inherits SMFS memory semantics. By default, files named `user.md` or `memory.md` are extracted as memories. Configure additional memory paths after construction: ```python result = await create_bash(api_key=api_key, container_tag=container_tag) bash = result.bash await result.configure_memory_paths(["/notes/", "/journal.md"]) ``` Trailing `/` matches recursively. No slash matches an exact file. Pass `[]` to disable memory generation. The container also exposes a virtual `profile.md` at the root: a live digest of everything in the container. Read it once at the start of a session to give the model context without walking every file. ```python r = await bash.exec("cat /profile.md") print(r.stdout) ``` ## Commands the agent can run The Python tool runs its own built-in commands: filesystem builtins (`pwd`, `cd`, `ls`, `cat`, `stat`, `mkdir`, `rm`, `rmdir`, `mv`, `cp`, `touch`, `echo`, `printf`, `tee`, `basename`, `dirname`), search and text utilities (`grep`, `find`, `head`, `tail`, `wc`, `sort`, `uniq`, `sed`, `cut`, `tr`, `seq`, `date`), file tests (`test`, `[`), plus the custom `sgrep [path]` for semantic search across the container. Pipes (`|`), redirects (`>`, `>>`, `<`), and `&&` / `||` chains work. Only simple commands run: loops (`for`, `while`), `if` blocks, and `awk` are not supported in the Python tool. Use the [TypeScript Bash Tool](/smfs/bash-tool#commands-the-agent-can-run) if your agent needs them. ## Configuration | Option | Default | Purpose | | --- | --- | --- | | `api_key` | required | Supermemory API key | | `container_tag` | required | Container to expose as the filesystem | | `base_url` | `None` | Override the API endpoint | | `eager_load` | `True` | Warm the path index when the instance starts | | `eager_content` | `True` | Also warm the content cache during eager load | | `cwd` | `"/"` | Initial working directory | | `env` | `None` | Extra environment variables | | `cache_ttl_ms` | `150_000` | Content cache TTL in ms. `None` = never expires (single-writer). `0` = no cache. | The container is what defines the filesystem; setting `cwd` or extra `env` from the host doesn't change the files the agent sees. ## Limitations - `chmod`, `ln`, and `readlink` aren't available; the shell returns `command not found`. - Redirecting to `/dev/null` discards the output; nothing is written to the container. - Binary uploads aren't supported. Text is extracted server-side.