三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

MCP协议实战:构建标准化AI Agent工具连接器,解决Agent集成痛点

MCP协议实战:构建标准化AI Agent工具连接器,解决Agent集成痛点

如果你最近在关注 AI Agent 的发展,可能会发现一个矛盾的现象:一方面,各种 Agent 框架和工具层出不穷,功能越来越强;另一方面,当你真正想把一个 Agent 集成到自己的业务系统里,或者让它调用一个内部 API 时,却常常感到无从下手,需要为每个框架写一遍适配代码。

这正是当前 AI Agent 生态面临的核心痛点:缺乏一个统一的“连接器”标准。每个 Agent 框架(如 LangChain、AutoGPT、CrewAI)都定义了自己的工具调用方式,每个数据源(如数据库、API、文件系统)都需要单独开发适配插件。这种“烟囱式”的集成方式,极大地增加了开发成本和维护负担,也让 Agent 的能力被局限在各自封闭的生态里。

最近,一个可能改变这一局面的重要动向出现了:OpenAI 联合 Anthropic、Google、微软、英伟达等科技巨头,共同推出了一个名为“模型上下文协议(Model Context Protocol, MCP)”的开放标准。这并非一个具体的产品或 SDK,而是一套旨在让 AI 模型(尤其是 Agent)与外部工具、数据源进行标准化通信的协议规范。

简单来说,MCP 想做的是 AI 世界的“USB 协议”。就像 USB 标准让鼠标、键盘、U 盘可以即插即用到任何电脑上一样,MCP 希望让任何工具(如数据库客户端、API 网关、文件系统)都能以标准化的方式“插入”到任何支持该协议的 AI 模型或 Agent 框架中,实现“一次开发,处处可用”。

本文将深入解析 MCP 协议的核心设计、技术实现,并通过一个完整的实战示例,展示如何将一个本地 SQLite 数据库快速“暴露”给 Claude Desktop 或兼容 MCP 的 IDE 插件,让 AI 助手直接查询数据。我们不仅会探讨“它是什么”,更会聚焦于“它解决了什么问题”、“开发者该如何上手”以及“它可能带来的生态变化”。

1. MCP 要解决的根本问题:Agent 集成的“巴别塔”

在深入技术细节之前,我们必须先理解 MCP 诞生的背景和它要啃的硬骨头。

1.1 当前 Agent 工具集成的混乱现状

假设你开发了一个智能客服 Agent,希望它能查询订单数据库、调用物流 API 并读取知识库文档。在现有技术栈下,你可能需要:

  1. 为 LangChain Agent编写对应的Tool类,实现_run方法,处理数据库连接和 SQL 执行。
  2. 为 AutoGPT编写不同的插件,遵循其特定的命令注册和响应格式。
  3. 如果直接使用 OpenAI 的 Assistant API,又需要定义 Function Calling 的 JSON Schema,并在后端实现对应的函数。

这三种方式,本质上你都在做同一件事:将后端能力“翻译”成 AI 模型能理解的语言。但由于“翻译规则”(即协议)不同,你需要重复劳动三次。更糟糕的是,当数据库 schema 变更或 API 升级时,你需要同步维护三套代码。

1.2 MCP 的核心理念:关注点分离

MCP 协议的核心设计思想是“关注点分离”

  • 工具/数据提供方(Server):只专注于一件事——以标准化的方式暴露自己的能力(如“执行 SQL 查询”、“读取文件列表”)。它不关心谁来调用、怎么调用。
  • AI 模型/客户端(Client):只专注于另一件事——理解用户意图,并从可用的工具列表中选取合适的工具来调用。它不关心工具的具体实现细节。
  • MCP 协议(Transport):作为中间层,定义了一套严格的、与具体模型和框架无关的通信格式(基于 JSON-RPC),确保 Server 和 Client 能互相理解。

这种架构带来的直接好处是:一个 MCP Server(例如一个 SQLite 服务器)开发完成后,可以同时被 Claude Desktop、Cursor IDE、Windmill 工作流引擎等任何支持 MCP Client 的应用使用。生态的繁荣从“重复造轮子”转向“共建基础设施”。

2. MCP 协议核心概念与技术架构

MCP 不是一个庞大的框架,而是一组轻量级的规范。理解其核心组件是上手的关键。

2.1 核心组件三元组

任何 MCP 交互都涉及三个角色:

  1. MCP Server(服务器)

    • 角色:能力提供者。它可以是任何能通过程序访问的资源,如数据库、API 网关、文件系统、内部业务系统。
    • 职责:启动后,向 Client 宣告自己提供了哪些“工具(Tools)”和“资源(Resources)”,并等待 Client 的调用请求。
    • 示例:一个提供query_database工具的 PostgreSQL MCP Server。
  2. MCP Client(客户端)

    • 角色:能力消费者。通常是 AI 应用或 IDE,它内嵌了 MCP 协议的处理逻辑。
    • 职责:发现并连接 Server,获取可用的工具和资源列表,在需要时代表用户(或自主)调用这些工具。
    • 示例:Claude Desktop 应用、Cursor IDE 的 AI 侧边栏。
  3. Transport(传输层)

    • 角色:通信管道。定义 Server 和 Client 如何连接和交换信息。
    • 类型
      • stdio(标准输入输出):最常见的方式,Server 作为一个子进程启动,通过 stdin/stdout 与 Client 通信。适合本地集成。
      • sse(服务器发送事件):基于 HTTP,允许远程 Server。更适合云端或跨网络场景。

2.2 核心能力模型:Tools 与 Resources

MCP 定义了两种主要的能力类型,这也是 Server 向 Client 宣告的内容:

  • Tools(工具):代表一个可执行的操作,通常会有输入参数,并产生输出。类比为函数调用。

    • 示例execute_sql(query: string) -> stringsend_email(to: string, subject: string, body: string) -> boolean
    • 在对话中:用户说“帮我查一下上个月的销售额”,Client 可能会选择调用execute_sql这个 Tool。
  • Resources(资源):代表可读取的静态或动态内容,通常作为上下文提供给模型。类比为文件或数据源。

    • 示例file:///path/to/docs/guide.md(一个文件),db://schema/tables(数据库表结构信息)。
    • 在对话中:这些资源可以被“注入”到模型的上下文窗口,帮助它更好地理解领域知识,而无需通过 Tool 去查询。

2.3 通信流程概览

一次典型的 MCP 交互遵循以下序列:

  1. 初始化:Client 启动(或配置)一个 MCP Server 进程(通过 stdio 或 SSE 连接)。
  2. 握手:双方交换初始化消息,协商协议版本。
  3. 能力宣告:Server 发送tools/listresources/list通知,告诉 Client “我有什么”。
  4. 工具调用:用户提出需求 -> Client 分析需求,选择工具 -> Client 向 Server 发送tools/call请求 -> Server 执行并返回tools/call结果 -> Client 将结果呈现给用户。
  5. 资源读取:Client 可以根据需要,发送resources/read请求来获取资源内容,并将其作为背景知识填入提示词。

3. 环境准备:从零开始构建你的第一个 MCP Server

理论讲完了,我们动手实战。我们将创建一个最简单的 MCP Server,它提供一个工具,可以查询本地 SQLite 数据库。完成后,我们将把它配置到 Claude Desktop 中使用。

3.1 前置条件与工具选择

  • 操作系统:macOS、Linux 或 Windows (WSL2 推荐)。本文以 macOS/Linux 命令行示例为主。
  • 编程语言:MCP 协议与语言无关。官方提供了TypeScript/JavaScriptPython的 SDK,极大降低了开发门槛。我们选择 Python,因其在数据处理和 AI 生态中应用广泛。
  • Python 环境:建议使用 Python 3.10 及以上版本。使用venvconda创建虚拟环境。
  • 目标客户端:我们将使用Claude Desktop作为 MCP Client 进行测试。请确保已安装 Claude Desktop 应用。
  • 基础工具git,pip

3.2 初始化项目与安装 SDK

首先,创建一个项目目录并初始化 Python 环境。

# 创建项目目录 mkdir mcp-sqlite-demo && cd mcp-sqlite-demo # 创建并激活虚拟环境 (可选但推荐) python3 -m venv .venv source .venv/bin/activate # Linux/macOS # .venv\Scripts\activate # Windows # 安装官方 MCP Python SDK pip install mcp

mcp这个包是 OpenAI 等维护的官方 Python SDK,它封装了协议细节,让我们可以像写普通 Python 函数一样创建 Tools 和 Resources。

3.3 准备示例数据

我们创建一个简单的 SQLite 数据库,包含一个sales表。

# 使用 sqlite3 命令行工具创建数据库和表 sqlite3 demo.db <<EOF CREATE TABLE sales ( id INTEGER PRIMARY KEY, date TEXT NOT NULL, region TEXT NOT NULL, product TEXT NOT NULL, amount REAL NOT NULL ); INSERT INTO sales (date, region, product, amount) VALUES ('2024-01-15', 'North', 'Laptop', 1200.50), ('2024-01-16', 'South', 'Mouse', 25.99), ('2024-01-17', 'East', 'Keyboard', 89.99), ('2024-01-18', 'West', 'Monitor', 350.00), ('2024-01-19', 'North', 'Laptop', 1100.00), ('2024-01-20', 'South', 'Monitor', 375.50); EOF echo "示例数据库 demo.db 已创建。"

4. 核心流程拆解:编写 SQLite MCP Server

现在,我们开始编写 Server 的核心代码。我们将创建一个server.py文件。

4.1 导入依赖与定义工具

# server.py import sqlite3 import json from typing import Any from mcp import Server, Tool import mcp.server.stdio # 初始化 MCP Server 实例 server = Server("sqlite-demo-server") # 定义第一个 Tool:查询数据库 @server.list_tools() async def list_tools() -> list[Tool]: """向客户端宣告本 Server 提供的工具列表。""" return [ Tool( name="query_database", # 工具名称,客户端据此调用 description="执行一条只读的 SQL SELECT 查询语句,并返回结果。用于查询销售数据。", # 给 AI 看的描述 inputSchema={ # 定义输入参数的 JSON Schema "type": "object", "properties": { "sql": { "type": "string", "description": "要执行的 SQL SELECT 查询语句。" } }, "required": ["sql"] # 必填参数 } ) ] # 定义 Tool 的执行函数 @server.call_tool() async def call_tool(name: str, arguments: dict[str, Any]) -> list[dict[str, Any]]: """处理客户端发来的工具调用请求。""" if name == "query_database": sql = arguments.get("sql", "") if not sql.strip().upper().startswith("SELECT"): return [{ "type": "text", "text": "错误:此工具仅支持 SELECT 查询,以确保数据安全。" }] try: # 连接 SQLite 数据库 conn = sqlite3.connect('demo.db') conn.row_factory = sqlite3.Row # 使返回结果为字典形式 cursor = conn.cursor() cursor.execute(sql) rows = cursor.fetchall() conn.close() # 格式化结果 if rows: # 获取列名 columns = [description[0] for description in cursor.description] result_text = "查询成功!结果如下:\n\n" result_text += " | ".join(columns) + "\n" result_text += "-" * (len(" | ".join(columns))) + "\n" for row in rows: result_text += " | ".join(str(row[col]) for col in columns) + "\n" result_text += f"\n共 {len(rows)} 行记录。" else: result_text = "查询成功,但未找到匹配的记录。" return [{ "type": "text", "text": result_text }] except sqlite3.Error as e: return [{ "type": "text", "text": f"数据库查询出错:{e}" }] except Exception as e: return [{ "type": "text", "text": f"执行过程中发生未知错误:{e}" }] # 如果收到未知的工具名请求 return [{ "type": "text", "text": f"未知的工具:{name}" }]

关键点解析:

  1. @server.list_tools():这是一个装饰器,用于注册“列出工具”的处理函数。当 Client 初始化时,会调用此函数获取工具列表。
  2. Tool对象:定义了工具的元数据。description至关重要,AI 模型(如 Claude)会阅读它来决定是否以及如何调用此工具。
  3. @server.call_tool():装饰器,用于注册“调用工具”的处理函数。参数namearguments由 Client 传入。
  4. 输入验证:在call_tool中,我们检查 SQL 是否以SELECT开头,这是一个简单的安全措施,防止数据被修改或删除。
  5. 返回格式:MCP 要求 Tool 调用返回一个内容列表。目前我们只返回简单的文本 (type: “text”),但它也支持图片、嵌入式资源等复杂类型。

4.2 添加资源支持(可选但推荐)

除了工具,我们还可以暴露一些静态资源,比如数据库的表结构信息,帮助 AI 更好地构建查询。

# 在 server.py 的 list_tools 函数后添加 from mcp import Resource @server.list_resources() async def list_resources() -> list[Resource]: """向客户端宣告本 Server 提供的资源列表。""" return [ Resource( uri="db://schema/tables", # 资源 URI,唯一标识符 name="sales_table_schema", # 资源名称 description="sales 表的详细结构定义,包括字段名、类型和说明。", # 描述 mimeType="text/plain" # MIME 类型 ) ] @server.read_resource() async def read_resource(uri: str) -> str: """处理客户端读取资源的请求。""" if uri == "db://schema/tables": schema_info = """ ## 数据库表结构:sales (销售记录表) | 字段名 | 数据类型 | 说明 | |--------|----------|------| | id | INTEGER | 主键,自增ID | | date | TEXT | 销售日期,格式 'YYYY-MM-DD' | | region | TEXT | 销售区域,可选值:'North', 'South', 'East', 'West' | | product | TEXT | 产品名称,如 'Laptop', 'Mouse', 'Keyboard', 'Monitor' | | amount | REAL | 销售金额(美元) | **示例查询:** - 查询所有记录:`SELECT * FROM sales;` - 按区域汇总销售额:`SELECT region, SUM(amount) as total_sales FROM sales GROUP BY region;` - 查找某产品销量:`SELECT * FROM sales WHERE product = 'Laptop';` """ return schema_info return f"未找到资源:{uri}"

关键点解析:

  1. @server.list_resources()@server.read_resource():与工具类似,用于宣告和读取资源。
  2. 资源 URI:类似于 URL,是资源的唯一标识。Client 可以通过resources/read请求获取其内容。
  3. 用途:当用户在 Claude Desktop 中提问“数据库里有什么表?”时,Claude 可以主动读取db://schema/tables这个资源来获取信息,而无需调用query_database工具去执行PRAGMA table_info。这更高效,也更符合直觉。

4.3 启动 Server 的主函数

最后,我们需要一个入口点来启动基于 stdio 的服务器。

# 在 server.py 文件末尾添加 async def main(): """启动 MCP Server,使用标准输入输出作为传输层。""" async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): await server.run(read_stream, write_stream, server.create_initialization_options()) if __name__ == "__main__": import asyncio asyncio.run(main())

5. 完整示例与配置:连接 Claude Desktop

代码写好了,如何让 Claude Desktop 识别并使用我们的 Server 呢?这需要通过一个配置文件来完成。

5.1 创建 Claude Desktop 的 MCP 配置文件

Claude Desktop 会在特定目录查找 MCP 服务器的配置。在 macOS 上,路径通常是~/Library/Application Support/Claude/claude_desktop_config.json。在 Windows 上,是%APPDATA%\Claude\claude_desktop_config.json

我们先创建这个配置文件(如果不存在的话)。

// ~/Library/Application Support/Claude/claude_desktop_config.json { "mcpServers": { "sqlite-demo": { "command": "/absolute/path/to/your/.venv/bin/python", "args": [ "/absolute/path/to/your/mcp-sqlite-demo/server.py" ], "env": { "PYTHONPATH": "/absolute/path/to/your/mcp-sqlite-demo" } } } }

配置详解:

  • mcpServers:顶级键,包含所有要加载的 MCP Server。
  • sqlite-demo:你为这个 Server 起的任意名字,会在 Claude 界面中显示。
  • command:启动 Server 进程的命令。这里指向我们虚拟环境中的 Python 解释器。
  • args:传递给命令的参数,即我们的server.py脚本的绝对路径
  • env:(可选)环境变量。这里设置了PYTHONPATH确保脚本能正确导入本地模块。

重要提示:你必须将上述路径/absolute/path/to/your/mcp-sqlite-demo/absolute/path/to/your/.venv替换为你电脑上的真实绝对路径。可以使用pwd命令(Linux/macOS)或cd后复制路径(Windows)来获取。

5.2 重启 Claude Desktop 并验证

  1. 完全关闭Claude Desktop 应用。
  2. 重新启动Claude Desktop。
  3. 打开与 Claude 的对话窗口。如果配置成功,你通常会在输入框上方或侧边栏看到一个新的图标或提示,表明已连接自定义工具。不同版本 UI 可能不同。
  4. 尝试提问,例如:
    • “我们数据库里有什么表?”
    • “帮我查一下北部(North)地区的总销售额是多少?”
    • “列出所有显示器的销售记录。”

Claude 应该能识别出query_database工具,并自动生成相应的 SQL 查询来调用它,最后将结果返回给你。

6. 运行结果与效果验证

如果一切配置正确,你的对话将呈现以下智能交互:

:“我们数据库里有什么表?”

Claude:(它可能会先读取db://schema/tables资源)根据数据库结构,有一个名为sales的表,用于记录销售数据。表结构如下:...(展示资源内容)。你想查询这个表的数据吗?

:“帮我查一下北部(North)地区的总销售额是多少?”

Claude:(思考后,决定调用query_database工具)我将执行查询来获取北部地区的总销售额。 (调用工具,传入 SQL:SELECT SUM(amount) as total_north_sales FROM sales WHERE region = 'North';) 工具返回结果:查询成功!结果如下:

total_north_sales
2300.5
共 1 行记录。
所以,北部地区的总销售额是2300.5 美元

:“列出所有显示器的销售记录。”

Claude:(调用工具,传入 SQL:SELECT * FROM sales WHERE product = 'Monitor';) 工具返回结果:查询成功!结果如下:

iddateregionproductamount
42024-01-18WestMonitor350.0
62024-01-20SouthMonitor375.5
共 2 行记录。

至此,你已经成功创建了一个功能完整的 MCP Server,并让 Claude Desktop 这个“通用客户端”具备了查询你私有数据库的能力。这比以往为特定 AI 助手编写定制插件要简洁和通用得多。

7. 常见问题与排查思路

在开发和配置过程中,你可能会遇到以下问题:

问题现象可能原因排查方式解决方案
Claude Desktop 启动后无任何新工具提示。1. 配置文件路径或名称错误。
2. 配置文件 JSON 格式错误。
3. Claude Desktop 未读取到新配置。
1. 检查配置文件是否在正确的操作系统路径下。
2. 使用jq . config.json或在线 JSON 校验工具检查格式。
3. 彻底关闭 Claude Desktop(包括任务栏/托盘图标)再重启。
1. 确认路径,特别是 Windows 的%APPDATA%和 macOS 的~/Library
2. 修正 JSON 语法错误,如多余的逗号。
3. 确保进程完全重启。
Claude 能识别工具,但调用时失败或超时。1.commandargs中的路径错误。
2. Python 依赖未安装。
3. Server 脚本存在语法或运行时错误。
4. 数据库文件路径不对。
1. 在终端手动执行配置中的命令,看能否启动 Server。
2. 检查虚拟环境是否激活,`pip list
grep mcp。<br>3. 查看 Claude Desktop 的应用日志(位置因系统而异)。<br>4. 在server.py` 中使用绝对路径连接数据库。
工具调用返回“未知的工具”。Server 中@server.call_tool()装饰的函数未正确处理工具名。检查call_tool函数中的if name == “query_database”:判断是否与list_tools中定义的name完全一致(大小写敏感)。确保工具名字符串匹配。
Claude 不主动读取资源。资源 URI 在提问中未被触发,或 Claude 当前版本/策略对资源使用保守。尝试更明确的提问,如“请先告诉我数据库的表结构”。资源是“锦上添花”,核心功能是工具。确保资源描述清晰。工具是主要交互方式。

高级排查:你可以使用一个名为mcp-cli的调试工具,在不启动 Claude 的情况下测试你的 Server,这对于开发阶段非常有用。

8. 最佳实践与工程建议

将 MCP 用于实际项目时,遵循以下建议可以避免很多坑:

  1. 安全性是第一要务

    • 最小权限原则:为 MCP Server 进程配置具有最小必要权限的数据库用户或系统账户。永远不要使用 root 或管理员账号。
    • 输入验证与净化:我们的示例仅检查SELECT,在实际生产中,需要对 SQL 进行更严格的校验,或使用参数化查询、ORM 等来彻底杜绝 SQL 注入。
    • 访问控制:在 Server 端实现基于令牌或 IP 的简单认证(虽然 stdio 模式多在本地)。对于 SSE 远程模式,必须启用 HTTPS 和强认证。
    • 敏感信息过滤:在返回查询结果前,检查并过滤掉密码、密钥、个人身份信息等敏感数据。
  2. 设计良好的工具与资源

    • 清晰的描述(Description):这是 AI 理解工具用途的唯一依据。描述应简洁、准确,包含关键参数和示例。例如:“查询用户订单:根据用户ID和日期范围查询订单详情,返回订单列表。”
    • 合理的工具粒度:不要设计一个“万能”工具。而是拆分为“查询订单”、“创建订单”、“更新订单状态”等具体工具。这有助于 AI 更准确地选择和调用。
    • 善用资源(Resources):将静态的、频繁使用的参考信息(如 API 文档、数据字典、公司制度)定义为资源。这比通过工具动态查询更高效,并能减少模型上下文窗口的消耗。
  3. 工程化与部署

    • 配置化管理:将数据库连接字符串、API 密钥等敏感信息从代码中剥离,使用环境变量或配置文件管理。
    • 错误处理与日志:在 Server 中实现完善的错误处理和日志记录,便于排查问题。日志应记录工具调用、参数和结果(脱敏后)。
    • 性能考虑:对于可能返回大量数据的工具,考虑支持分页(在参数中添加limitoffset)。避免单次调用拖慢整个 AI 交互。
    • 版本化:当你的工具接口需要变更时,考虑通过工具名或参数版本化来保持向后兼容,避免影响已配置的客户端。
  4. 超越数据库:更多的 Server 想象空间

    • 内部 API 网关:创建一个 MCP Server 来代理公司内部的所有微服务 API,让 AI 助手能够安全地调用内部系统。
    • 文件系统浏览器:让 AI 可以安全地浏览、读取指定目录下的项目文档、日志文件。
    • 代码仓库查询:连接 GitLab/GitHub API,让 AI 能查询提交历史、检索代码片段。
    • 监控与告警:连接 Prometheus 或 Grafana,让 AI 能查询系统当前指标。

9. 总结与生态展望

通过本文的实战,我们不仅亲手构建了一个可用的 MCP Server,更重要的是,我们体验了“开放标准”如何降低集成复杂度。MCP 协议的价值,不在于它本身的技术有多高深,而在于它试图建立一种共识,让 AI 能力提供方和消费方能够用一种通用语言对话。

对于开发者而言,MCP 带来的直接收益是:

  • 开发效率提升:写一个 MCP Server,即可赋能所有兼容的 AI 客户端。
  • 维护成本降低:只需维护一套后端适配代码。
  • 生态互操作性:你的工具可以更容易地被集成到不同的 AI 工作流中。

从更宏观的视角看,OpenAI、Anthropic、Google 等巨头联手推动 MCP,标志着 AI 应用开发正从“模型中心化”走向“工具生态化”。未来的竞争可能不再只是大模型本身能力的竞争,更是谁能构建更丰富、更易用的工具生态的竞争。对于广大开发者和企业来说,现在开始关注并尝试 MCP,是在为未来 AI 原生应用的基础设施布局。

下一步,你可以

  1. 尝试为你团队内部的 CRM、ERP 系统创建一个 MCP Server。
  2. 探索使用 SSE 传输模式,将 Server 部署到内网服务器,供多个客户端远程连接。
  3. 关注 MCP 官方 GitHub 仓库 ,了解协议更新和社区贡献的众多开源 Server 实现(如 PostgreSQL、GitHub、Slack 等)。

将你的业务能力“MCP 化”,或许就是让你在即将到来的 Agent 时代,抢占先机的第一步。建议收藏本文,当你需要连接下一个内部系统时,这份指南或许能派上用场。

← 返回列表