S19|MCP 与插件:多 Agent 平台 —— 外部能力总线,让外部工具安全接入

在前十八章,我们的 Agent 已经拥有完整的内部能力体系:循环、工具、计划、子代理、技能、压缩、权限、Hook、记忆、提示词流水线、错误恢复、任务系统、后台任务、定时调度、多 Agent 团队、团队协议、自主代理、Worktree 隔离,所有工具都写在本地代码里。

但真实系统走到一定阶段,会遇到一个自然的需求:能不能让外部程序也把工具接进来,而不用每次都改主程序?

这一章 S19,我们给 Agent 加上MCP 与插件系统:让外部工具通过统一协议安全接入,和本地工具处在同一控制面,实现 "外部能力总线",让工具不再局限于本地硬编码。


本章核心信息

  • 核心闭环:插件配置发现 → MCP Server 启动 → 工具标准化注册 → 统一路由分发 → 权限检查 → 结果回流主循环
  • 工具数量:4 个
  • 核心思想:外部能力系统不该是外挂;它们应和原生工具一起处在同一控制面上

先看懂本章所有名词

  1. MCP(Model Context Protocol)

    一套让 Agent 和外部工具程序对话的统一协议,定义了如何发现工具、调用工具、接收结果的标准方式。

  2. MCP Server

    提供外部工具能力的独立进程,通过 MCP 协议暴露工具列表和调用接口,Agent 通过连接 Server 使用这些工具。

  3. 插件(Plugin)

    负责外部工具配置的声明文件,告诉主程序如何发现、启动和连接 MCP Server,包含插件名、版本、Server 配置等信息。

  4. 工具前缀规则

    区分本地工具和外部 MCP 工具的命名约定,格式为mcp__{server}__{tool},避免命名冲突,一眼就能识别工具来源。

  5. 统一路由器

    负责分发工具调用请求的核心组件,判断是本地工具还是 MCP 工具,分别交给对应的处理器处理。

  6. 外部能力总线

    所有外部工具通过 MCP 接入后,和本地工具共享同一套权限检查、结果处理、主循环回流逻辑,形成统一的能力接入体系。


这一章到底要解决什么问题?

到 S18,Agent 的所有工具都写在本地代码里,存在明显的局限性:

  • 新增工具必须修改主程序,扩展性差
  • 外部程序无法直接提供能力,只能通过硬编码集成
  • 工具来源单一,无法复用成熟的第三方服务能力
  • 无法统一管理本地和外部工具的权限、调用和结果

本章要解决的核心问题:把工具来源从 "本地硬编码" 升级为 "外部可插拔",让外部工具通过统一协议安全接入,和本地工具共享同一控制面,实现系统的可扩展能力。


最小心智模型:外部工具接入的三层结构

复制代码
LLM
  |
  | 发起工具调用请求
  v
Agent 工具路由器
  |
  +-- 本地工具 → 交给本地 Python 处理器执行
  |
  +-- MCP 工具 → 交给 MCP Client 转发给外部 Server
                        |
                        v
                    MCP Server 执行工具并返回结果

一句话:MCP 不是独立的外挂系统,而是外部工具接入 Agent 的统一协议,最终和本地工具汇入同一套执行逻辑。


关键数据结构(本章灵魂)

1. 插件配置文件(plugin.json)

复制代码
{
  "name": "my-db-tools",
  "version": "1.0.0",
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {}
    }
  }
}

它本质上是一份配置声明,告诉主程序如何发现和启动 MCP Server。

2. MCP Server 配置

python 复制代码
server_config = {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-postgres"],
    "env": {}
}

定义了启动外部 Server 的命令、参数和环境变量。

3. 标准化后的工具定义

python 复制代码
{
    "name": "mcp__postgres__query",
    "description": "Run a SQL query on PostgreSQL",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {"type": "string"}
        }
    }
}

把 MCP Server 暴露的工具转成 Agent 能看懂的统一格式,加上mcp__前缀区分来源。

4. MCP Client 注册表

python 复制代码
clients = {
    "postgres": mcp_client_instance
}

记录已连接的 MCP Client 实例,方便路由器快速查找和转发请求。


最小实现代码(极简可运行)

第一步:实现一个基础的 MCPClient

python 复制代码
class MCPClient:
    def __init__(self, server_config):
        self.config = server_config
        self.process = None
        self.tools = []

    def connect(self):
        # 启动外部Server进程
        self.process = subprocess.Popen(
            [self.config["command"]] + self.config["args"],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        # 建立连接并获取工具列表
        self.tools = self.list_tools()

    def list_tools(self):
        # 向Server请求工具列表
        # 教学版简化,实际按MCP协议通信
        return [{"name": "query", "description": "Run SQL query"}]

    def call_tool(self, tool_name, arguments):
        # 转发工具调用请求给Server
        # 教学版简化,实际按MCP协议通信
        return {"status": "ok", "preview": "Query executed successfully"}

核心能力:连接 Server、获取工具列表、转发调用请求。

第二步:把外部工具标准化为 Agent 工具格式

python 复制代码
def normalize_mcp_tools(server_name, raw_tools):
    normalized = []
    for tool in raw_tools:
        # 加上mcp__前缀,格式:mcp__{server}__{tool}
        prefixed_name = f"mcp__{server_name}__{tool['name']}"
        normalized.append({
            "name": prefixed_name,
            "description": tool["description"],
            "input_schema": tool.get("input_schema", {})
        })
    return normalized

关键:通过前缀规则区分本地和外部工具,避免命名冲突。

第三步:实现统一工具路由器

python 复制代码
class ToolRouter:
    def __init__(self, native_tools, mcp_clients):
        self.native_tools = native_tools
        self.mcp_clients = mcp_clients  # key: server_name, value: MCPClient

    def route(self, tool_name, arguments):
        # 判断是否为MCP工具
        if tool_name.startswith("mcp__"):
            # 解析server名和工具名:mcp__postgres__query
            parts = tool_name.split("__", 2)
            server_name = parts[1]
            tool_name = parts[2]
            # 转发给对应的MCP Client
            client = self.mcp_clients[server_name]
            return client.call_tool(tool_name, arguments)
        else:
            # 本地工具,交给本地处理器
            return self.native_tools[tool_name](arguments)

路由器只做一件事:根据工具名分发请求,不区分工具来源。

第四步:MCP 工具必须经过同一条权限管道

python 复制代码
def check_permission(tool_name, arguments, mode):
    # 权限检查逻辑和本地工具完全一致
    # 即使是MCP工具,也不能绕开权限闸门
    if tool_name.startswith("mcp__"):
        # 提取server和工具信息,进行权限检查
        if is_dangerous_mcp_tool(tool_name):
            return "deny", "Dangerous MCP tool call blocked"
    # 本地工具权限检查
    return native_permission_check(tool_name, arguments, mode)

关键:外部工具也必须经过统一的权限检查,不能成为安全后门。

第五步:结果标准化回流主循环

python 复制代码
def normalize_tool_result(tool_name, raw_result):
    if tool_name.startswith("mcp__"):
        parts = tool_name.split("__", 2)
        server_name = parts[1]
        return {
            "source": "mcp",
            "server": server_name,
            "tool": parts[2],
            "status": raw_result.get("status", "ok"),
            "preview": raw_result.get("preview", "")
        }
    else:
        # 本地工具结果格式
        return {
            "source": "native",
            "tool": tool_name,
            "status": raw_result.get("status", "ok"),
            "preview": raw_result.get("preview", "")
        }

无论本地还是外部工具,结果都要转成主循环能统一处理的格式。


插件、MCP Server、MCP Tool 三层关系

初学者很容易把这三个概念混在一起,这里用表格帮你理清边界:

层级 它是什么 它负责什么
Plugin Manifest 一份配置声明文件 告诉系统要发现和启动哪些 MCP Server
MCP Server 一个外部进程 / 连接对象 对外暴露一组工具能力
MCP Tool Server 暴露的一项具体调用能力 真正被模型点名调用的功能

一句话总结:Plugin 负责 "发现",Server 负责 "连接",Tool 负责 "调用"。


如何接到前面章节的系统里

MCP 不是孤立的外挂,而是接入 Agent 已有体系的扩展层,完整链路如下:

启动时

复制代码
PluginLoader 读取插件配置
  ->
解析并获取MCP Server配置
  ->
启动Server并建立连接
  ->
获取Server的工具列表
  ->
标准化工具名并合并进主程序工具池

运行时

复制代码
LLM 生成工具调用请求
  ->
统一权限闸门检查(和本地工具完全一致)
  ->
工具路由器分发请求(本地/MCP)
  ->
执行工具并返回结果
  ->
结果标准化后回流主循环

关键:进入方式不同,但进入后必须回到同一条控制面和执行面。


初学者最容易被带偏的 3 个坑

  1. 一上来就讲太多协议细节不要一开始就陷入 transports、auth、resources 等复杂概念,主线只有一句话:外部工具也能像本地工具一样接进 Agent。

  2. 把 MCP 当成一套完全不同的工具系统它最终仍然汇入你原来的工具体系:一样要注册、一样要过权限、一样要返回 tool_result,不是独立的外挂。

  3. 忽略命名与路由规则没有统一前缀和路由,本地和外部工具会混在一起,系统很快就会混乱,无法区分工具来源。


S18 → S19 升级了什么?

模块 S18 S19
工具来源 本地硬编码工具 本地工具 + 外部可插拔 MCP 工具
扩展性 新增工具需修改主程序 外部工具通过插件配置接入,无需修改主程序
能力边界 仅依赖开发者手写的工具 可复用第三方成熟服务能力(如数据库、浏览器等)
控制面 统一权限、路由、结果处理 本地和外部工具共享同一控制面,无安全后门
架构地位 多 Agent 并行隔离执行层 系统外部能力扩展层

本章教学边界

本章先停在 "tools-first" 的主线,不展开协议细节:

  • 不深入讲 transports、认证、连接恢复等复杂协议内容
  • 不展开 resources、prompts 等 MCP 扩展能力
  • 只讲清外部工具如何被发现、命名、路由、过权限、回流主循环

这些扩展内容可以放到后续平台桥接文档中展开,正文先聚焦主线,避免读者失焦。


一句话总结本章

MCP 的本质不是协议名词堆砌,而是把外部工具安全、统一地接进你的 Agent。它让工具不再局限于本地硬编码,通过插件和 MCP 协议,让外部能力成为系统的可扩展总线,和本地工具共享同一控制面,实现真正的可扩展 Agent 平台。

相关推荐
Komorebi_99991 小时前
Agent 易混概念辨析 + 全套总复盘
人工智能·agent
测试员周周1 小时前
【Appium 系列】第07节-API测试封装 — BaseAPI 的设计与实现
开发语言·人工智能·功能测试·测试工具·appium·自动化·测试用例
加号31 小时前
【C#】WPF基于Halcon 的HWindowControlWPF 控件实现图像缩放、移动
开发语言·c#·wpf
ComputerInBook2 小时前
C++ 中的 lambda 表达式
开发语言·c++·lambda表达式·匿名函数
千桐科技2 小时前
qKnow 智能体构建平台知识图谱能力优化:围绕图谱探索、知识库、数据源、知识推理、知识融合与概念属性的完善升级
人工智能·大模型·知识图谱·agent·rag·qknow·智能体构建平台
ZC跨境爬虫2 小时前
跟着 MDN 学 HTML day_52:(深入 XPathExpression 接口)
开发语言·前端·javascript·ui·html·音视频
yuanpan2 小时前
Python + Selenium 浏览器自动化测试与网页自动登录
开发语言·python·selenium
Wy_编程2 小时前
Go语言中的指针
开发语言·后端·golang
不想写代码的星星2 小时前
C++协程从入门到放弃?不,是从入门到手搓调度器
开发语言·c++