MCP(Model Context Protocol,模型上下文协议)是一套标准化的工具与上下文接入协议,解决“AI 应用如何统一接入外部工具、数据源和上下文”的问题。它最初由 Anthropic 提出并开源,核心思想并不复杂:模型应用不再为每个工具单独开发私有适配器,而是通过同一套协议,把“模型能用什么工具、能读什么上下文”变成标准化接口。过去一年里,Claude Desktop、Cursor、Codex、Cherry Studio、Dify 等客户端和服务端产品陆续加入 MCP 支持,社区也出现了数据库、浏览器、设计稿、游戏引擎、安全分析等大量 MCP Server。下面从协议要解决的问题开始,逐步完成一个最小 MCP Server 的搭建、客户端接入、验证调试和生产落地配置。阅读完这篇内容,你能判断什么场景该用 MCP、如何自建 MCP Server、接入失败时该从哪条链路排查。适合想在 Cursor、Claude Code 等工具中接入私有数据的应用开发者,也适合需要把自有系统暴露给多个 AI 客户端的后端工程师。
1. MCP 要解决什么问题,为什么会产生这个协议
1.1 工具接入碎片化的现状
在 MCP 出现之前,AI 应用接入外部能力基本是“一对一”的适配。假设一个团队同时维护三个 AI 应用(一个桌面助手、一个代码编辑器插件、一个客服机器人),要接入四类外部系统(MySQL、Jira、Figma、企业内部接口),就需要开发十几套不同的适配逻辑:每套系统有各自的认证方式、数据格式和调用约定,每个 AI 应用都要重新实现一遍。
更麻烦的是,工具与模型之间的能力描述没有统一规范。同一个“查询用户订单”的功能,在 A 应用里通过 Python 函数暴露,在 B 应用里变成 HTTP 接口,在 C 应用里又成了提示词。模型很难在这些形态之间复用能力,开发者维护成本也成倍增长。搜索热词里大量出现的“cursor连接蓝湖mcp”“playwright mcp”“unity mcp”正是这种痛点的体现:大家希望把设计稿、浏览器操作、游戏引擎这些原本高度专用的能力,变成 AI 客户端能统一理解的东西。
1.2 MCP 的设计定位
MCP 把这个问题归约成一套协议。它基于 JSON-RPC 2.0,在 AI 应用(Host)和工具提供方(MCP Server)之间建立标准化会话。Host 启动连接后,先发现 Server 暴露了哪些工具、资源或提示词,再在需要时发起调用;Server 执行具体逻辑后,把结构化结果返回给模型。整个过程对工具提供方透明,不同客户端只要实现一次 MCP Client,就能连接同一个 Server。
设计上有几个关键取舍:
- 统一接口优先。无论工具背后是数据库、REST API、本地文件还是图形界面自动化,对外都收敛为 tools、resources、prompts 三类原语。
- 传输与部署解耦。本地工具用 stdio,远程服务用 HTTP,协议层保持一致,切换部署形态不需要重写工具逻辑。
- 权限边界清晰。MCP Server 只暴露它认为安全的能力,Host 负责向用户展示权限确认,模型不能越过协议直接访问后端资源。
1.3 MCP 与 Function Calling、Skills、Computer Use 的区别在哪里
很多资料把 MCP 和 Function Calling、Skills、Computer Use 混在一起讲,实际上它们处于不同层次,解决的问题也不一样。
| 方案 | 本质 | 典型场景 | 与 MCP 的关系 |
|---|---|---|---|
| Function Calling | 模型 API 层的能力,允许模型按声明的函数 schema 输出调用参数 | 单模型单应用内的函数调用 | MCP 可以包装 Function Calling,让多客户端复用工具 |
| MCP | 标准化的工具与上下文接入协议 | 多个 AI 客户端复用同一套工具和上下文 | 本文主题 |
| Skills | 提示词、流程和经验的沉淀 | 复用固定工作流,指导模型按步骤执行 | 与 MCP 互补,Skill 可以引导模型在合适时机调用 MCP 工具 |
| Computer Use | 模型直接操作屏幕、鼠标和键盘的界面自动化 | 处理没有 API 的桌面软件 | 面向界面交互,MCP 面向结构化接口,两者解决不同问题 |
从实战角度理解:如果只需要在单个模型里调用几个内部函数,直接用 Function Calling 足够;如果同一个工具要在多个客户端上复用,或者希望把工具注册、权限、审计沉淀成平台能力,就值得走 MCP。Skills 解决“怎么提示”,MCP 解决“能调用什么、怎么调用”,两者叠加使用是当前 Claude Code、Cursor 里很常见的组合,也是“skills如何调用mcp工具”这类问题背后的实际场景。
2. 核心概念:Host、Client、Server 与三种原语
2.1 三层角色划分
MCP 的调用链路上有三个角色。
- Host:面向用户的 AI 应用,负责承载模型、管理会话和展示结果。Claude Desktop、Cursor、Codex CLI 都是 Host。
- Client:运行在 Host 内部的协议处理器,负责与 MCP Server 建立连接,维护会话状态,发送和接收 JSON-RPC 消息。
- Server:对外暴露能力的独立进程或服务。它负责注册工具、提供资源和提示词,并在收到调用请求时执行真实逻辑。
一句话概括:Host 是“用户看到的东西”,Client 是“协议里的会话方”,Server 是“能力的提供方”。大多数时候不需要理解 Client 的实现细节,但要知道配置入口改的是 Host 对一个 Server 的启动方式,所以命令、路径、环境变量这些信息会直接影响 Client 能否拉起 Server。
2.2 Tools、Resources、Prompts 三种原语
MCP Server 对外暴露的能力被收敛为三种原语。理解它们之间的差别,是设计好一个 Server 的前提。
| 原语 | 方向 | 作用 | 典型例子 |
|---|---|---|---|
| Tools | 模型到工具 | 由模型发起调用,执行动作并返回结果 | 查询用户、发送邮件、执行 SQL、列出目录 |
| Resources | 模型读数据 | 向模型提供只读上下文,按需读取 | 配置文件、项目文档、数据库 schema 说明 |
| Prompts | 模板 | 可复用的提示词模板,让模型按约定流程工作 | 代码评审模板、SQL 生成模板 |
使用上有几个常见误区。第一,Tools 不等于后端接口,它是给模型看的“可调用动作”,描述好不好直接影响模型是否会调用。第二,Resources 不要设计成动态查询入口,它更接近“上下文素材”,高频写入场景应该用 Tools。第三,Prompts 不是必须的,但它适合把团队内部的经验固化下来,比如规定 SQL 生成前必须先读取表结构 Resource。
2.3 一次完整调用链路
当用户在 Host 里问“查一下用户 u_1001 的等级”时,完整链路大致如下。
- Host 加载已配置的 MCP Server,并建立连接。
- Client 发送
tools/list,获取 Server 暴露的工具列表,包括名称、描述和参数 schema。 - 模型根据用户问题和工具描述,判断应该调用
get_user_info,并生成参数。 - Client 发送
tools/call请求,参数是{"user_id": "u_1001"}。 - Server 执行函数,返回统一结构的内容。
- 模型拿到结果后,组织成自然语言返回给用户。
协议消息是 JSON-RPC 2.0 格式。下面是一个工具调用请求的示例。
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_user_info", "arguments": { "user_id": "u_1001" } } }对应的返回结果长这样。
{ "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "{\"user_id\": \"u_1001\", \"name\": \"测试用户\", \"level\": \"vip\"}" } ] } }不需要手写这些消息,官方 SDK 会帮你完成序列化和反序列化。但理解这段链路对排错非常关键:问题可能出在“Server 没暴露工具”“模型没选择调用”“参数序列化失败”“函数内部抛异常”这四层中的任何一层。
2.4 stdio 和 HTTP 两种传输方式
MCP 支持两种主流传输方式,对应不同部署形态。
| 传输方式 | 工作原理 | 适合场景 | 部署复杂度 |
|---|---|---|---|
| stdio | 客户端启动 Server 进程,通过标准输入和输出传递 JSON-RPC 消息 | 本地开发、个人工具、与代码编辑器集成 | 低 |
| HTTP | Server 独立运行,客户端通过 URL 访问 | 远程共享、多用户、平台化接入 | 高,需要认证和运维 |
stdio 的优点是配置简单、进程生命周期由客户端接管;缺点是 Server 只能被本机发起它的客户端使用,不适合跨机器共享。HTTP 方式对应热词里的“远程 MCP”“MCP 服务搭建”,它要求服务端维护鉴权、限流和可用性,生产环境通常还需要结合 OAuth 等认证机制。
注意:配置客户端时,先想清楚 Server 跑在哪里。写着
command的配置走 stdio,写着url的配置走 HTTP,两种方式不能混用。
3. 从零搭建一个最小 MCP Server
3.1 环境准备
本文使用 Python SDK 做示例,因为从注册工具到跑通只需要一个文件。建议使用 Python 3.10 及以上版本,并先创建虚拟环境。
python -m venv .venv source .venv/bin/activate # Windows 使用 .venv\Scripts\activate pip install "mcp[cli]"装完后确认 SDK 能正常导入。
python -c "import mcp; print(mcp.__version__)"如果输出一个版本号,说明环境没问题。Node 技术栈同样可以开发 MCP Server,对应的是@modelcontextprotocol/sdk,后续示例中的协议概念完全一致。MCP SDK 还在快速迭代,落地前先确认你安装的 SDK 版本,不同小版本的 API 可能会有差异。
3.2 用 FastMCP 注册两个工具
创建一个mcp_demo_server.py,用官方 SDK 提供的 FastMCP 封装快速实现一个最小 Server。
from mcp.server.fastmcp import FastMCP mcp = FastMCP("demo-server") @mcp.tool() def add(a: int, b: int) -> int: """计算两个整数之和,返回求和结果。""" return a + b @mcp.tool() def get_user_info(user_id: str) -> dict: """根据用户 ID 查询用户信息,返回昵称和会员等级。""" # 实际项目中可以在这里访问数据库、内部接口或缓存。 # 演示环境直接返回固定数据,方便验证调用链路。 return {"user_id": user_id, "name": "测试用户", "level": "vip"} if __name__ == "__main__": mcp.run(transport="stdio")这里有两个细节决定模型能不能用好工具。第一,函数 docstring 会成为模型看到的工具描述,写得具体才能让模型在正确时机调用。add的说明要写清楚“计算两个整数之和”,而不是写“这是一个加法函数”。第二,类型注解会生成参数 schema,user_id: str会告诉模型这里需要一个字符串参数;如果不写类型,部分 SDK 会把参数当成自由对象,调用时容易出现类型不匹配。
除了工具,还可以用类似方式暴露 Resources 和 Prompts。
@mcp.resource("config://app") def get_config() -> str: """返回应用的基础配置,供模型在讨论配置问题时读取。""" return "log_level=info\ncache_ttl=60" @mcp.prompt() def sql_review(sql: str) -> str: """生成一条 SQL 评审提示词模板。""" return f"请从性能、安全、可读性三个方面评审这条 SQL:\n{sql}"3.3 运行与自我验证
先手动启动 Server,确认它能正常等待连接。
python mcp_demo_server.py这个进程不会退出,也不会打印内容,它在等待标准输入中出现 JSON-RPC 消息,直接 Ctrl+C 关掉即可。只启动进程还不够,要完整验证协议链路,可以写一个极简客户端测试脚本。
import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main(): server_params = StdioServerParameters( command="python", args=["mcp_demo_server.py"], ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools = await session.list_tools() print("可用工具:", [t.name for t in tools.tools]) result = await session.call_tool("add", {"a": 3, "b": 5}) print("add(3,5) 调用结果:", result) if __name__ == "__main__": asyncio.run(main())运行命令如下。
python client_test.py正常输出会把add、get_user_info两个工具列出来,并显示add(3,5)返回的内容中包含计算结果 8。做到这一步,说明 Server 本身没有问题,后续如果客户端里看不到工具,问题就出在客户端配置,而不是 Server 逻辑。
4. 把 Server 接入客户端:Claude Desktop、Cursor 与配置详解
4.1 Claude Desktop 与 Claude Code 的配置方式
Claude Desktop 通过配置文件声明 MCP Server。配置文件的具体路径会随系统和版本变化,修改前先确认你使用的版本对应的路径。配置结构如下。
{ "mcpServers": { "demo-server": { "command": "python", "args": ["/绝对路径/mcp_demo_server.py"], "env": {} } } }Claude Code 这类命令行客户端通常提供命令式配置,不需要手工改 JSON。例如:
claude mcp add demo-server -- python /绝对路径/mcp_demo_server.py如果使用uvx而非python,可以把command写成uvx,在args里写包名,SDK 会自动解析依赖。这种方式对远程机器和团队协作更友好,但第一次启动会因为下载依赖而更慢。修改或添加配置后,必须重启客户端或触发 MCP 重新加载,配置才会生效。
4.2 Cursor 的 MCP 配置入口
Cursor 同时支持界面配置和项目配置文件两种方式。在项目根目录创建.cursor/mcp.json。
{ "mcpServers": { "demo-server": { "command": "python", "args": ["/绝对路径/mcp_demo_server.py"], "env": {} } } }保存后回到 Cursor 的 MCP 面板,确认 demo-server 显示为已连接。界面配置适合一次性调试,项目配置文件适合提交到团队仓库统一管理。不同版本 Cursor 对 MCP 的权限入口位置会有差异,以当前版本界面提示为准。如果改动后没有生效,先尝试重载窗口或重启应用,再检查连接状态。
4.3 配置项参数详解
无论哪个客户端,stdio 类型的 MCP Server 配置都围绕几个固定字段。
| 字段 | 含义 | 常见值 | 注意事项 |
|---|---|---|---|
| command | 要启动的可执行文件 | python、node、uvx、npx | 必须能在客户端的 PATH 中找到,否则用绝对路径 |
| args | 传给命令的参数 | ["/path/server.py"] | 脚本路径建议写绝对路径,避免相对路径踩坑 |
| env | 注入给 Server 进程的环境变量 | {"DATABASE_URL": "..."} | 敏感信息不要提交到版本库 |
| url | HTTP 传输时的 Server 地址 | https://api.example.com/mcp | 仅用于远程 Server,与 command/args 互斥 |
注意env里的环境变量只对当前 MCP Server 进程生效,不会影响客户端里的其他工具。需要团队复用的配置,建议集中到统一的配置中心或启动脚本里,而不是分散写在每个客户端的 JSON 文件中。如果客户端提示“找不到 python”或“spawn ENOENT”,最常见的原因就是 command 不在客户端进程的 PATH 里,本地开发时优先写python或python3对应的绝对路径。
注意:修改 MCP 配置后不会自动热加载。遇到“刚改完还不行”的情况,先重启客户端,再看服务端日志,不要反复改配置浪费时间。
5. 验证调用链路:Inspector、日志与检查清单
5.1 用 MCP Inspector 做协议级调试
官方提供 MCP Inspector,它相当于一个临时 Host,可以直接连接你的 Server,查看工具列表、发起调用并观察原始 JSON-RPC 消息。对 stdio 的 Server,一条命令就能启动。
npx @modelcontextprotocol/inspector python /绝对路径/mcp_demo_server.py启动后浏览器会打开调试面板,默认地址通常是 localhost 上的一个本地端口,以命令实际输出为准。在面板里可以:
- 查看 Server 暴露的 Tools、Resources、Prompts;
- 手动填写参数并调用单个工具;
- 查看请求和响应消息,确认 schema 是否正确;
- 观察 Server 端的报错堆栈。
Inspector 最适合用来区分两类问题:如果 Inspector 里能正常调用,说明 Server 没问题,问题在客户端配置;如果 Inspector 里也报错,说明是 Server 实现有 bug,先修 Server。
5.2 客户端日志和 Server 端日志怎么看
不同客户端记录 MCP 日志的方式不同,有的在界面事件面板展示,有的输出到日志文件。排查时不要一上来就猜,按顺序做三件事:
- 在客户端里断开并重连当前 MCP Server,观察连接状态变化。
- 使用 Inspector 独立启动同一个启动命令,验证是否能列出工具。
- 在 Server 的工具函数里加上日志或 print,观察调用是否到达 Server。
例如在add函数里临时加上一行输出。
@mcp.tool() def add(a: int, b: int) -> int: """计算两个整数之和,返回求和结果。""" print(f"[demo-server] add called: {a} + {b}", flush=True) return a + b如果调用请求到了但结果不对,会在标准输出里看到参数;如果完全没有输出,说明请求根本没有到达 Server,问题在连接层或客户端配置。
5.3 接入完成后的验证清单
每次接入一个新的 MCP Server,建议按下面的清单确认一遍。
| 检查项 | 确认方式 | 期望结果 |
|---|---|---|
| Server 能独立启动 | 命令行直接运行 | 进程保持运行,不报错 |
| 工具能被列出 | Inspector 或测试脚本 | 工具名称、描述、schema 完整 |
| 单工具调用成功 | Inspector 手动调用 | 返回正确结果 |
| 客户端配置正确 | 重启客户端后查看连接状态 | 显示已连接 |
| 对话中能被模型调用 | 用自然语言提问触发 | 模型正确填写参数并展示结果 |
| 错误路径可见 | 传入非法参数 | 有明确报错或日志 |
清单没有全部通过之前,不要认为“能连上”就是接入完成。连接成功只证明传输层没问题,真正有价值的是工具调用结果是否正确和稳定。
6. 工具不出现、调用超时、远程认证失败,按这条链路排查
6.1 工具没有出现在对话或工具列表里
现象:配置完成、客户端显示已连接,但对话里模型从不调用该工具,工具列表里也看不到。
排查顺序:
- 确认配置文件 JSON 语法正确,没有多余逗号。
- 确认 command 和脚本路径正确,最好用绝对路径。
- 在终端手工执行
python /绝对路径/脚本.py,确认没有语法错误和依赖缺失。 - 用 Inspector 启动同一命令,看能否列出工具。
- 确认模型确实收到了工具列表。部分客户端会缓存工具列表,修改 Server 后需要重连或重启。
如果 Inspector 能列出工具而客户端不能,重点检查客户端的权限开关。Cursor 等编辑器通常要求你在对话中手动允许某个工具执行,未允许之前模型不会调用。
注意:大部分“工具没出现”的问题都不是协议问题,而是配置路径、依赖环境或权限开关问题。先确认基础运行环境,再怀疑协议本身。
6.2 工具调用超时或返回内容异常
现象:模型选择了工具,但几秒后提示超时,或者返回内容与预期不符。
可能原因和检查方式:
- Server 内部调用了外部接口,外部服务响应慢。查看外部依赖的耗时,必要时给工具加超时参数,或把长任务改成异步提交。
- 参数类型不匹配。模型传的是字符串,函数期望整数。应在函数入口做强校验,并让 docstring 写清参数类型含义。
- 工具函数抛了未捕获异常。SDK 会把异常包装成错误返回给客户端,但信息可能不完整。建议在工具函数里捕获异常并返回可读的错误文本。
推荐做法:所有工具函数统一做参数校验,外围调用包 try/except,异常时返回结构化错误信息,既方便模型向用户解释,也方便开发者定位。
6.3 远程 HTTP Server 连接失败或认证失败
现象:配置了url的远程 MCP Server,客户端持续报连接失败,或者提示认证未通过。
排查顺序:
- 先确认地址可达,用 curl 测试服务端地址是否返回预期响应。
- 查看服务端日志,确认请求是否到达。
- 检查认证方式是否为 OAuth。远程 MCP 越来越多采用 OAuth 完成授权,客户端首次连接会拉起授权流程;如果服务端要求 token 而配置里没提供,自然连不上。
- 确认 URL 路径与 Server 暴露的路径一致,不能把 Web 根路径误当成 MCP 端点。
从实际经验看,远程 MCP 的问题大多出在“地址错误”和“认证流程未走完”两类,先把这两类排除,再怀疑协议本身。
6.4 高频踩坑点汇总
| 坑点 | 错误做法 | 推荐做法 |
|---|---|---|
| 路径 | 脚本写相对路径,客户端工作目录不同导致找不到文件 | 使用绝对路径 |
| 工具描述 | docstring 写“函数说明”,模型无法判断调用时机 | 写清功能、参数含义、适用场景 |
| 密钥 | 把数据库密码写进 mcp.json 并提交到版本库 | 使用 env 注入或密钥管理服务 |
| 传输方式 | 本地 Server 却配置 url,远程服务却用 command | 先想清楚部署形态,再选配置字段 |
| 热加载 | 改完配置不重启,反馈“改了没用” | 重启客户端或触发 MCP 重连 |
7. 生产接入建议与扩展方向
7.1 自建 Server 还是直接使用现成 Server
选题决策应该基于需求和成本。社区和官方已经提供了大量现成 MCP Server,覆盖浏览器自动化(Playwright MCP)、设计稿读取(蓝湖、MasterGo、Figma 的 MCP)、数据库访问(MySQL、PostgreSQL 的 MCP)、协同办公和代码托管(GitHub MCP)等场景。这些现成方案接入成本低,适合快速验证,也是热词中“需要自己实现mcp还是用现有的mcp”最直接的答案:先看现成的。
需要自建 MCP Server 的典型情况包括:
- 内部系统没有公开 API,或不想让第三方服务接触核心数据。
- 需要把多个内部接口聚合成一个领域能力,比如“查询客户全景视图”。
- 需要统一控制工具的权限、审计和限流。
- 希望把团队多年沉淀的脚本和命令封装成模型可调用工具。
判断标准可以很简单:现成 Server 能满足 80% 的需求,就用现成的;剩下 20% 的私有逻辑,用一个自建 Server 补齐,不要为了“自建”而自建。
7.2 生产环境的工程化与安全
把 MCP Server 从演示推到生产,至少要考虑以下几件事。
- 认证与授权。远程 Server 要考虑 OAuth 或服务间鉴权,遵循最小权限原则,每个工具只授给它完成任务所需的最小权限。
- 审计日志。记录所有工具调用:谁调的、哪个客户端调的、参数是什么、返回结果、耗时。这是排查问题和满足合规要求的基础。
- 超时与限流。模型对工具耗时的容忍度有限,长任务应该异步化;对高频调用要限流,避免一个用户把平台资源打满。
- 敏感操作确认。对于具备写操作的 Tools,Host 侧应展示确认提示。Server 设计上也要区分只读工具和写工具,不要把所有能力都暴露成一个万能工具。
- 监控与回滚。监控工具调用成功率、延迟和异常率;发布新工具前先灰度,出问题能快速下线。
- 版本管理。SDK 和协议版本会持续演进,升级前先在测试环境验证,不要在生产环境直接升。
参考安全分析场景的社区实践:例如 Ghidra、Burp Suite、Wazuh 等工具社区提供了 MCP Server,把逆向分析、Web 安全检测和日志监控能力暴露给 AI 客户端。这类场景尤其要控制权限边界,确保模型只能访问被授权的分析对象,不能触碰生产数据和敏感凭据。
7.3 扩展方向:多智能体、企业知识库与平台化
MCP 的价值会随接入方数量增长而放大。当前比较明确的扩展方向包括:
- 多智能体协同。多个 Agent 共享一组 MCP Server,避免每个 Agent 重复实现工具接入,同时通过协议统一工具命名和权限,减少智能体之间的能力冲突。
- 企业知识库。把内部文档、表结构说明、运维手册做成 Resources,让模型在回答问题前先读取相关上下文,降低幻觉。
- 浏览器与桌面自动化。Playwright MCP 等方案让模型能完成页面操作、截图、填写表单,适合测试和自动化办公场景。
- 游戏引擎与设计工具。Unity、Unreal、Cocos Creator 以及蓝湖、MasterGo、Figma 的 MCP,让模型能够在设计与开发工具链中直接读取场景、导出资源。
- 平台化网关。团队内统一部署远程 MCP 网关,集中管理认证、路由、日志和限流,各客户端只接网关,不直接暴露内部系统。
对一个人的学习路径来说,最务实的做法是先把最小 Server 跑通,再用 Inspector 观察一次真实调用,然后逐步增加一个接数据库的工具和一个接内部 HTTP 接口的工具,最后再考虑 OAuth 和远程部署。协议本身不复杂,复杂的是工具的边界设计、权限控制和错误处理。把这几件事想清楚,MCP 才能真正成为项目里稳定可靠的一层基础设施,而不是一个“能连上就完事”的演示玩具。