MCP 调用

支持 MCP 的 agent 可以直接调用沙箱内置工具,完成文件、命令、代码和浏览器操作。

要求

使用 MCP 客户端连接 POST /mcp 即可。每个工具都需要对应的 plane。

可用能力见 GET /v2/sandbox 返回的 capabilities

可用能力见 GET /v1/capabilities

设置 AIO_API_KEY 后,需要在 Authorization: Bearer <key> 中携带 key。/mcp 不是公开路由,不带 key 会返回 401

工具列表

客户端可以通过 tools/list 获取当前可用的工具。

curl
curl -X POST "$BASE_URL/mcp" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

内置工具集编译进了二进制,顺序如下:

工具作用
sandbox_execute_bash在托管会话中执行 shell 命令
sandbox_execute_code执行 Python 或 JavaScript
sandbox_file_operationsread、write、replace、search、find、list、grep、glob
sandbox_str_replace_editorview、create、str_replace、insert、undo_edit
sandbox_get_context沙箱版本与 home 目录
sandbox_get_packages已安装的 Python 或 Node 包
sandbox_load_skill加载一个 skill,或列出全部
browser_get_infoCDP url、视口大小等信息
browser_gui_screenshot整个显示器的截图
browser_gui_execute_action在显示器上执行一个 GUI 动作

除此之外的工具都来自注册的 MCP server,所以以列表为准,不必记数量。每个条目都带自己的 JSON Schema:

{
  "description": "Execute code in Python or JavaScript runtime.",
  "inputSchema": {
    "properties": {
      "code": {
        "description": "Code to execute",
        "type": "string"
      },
      "language": {
        "description": "Programming language ('python', 'javascript')",
        "type": "string"
      },
      "timeout": {
        "description": "Execution timeout in seconds",
        "type": "integer"
      }
    },
    "required": [
      "code"
    ],
    "type": "object"
  },
  "name": "sandbox_execute_code"
}

用四次工具调用完成一项任务

下面用四个 JSON 请求完成一个小任务:把数字写入文件、统计行数、用 Python 求和,再读取结果。

所有步骤共享同一个文件系统,因此命令和代码都能读取第 1 步写入的文件。

1. 写入输入文件

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "sandbox_file_operations",
    "arguments": {
      "action": "write",
      "path": "/tmp/agent-data.txt",
      "content": "3\n7\n12\n5\n"
    }
  }
}

2. 执行命令

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "sandbox_execute_bash",
    "arguments": {
      "cmd": "wc -l /tmp/agent-data.txt",
      "cwd": "/tmp"
    }
  }
}

3. 执行 Python 代码

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "sandbox_execute_code",
    "arguments": {
      "language": "python",
      "code": "nums = [int(n) for n in open(\"/tmp/agent-data.txt\").read().split()]\nopen(\"/tmp/agent-result.txt\", \"w\").write(str(sum(nums)))\nprint(\"sum written\")"
    }
  }
}

4. 读取结果

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "sandbox_file_operations",
    "arguments": {
      "action": "read",
      "path": "/tmp/agent-result.txt"
    }
  }
}

每个结果都包含 content 列表;调用失败时,isErrortrue

content 中的文本就是模型读到的内容,可以是文件工具返回的 JSON、命令输出或解释器的 stdout。第 1 步的完整响应如下:

{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "text": "{\"action\":\"write\",\"bytes_written\":9,\"path\":\"/tmp/agent-data.txt\",\"success\":true}",
        "type": "text"
      }
    ],
    "isError": false
  }
}

sandbox_execute_bash 默认在同一个共享会话中执行,除非传入 new_session

结果中包含会话结束时的 cwd。在下一次调用中传回该值,agent 使用的工作目录就会与 shell 保持一致。

调用失败时

工具调用失败有两种情况,区别在于工具是否真正执行。下面分别是工具不存在和文件不存在时的请求:

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "sandbox_execute",
    "arguments": {}
  }
}
{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "tools/call",
  "params": {
    "name": "sandbox_file_operations",
    "arguments": {
      "action": "read",
      "path": "/tmp/agent-missing.txt"
    }
  }
}

工具名不存在、未传工具名或方法不存在,都属于 JSON-RPC 错误。响应仍然是 HTTP 200,MCP 客户端解析的是 error 对象:

{
  "error": {
    "code": -32602,
    "message": "unknown tool: sandbox_execute"
  },
  "id": 1,
  "jsonrpc": "2.0"
}

工具调用失败时,仍返回 result 对象,但会带有 isError: true。文本中包含失败原因,文件 API 的结构化错误会原样返回:

{
  "id": 1,
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "text": "{\"action\":\"read\",\"error\":{\"errno\":2,\"errno_name\":\"ENOENT\",\"error_type\":\"not_found\",\"exception_type\":\"FileNotFoundError\",\"message\":\"Failed to read file: No such file or directory (os error 2)\",\"operation\":\"read\",\"path\":\"/tmp/agent-missing.txt\",\"retryable\":false},\"message\":\"Failed to read file: No such file or directory (os error 2)\",\"path\":\"/tmp/agent-missing.txt\",\"success\":false}",
        "type": "text"
      }
    ],
    "isError": true
  }
}

缺少参数也属于工具执行失败。例如,sandbox_execute_bash 不带 cmd 时返回 isError: true,文本为 cmd is required

没有 computer-use worker 时调用 browser_gui_screenshotbrowser_gui_execute_action、CDP 端口不可达时调用 browser_get_info,以及调用已停止的上游 MCP server,也都属于这一类。

接入其他 MCP server

可通过 EXTRA_MCP_SERVERS(或 --mcp-servers)注册本机的无状态 MCP server;其工具会并入同一目录,调用会被转发。下面用第二个 daemon 演示这一点:

EXTRA_MCP_SERVERS='{"peer":{"url":"http://127.0.0.1:18091/mcp","prefix":"peer"},"gone":{"url":"http://127.0.0.1:18111/mcp","prefix":"gone"}}' \
  aiod start --port 18092

curl -X POST "http://127.0.0.1:18092/mcp" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# → 20 tools: the 10 built-in ones, then peer_sandbox_execute_bash …
#   peer_browser_gui_execute_action. An upstream that does not answer, like
#   "gone" here, is simply absent from the list.

curl -X POST "http://127.0.0.1:18092/mcp" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"peer_sandbox_execute_bash","arguments":{"cmd":"echo hello","cwd":"/tmp"}}}'

curl -X POST "http://127.0.0.1:18092/mcp" -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"gone_hello","arguments":{}}}'

这次运行遵循的规则:

  • 只接受 127.0.0.1localhost[::1] 上的 http://。启动时会跳过远程地址和 command(stdio)条目,并记一条警告。
  • 没有握手也没有 session,一次调用一个 POST。因此上游可以晚于 daemon 启动,下次 tools/list 就会看到它。
  • prefix 时,工具以 <prefix>_<tool> 的形式暴露,转发时再去掉前缀。 不带 prefix 时,上游自己的工具名必须已经以 <name>_ 开头;其余工具会被丢弃,因为无法确定路由。
  • 名字冲突时内置工具优先;两个上游冲突时,保留先注册的名称。

AIO 镜像里的 mcp-server-browser 就是这样注册的,该镜像工具列表中的页面级 browser_* 导航工具由此而来。

连接客户端

支持 streamable-HTTP MCP 的客户端可以连接 http://127.0.0.1:18091/mcp;使用预置镜像时,连接网关的 /mcp。设置 key 后,通过 bearer header 发送。

相关页面