MCP Server Design
Agent Sandbox MCP Server exposes sandbox capabilities as MCP (Model Context Protocol) Tools, allowing AI Agents (Cursor, Claude Desktop, VS Code, etc.) to directly operate cloud sandboxes.
Implemented surface (kept in sync with src/agent/):
- 7 P0 tools (
agent/tools.ts) - STDIO transport (
agent/mcp.ts) —abx mcp start - Streamable HTTP transport (
agent/mcp_http.ts) — HTTP application - CLI:
abx mcp install / start / status / deploy
1. Tools Definition
All 7 tools are defined in TOOL_SCHEMAS (agent/tools.ts). Omitting sandbox_id targets the default sandbox (see Section 2).
create_sandbox
{
"name": "create_sandbox",
"description": "Create a cloud sandbox and make it the session default. When template is omitted, use the MCP server's configured template (code-interpreter-v1 for abx mcp start).",
"inputSchema": {
"type": "object",
"properties": {
"template": {
"type": "string",
"description": "Sandbox template name; omitted means the server template"
},
"timeout": {
"type": "integer",
"description": "Sandbox timeout in seconds, default 300",
"default": 300
},
"envs": {
"type": "object",
"description": "Environment variable key-value pairs",
"additionalProperties": { "type": "string" }
}
}
},
"returns": {
"sandbox_id": "string — Sandbox ID",
"status": "string — Sandbox status",
"url": "string — Sandbox access URL"
}
}
run_code
{
"name": "run_code",
"description": "Execute code in the sandbox (via the code interpreter). Supports Python, JavaScript, and more. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"code": { "type": "string", "description": "Code to execute" },
"language": {
"type": "string",
"description": "Programming language, default python",
"enum": ["python", "javascript", "shell", "typescript", "r"],
"default": "python"
},
"sandbox_id": {
"type": "string",
"description": "Sandbox ID; uses the default sandbox when omitted"
},
"timeout": {
"type": "integer",
"description": "Execution timeout in seconds, default 30",
"default": 30
}
},
"required": ["code"]
},
"returns": {
"stdout": "string",
"stderr": "string",
"exit_code": "integer",
"output_files": "array — [{name, path, size}] generated files"
}
}
run_command
{
"name": "run_command",
"description": "Execute a shell command in the sandbox. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"command": { "type": "string", "description": "Shell command" },
"sandbox_id": { "type": "string", "description": "Sandbox ID" },
"cwd": {
"type": "string",
"description": "Working directory; omitted uses the image workdir",
"default": "/app"
},
"timeout": {
"type": "integer",
"description": "Execution timeout in seconds, default 60",
"default": 60
}
},
"required": ["command"]
},
"returns": {
"stdout": "string",
"stderr": "string",
"exit_code": "integer"
}
}
read_file
{
"name": "read_file",
"description": "Read file content from the sandbox. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"path": { "type": "string", "description": "Absolute file path" },
"sandbox_id": { "type": "string", "description": "Sandbox ID" },
"encoding": {
"type": "string",
"description": "File encoding, default utf-8",
"default": "utf-8"
}
},
"required": ["path"]
},
"returns": { "content": "string" }
}
write_file
{
"name": "write_file",
"description": "Create or overwrite a file in the sandbox. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"path": { "type": "string", "description": "Absolute file path" },
"content": { "type": "string", "description": "File content" },
"sandbox_id": { "type": "string", "description": "Sandbox ID" }
},
"required": ["path", "content"]
},
"returns": { "success": "boolean", "bytes_written": "integer" }
}
list_files
{
"name": "list_files",
"description": "List files and subdirectories in a sandbox directory. Uses the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "Directory path, default /",
"default": "/app"
},
"sandbox_id": { "type": "string", "description": "Sandbox ID" }
}
},
"returns": {
"files": "array — [{name, path, type, size}]"
}
}
kill_sandbox
{
"name": "kill_sandbox",
"description": "Destroy the specified sandbox, or the default sandbox when sandbox_id is omitted.",
"inputSchema": {
"type": "object",
"properties": {
"sandbox_id": {
"type": "string",
"description": "Sandbox ID to destroy; destroys the default sandbox when omitted"
}
}
},
"returns": { "success": "boolean" }
}
2. Session Binding Design
Default Sandbox Concept
The MCP Server introduces a "default sandbox" concept to simplify Agent operations:
graph TD
A["Agent's first tool call (no sandbox_id)"] --> B[MCP Server automatically creates a default sandbox]
B --> C["Subsequent calls without sandbox_id reuse the default sandbox"]
Behavior rules (implemented by SandboxManager in agent/mcp.ts):
create_sandboxcreates a sandbox and makes it the default. Later calls that omitsandbox_iduse that sandbox. Earlier sandboxes stay alive untilkill_sandboxor the session ends- When there is no default yet, the first tool call that omits
sandbox_idlazily creates one. Concurrent lazy creates share a single sandbox - Default template:
code-interpreter-v1when started viaabx mcp start(the CLI default);basewhen constructingSandboxMCPServer/ the HTTPSessionStoreprogrammatically without an explicit template.create_sandboxuses that server template whentemplateis omitted kill_sandboxwithoutsandbox_iddestroys the default sandbox, and returns an error when there is no default. Session end destroys every sandbox in the session — STDIO EOF / server shutdown, or HTTPDELETE /mcp/ idle session TTL expiry- An explicit
sandbox_idaddresses that sandbox and does not change the default. Each use callsSandbox.setTimeoutwith the timeout from create, extending the lifetime
sequenceDiagram
participant Agent
participant MCP as MCP Server
participant SB1 as sb-001
participant SB2 as sb-002
Note over Agent,MCP: Session starts
Agent->>MCP: run_code("print(1)")
MCP->>SB1: Auto-create default sandbox sb-001
Agent->>MCP: run_code("print(2)")
MCP->>SB1: Reuse sb-001
Agent->>MCP: create_sandbox(template=...)
MCP->>SB2: Create sb-002 and make it the default
Agent->>MCP: write_file / run_code (no sandbox_id)
MCP->>SB2: Use sb-002
Agent->>MCP: run_code("...", sandbox_id=sb-001)
MCP->>SB1: Explicit id still addresses sb-001
Note over Agent,MCP: Session ends
MCP->>SB1: Destroy sb-001 and sb-002
3. Transport Methods
STDIO — Local IDE Integration
graph LR
IDE["IDE / Agent<br/>Cursor, Claude, VS Code"] <-->|"STDIO stdin/stdout<br/>newline-delimited JSON-RPC 2.0"| MCP["MCP Server<br/>abx mcp start"]
MCP --> SM[SandboxManager]
SM --> FC[Alibaba Cloud FC]
- Implementation:
agent/mcp.ts— a self-contained minimal JSON-RPC 2.0 handler over newline-delimited STDIO; no externalmcpSDK dependency required - Protocol version:
2024-11-05— the only version the STDIO server supports.initializeechoes it when requested and falls back to it for missing/unsupported requests, preserving the original behavior - Supported methods:
initialize,notifications/initialized,tools/list,tools/call,ping - Use case: local development, single user; the IDE spawns the process
- Startup:
abx mcp start [--template NAME] [--api-key KEY] [--api-url URL] [--domain DOMAIN](typically launched by the IDE). A manual run prints configuration on stderr and keeps stdout as JSON-RPC;abx mcp stopends the recorded pid.--http --backgrounddetaches Streamable HTTP
Streamable HTTP — Remote Deployment
graph LR
A["Client A - Cursor"] <-->|"Streamable HTTP<br/>POST /mcp"| MCP["MCP Server<br/>FC function / uvicorn"]
B["Client B - Claude"] <-->|"Streamable HTTP<br/>POST /mcp"| MCP
MCP --> SM["SessionStore<br/>routes by Mcp-Session-Id"]
SM --> FC[Alibaba Cloud FC]
- Implementation:
agent/mcp_http.ts— HTTP application returned bycreateMcpApp()(the Python ASGI counterpart is exposed as the module-levelasgi_appentry point used by the FC artifact) - Protocol: MCP Streamable HTTP (spec 2025-06-18)
- Endpoints:
POST /mcp— JSON-RPC requests;initializecreates a session and returnsMcp-Session-Id; subsequent requests must carry the headerDELETE /mcp— session termination and sandbox cleanup (requiresMcp-Session-Id)GET /mcp— returns 405; SSE server-initiated notifications are not implementedGET /health— health probe returning{"status": "ok", "protocol": "2025-06-18"}- Sessions: in-process
SessionStore; idle TTL 3600 s (default), max 100 concurrent sessions (default); capacity exhaustion returns 503 with JSON-RPC error-32000 - Version negotiation:
initializeechoes the client-requestedprotocolVersionwhen supported; this transport supports2025-06-18only, matchingGET /health. Missing or unsupported requested versions negotiate to2025-06-18— per the MCP spec the server responds with a version it supports, and clients that cannot accept it may disconnect. The STDIO transport independently keeps2024-11-05 - Use case: remote service, team sharing, multiple clients; deploy to Alibaba Cloud FC via the artifact from
abx mcp deploy, or run locally with uvicorn - Optional dependency: none for
abx mcp start --http; the Python ASGI runtime of the FC artifact / uvicorn needs Starlette + uvicorn (pip install 'agent-sandbox[mcp]')
Authentication
- Client → MCP:
Authorization: Bearer <token>, validated with constant-time comparison; configured viaABX_MCP_AUTH_TOKEN. On loopback, leaving it unset disables authentication; a configured-but-empty token fails closed (every request gets 401). The FC artifact callsrequire_auth_tokenand refuses to start when the token is empty - MCP → Sandbox:
ABX_SANDBOX_API_KEYenvironment variable
Environment Variables
| Variable | Purpose |
|---|---|
ABX_SANDBOX_API_KEY |
Sandbox backend API key |
ABX_SANDBOX_BASE_URL |
Platform API URL |
ABX_SANDBOX_DOMAIN |
Sandbox domain |
ABX_DEFAULT_TEMPLATE |
Default sandbox template |
ABX_MCP_AUTH_TOKEN |
Bearer token for client authentication (HTTP transport) |
4. Server Architecture
graph TD
subgraph MCP["MCP Server"]
TL["Transport Layer\nSTDIO (mcp.ts) / Streamable HTTP (mcp_http.ts)"]
TR["Tool Registry\n7 P0 tools (agent/tools.ts)"]
SM["SandboxManager / SessionStore"]
RR["JSON-RPC Router\ninitialize / tools/list / tools/call / ping"]
SC["Sandbox Client\nsrc/api (Sandbox)"]
end
TL --> RR
TR --> RR
SM --> RR
RR --> SC
SC --> FC["Alibaba Cloud FC Sandbox Runtime"]
5. Installation and CLI
One-Click Installation
# Install to Cursor
abx mcp install --target cursor
# Install to Claude Desktop
abx mcp install --target claude
# Install to VS Code (Copilot)
abx mcp install --target vscode
install merges an agent-sandbox entry into the target IDE config: Cursor ~/.cursor/mcp.json, Claude Desktop claude_desktop_config.json (mcpServers), and the VS Code workspace .vscode/mcp.json (servers, type: stdio). The command is the absolute path of the running abx executable, or node <absolute path of abx.js> when abx cannot be found. Cursor and Claude receive ABX_SANDBOX_API_KEY in env when a key is available. The VS Code workspace file does not. A config that is not strict JSON is left untouched.
Installation Output
$ abx mcp install --target cursor
MCP Server config written to ~/.cursor/mcp.json
Registered tools:
• create_sandbox — Create a cloud sandbox
• run_code — Execute code
• run_command — Execute a command
• read_file — Read a file
• write_file — Write a file
• list_files — List files
• kill_sandbox — Destroy a sandbox
Please restart Cursor to apply changes.
status
abx mcp status # table output
abx --json mcp status # JSON output
Reports the server name, transport (stdio), tool count and names, whether an API key is configured, and whether agent-sandbox is installed in each supported IDE.
6. Configuration Examples
Claude Desktop
// ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"agent-sandbox": {
"command": "abx",
"args": ["mcp", "start"],
"env": {
"ABX_SANDBOX_API_KEY": "your-api-key"
}
}
}
}
Cursor
// ~/.cursor/mcp.json
{
"mcpServers": {
"agent-sandbox": {
"command": "abx",
"args": ["mcp", "start"],
"env": {
"ABX_SANDBOX_API_KEY": "your-api-key"
}
}
}
}
VS Code
// .vscode/mcp.json
{
"servers": {
"agent-sandbox": {
"type": "stdio",
"command": "/path/to/abx",
"args": ["mcp", "start"]
}
}
}
Remote (Streamable HTTP)
After deploying the HTTP app (Section 7), point clients at the endpoint; add a Bearer header when a token is configured:
{
"mcpServers": {
"agent-sandbox-remote": {
"url": "https://<FC_HTTP_TRIGGER_URL>/mcp",
"headers": {
"Authorization": "Bearer <BEARER_TOKEN>"
}
}
}
}
7. FC Deployment
Status:
abx mcp deploygenerates a deployment artifact; automatic FC API deployment is not implemented. Manual deployment steps are printed after generation.
Architecture
The MCP Server can be deployed to Alibaba Cloud Function Compute (FC) as a Streamable HTTP endpoint, leveraging FC's MCP session affinity routing.
graph TB
CLI["abx mcp deploy → artifact directory"]
FC["FC function: agent-sandbox-mcp (created manually)"]
Sandbox["Envd sandbox (separate FC instance)"]
CLI --> FC
FC --> Sandbox
subgraph FC_Function ["FC function"]
ASGI["app.py → agent_sandbox.agent.mcp_http:asgi_app"]
Trigger["HTTP trigger: POST/GET/DELETE /mcp"]
Session["Session affinity: Mcp-Session-Id"]
Env["Env vars: ABX_SANDBOX_API_KEY / ABX_MCP_AUTH_TOKEN / ABX_DEFAULT_TEMPLATE"]
end
CLI Command
abx mcp deploy \
--name agent-sandbox-mcp \
--region cn-hangzhou \
--template base \
--memory 512 --timeout 600 \
--generate-token \
--api-key $ABX_SANDBOX_API_KEY \
--output-dir ./mcp-artifact
Options: --name (default agent-sandbox-mcp), --region (command --region > ALIBABA_CLOUD_REGION_ID > abx config set region > cn-hangzhou), --template (default base), --memory (default 512), --timeout (default 600), --auth-token-file / --generate-token / ABX_MCP_AUTH_TOKEN env for the Bearer token, --enable-session-affinity/--no-session-affinity (default enabled), --sandbox-api-key (ABX_SANDBOX_API_KEY), --custom-domain, --output-dir.
Artifact Contents
| File | Content |
|---|---|
requirements.txt |
agent-sandbox[mcp], uvicorn>=0.29 |
app.py |
ASGI entry point reading ABX_MCP_AUTH_TOKEN, ABX_SANDBOX_API_KEY, ABX_SANDBOX_BASE_URL, ABX_SANDBOX_DOMAIN, ABX_DEFAULT_TEMPLATE from the FC function environment |
config.yaml |
YAML deployment manifest (not an FC API payload): function_name, region, runtime (python3.10), handler (app.app), memory, timeout, environment_variables, http_trigger (methods POST/GET/DELETE + enable_session_affinity), optional custom_domain |
abx mcp deploy also prints manual deployment steps (package the artifact, create the function via the FC console or SDK, create an HTTP trigger, enable Mcp-Session-Id affinity if supported) and an IDE config template with the Bearer token masked.
Key Design Points
- Protocol: Streamable HTTP (MCP spec 2025-06-18);
initializenegotiates2025-06-18consistently withGET /health(STDIO keeps2024-11-05) - Session affinity: delegated to the FC platform layer via the
Mcp-Session-Idheader — no application-level sticky routing needed - Authentication: dual-layer — client→MCP uses
Authorization: Bearer <token>; MCP→sandbox usesABX_SANDBOX_API_KEYfrom the FC env - Lifecycle: sandboxes are lazily created per MCP session;
DELETE /mcptriggers cleanup; the idle-session TTL is the safety net - Cold start: FC cold start (~1–3 s) may conflict with MCP initialize timeouts; provisioned instances are recommended for production
- Secrets:
config.yamlcontains the API key and Bearer token — never commit it to version control
See CLI Design — mcp deploy for the full parameter reference and the MCP Integration guide for end-to-end usage.
Related SDK documentation
The Python SDK ships its own python -m agent_sandbox.agent.mcp entry point and the create_mcp_app ASGI factory; see the SDK's MCP Server design and MCP Integration guide. This page covers the abx mcp commands and the Node.js implementation.