Skip to content

5.3 MCP 服务器接入

本节你会学到

  • 什么场景适合用 MCP,什么场景应当写自定义 Tool
  • Agentao 支持的两种 transport(stdio + SSE;不支持 HTTP)
  • 多租户模式:会话级 extra_mcp_servers、环境变量展开、trust: 的边界

MCP(Model Context Protocol) 是"工具互操作的事实标准"。Agentao 作为 MCP Client,可以接入任何 MCP 兼容服务器——GitHub、Filesystem、Postgres、Slack、Jira、你自己写的……所有这些工具都会自动mcp_{server}_{tool} 的形式出现在 Agent 可用工具列表里。

MCP 能做什么

场景推荐的 MCP Server
读写文件 / 代码仓库@modelcontextprotocol/server-filesystem
GitHub issues/PR@modelcontextprotocol/server-github
数据库查询@modelcontextprotocol/server-postgres
Slack / Linear / Jira官方或社区 MCP
内部工具自建 MCP Server(见末尾)

优势:不用自己写 Tool 子类——社区已经写好并维护。

配置的两种方式

方式 A · JSON 配置文件

文件位置(同名冲突时用户级胜;项目级仅可新增名字):

~/.agentao/mcp.json         ← 用户级(所有项目共享;任何在该文件声明的 server name 都以此为准)
<working_dir>/.agentao/mcp.json ← 项目级(仅可声明新 name,不能覆盖用户级同名条目)

为何仅可新增?

项目级 mcp.json 一旦 checked-in 进 git,就可能被恶意(或意外)地把已知 server name(如 github)静默重定向到不同 transport 或 endpoint。冲突时打 warning + 跳过项目项。要解决,请改名项目项或移除用户级同名条目。

格式

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/code"],
      "env": {},
      "trust": false,
      "timeout": 60
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
      }
    },
    "analytics-sse": {
      "url": "https://mcp.your-company.com/sse",
      "headers": {
        "Authorization": "Bearer ${ANALYTICS_TOKEN}"
      },
      "timeout": 30
    }
  }
}

方式 B · 程序式(嵌入首选)

构造 Agent 时通过 extra_mcp_servers 参数注入——完全跳过 JSON 文件,按会话/租户动态生成:

python
from agentao import Agentao

agent = Agentao(
    working_directory=Path(f"/tmp/tenant-{tenant.id}"),
    extra_mcp_servers={
        "github-per-tenant": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-github"],
            "env": {"GITHUB_PERSONAL_ACCESS_TOKEN": tenant.github_token},
        },
    },
)

合并规则:同名覆盖 .agentao/mcp.json 的同名条目。

配置字段详解

stdio 传输(子进程)

字段必填说明
command可执行文件(npx, python, 绝对路径)
args命令行参数列表
env额外环境变量;支持 $VAR / ${VAR} 从进程环境展开
cwd子进程工作目录
timeout初始化超时(秒),默认 60
trust为 true 时跳过工具确认

SSE 传输(远程服务)

字段必填说明
urlSSE endpoint URL
headersHTTP 头;支持 ${VAR} 展开
timeout秒,默认 60
trust同上

⚠️ HTTP 不支持:Agentao 的 MCP 客户端只导入了 stdio_clientsse_clienthttp 类型的 MCP Server 无法接入(ACP 握手也会通告 mcpCapabilities.http: false)。

环境变量展开

json
"env": {
  "TOKEN": "${MY_TOKEN}",     // ${...} 形式
  "REGION": "$AWS_REGION"     // $... 形式
}

展开时机:加载配置时——也就是 Agent 构造时。展开后的字面量值进入子进程 env。

未定义的变量展开成空字符串(不抛错)。

MCP 工具的命名

一个 MCP Server 发现的每个工具都被包装为 Agentao Tool,名字加前缀

Server: "github"
MCP 工具: "create_issue"
Agentao 里的名字: "mcp_github_create_issue"

名字里的非 [a-zA-Z0-9_] 字符会被替换成 _

这意味着:

  • 你写自己的 Tool 时,不要以 mcp_ 打头(避免看起来像 MCP 工具)
  • 权限规则可以按前缀匹配:{"tool": "mcp_github_*", ...}

调试 MCP 接入

python
# 查看所有发现的工具
for t in agent.tools.list_tools():
    if t.name.startswith("mcp_"):
        print(t.name, "—", t.description[:60])

# 查看 MCP manager 状态
if agent.mcp_manager:
    print(f"{len(agent.mcp_manager.clients)} server(s) connected")

日志文件 agentao.log 会记录:

  • MCP Server 启动成功/失败
  • 每个工具发现
  • 工具调用参数和结果

自己写一个 MCP Server(3 分钟上手)

最小的 MCP Server 用 Python 写:

python
# my_mcp_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("my-internal-tools")

@mcp.tool()
def get_user_info(user_id: str) -> str:
    """Query internal user info by ID."""
    return my_backend.get_user(user_id).to_json()

@mcp.tool()
def send_notification(user_id: str, message: str) -> str:
    """Send an in-app notification to a user."""
    my_backend.notify(user_id, message)
    return "ok"

if __name__ == "__main__":
    mcp.run()   # 默认 stdio

然后在 .agentao/mcp.json 里:

json
{
  "mcpServers": {
    "internal": {
      "command": "python",
      "args": ["/path/to/my_mcp_server.py"]
    }
  }
}

Agent 重启后自动发现 mcp_internal_get_user_infomcp_internal_send_notification

多租户策略

生产 SaaS 里,典型 MCP 使用模式:

Server谁写范围
官方/开源(github、filesystem、postgres)用户/运维配全局或项目级 JSON 文件
你自己的业务 MCP你写 Python/Node每租户一个实例,经 extra_mcp_servers= 按会话启
租户自带的 MCP租户配置(SaaS 控制台)存 DB,构造 Agent 时翻译成 extra_mcp_servers=

安全要点

  • 租户 token/密钥永远不要写进 JSON 文件——通过环境变量或 extra_mcp_serversenv 动态注入
  • MCP 子进程继承父进程环境变量——确保没有泄漏其他租户的凭据
  • 每会话独立子进程,避免跨租户状态污染

与权限引擎的配合

MCP 工具默认也需要确认(等同 requires_confirmation=True),除非配置 trust: true

json
{
  "mcpServers": {
    "trusted-internal": {
      "command": "...",
      "trust": true 这些工具直接允许执行,不走 confirm_tool
    }
  }
}

或者用权限规则细粒度控制:

json
{
  "rules": [
    {"tool": "mcp_github_get_*", "action": "allow"},
    {"tool": "mcp_github_delete_*", "action": "deny"},
    {"tool": "mcp_github_create_*", "action": "ask"}
  ]
}

权限详见 5.4

MCP 工具注解:readOnlyHint / destructiveHint

如果 server 声明了标准 MCP ToolAnnotations,Agentao 会读取 —— 但仅当 trust: true(按 MCP 规范:客户端不应基于来自不受信任 server 的注解作出工具决策)。

trust注解效果
false(默认)任何注解被忽略 —— server 可能撒谎;总是要求确认
truereadOnlyHint: trueis_read_only=True(read-only 模式可放行该调用),不要确认
truedestructiveHint: true要求确认 —— 覆盖 trust 默认
true无 / 都没有当前 trusted 行为 —— 不要求确认

这是安全方向正向的接线:注解可以增加摩擦(一个 trusted server 把某调用标为 destructive 时触发确认),但永远不会在不受信任路径上降低摩擦。Host 可以通过 McpTool.mcp_annotations 读到原始 dict 用于更丰富的投影。

⚠️ 常见陷阱

上线前先确认这几条

  • Server 启动失败但 Agent 静默继续 —— agent.chat() 不会暴露 MCP init 失败
  • 工具名超长 —— provider 会截断,function calling 直接断
  • trust: true 用得太宽 —— 绕过所有安全确认

下面每一条都附完整修法。

❌ Server 启动失败但 Agent 静默继续

Agentao 的 MCP 初始化是容错的——单个 Server 失败只会 log warning,不会阻塞 Agent 构造。检查 agentao.log

MCP: failed to start 'github': ...
MCP: 12 tools from 2 server(s)       ← 有些 Server 没起来

部署前务必确认期望数量的 Server 都在。

❌ 工具名超长

有些 MCP Server 工具名很长。拼上前缀后可能超过 OpenAI function calling 的名字长度限制(64 字符)。如果发现 LLM 不认某工具,检查名字长度。

trust: true 用得太宽

写过/删过东西的 Server 不要轻易设 trust: true——等于绕过所有安全确认。只给纯读或已经有自己权限层的 Server 用。

TL;DR

  • MCP 用来消费第三方工具生态(GitHub / 文件系统 / Postgres / Slack …);自定义 Tool 用来封装你自己的业务逻辑。
  • 两种 transport:stdio 子进程或 SSE URL。HTTP 不支持。
  • 多租户 token:构造时传 extra_mcp_servers{name: {command, args, env}}),同名会覆盖 .agentao/mcp.json。项目级 .agentao/mcp.json仅可新增的 —— 不能覆盖用户级同名条目。
  • 工具命名:mcp_{server}_{tool} ——自动加前缀避免不同服务器的同名冲突。
  • 写操作能力的 Server 绝对不要trust: true,会绕过所有确认。

→ 下一节:5.4 权限引擎