
1. 项目缘起从“大模型应用”到“MCP”的认知跃迁最近在折腾大模型应用开发的朋友估计没少被一个词刷屏——MCP。乍一看这缩写平平无奇但如果你还在用传统的API调用方式吭哧吭哧地写着一堆胶水代码把各种工具、数据源硬塞给大模型那你可能已经落后了半个身位。我第一次接触MCP是在尝试给一个内部知识库问答系统增加实时数据查询能力时。当时的方案是写一个臃肿的后端服务里面塞满了不同数据源的客户端、认证逻辑和结果解析器每次新增一个工具都得改代码、重新部署维护成本高得吓人。直到看到MCP的核心理念才恍然大悟原来工具集成可以如此优雅、标准化。那么MCP到底是什么它不是某个具体的软件或库而是一套协议规范全称是Model Context Protocol。你可以把它理解为大模型如ChatGPT、Claude、本地部署的Llama等与外部世界工具、数据源、系统进行安全、结构化通信的“通用插座”和“说明书”。它的核心价值在于将“大模型能使用什么工具”这个问题的定义权从应用代码中剥离出来变成一个可动态配置、声明式的清单。这意味着开发者无需再为每一个工具编写特定的集成代码只需按照MCP协议“描述”好这个工具任何兼容MCP的服务器Server就能被任何兼容MCP的客户端Client通常是大模型应用框架所发现和使用。举个例子以前你想让大模型能查询数据库你得1. 在应用里写死数据库连接逻辑2. 设计一个提示词模板告诉模型怎么调用3. 处理模型返回的JSON并执行查询4. 把查询结果再塞回给模型。而在MCP的世界里你只需要启动一个实现了MCP协议的“数据库服务器”这个服务器会告诉客户端“嗨我这儿有个工具叫query_database它接受一个sql字符串参数返回查询结果。” 客户端比如一个AI助手应用在运行时自动发现这个服务器和它的工具列表大模型就能直接调用query_database了。工具的增加、删除、更新完全独立于主应用实现了真正的解耦和动态扩展。这套协议最适合两类人一是AI应用开发者尤其是那些正在构建需要复杂工具调用能力的智能体Agent或Copilot类产品的团队二是工具/数据源的提供者他们可以通过实现MCP服务器让自己的服务能无缝接入任何支持MCP的AI生态极大地降低了集成门槛。接下来我们就深入拆解MCP的架构、看看它具体怎么工作并手把手带你从零开始实践。2. MCP核心架构拆解客户端、服务器与资源的三重奏理解MCP关键在于理清它的三个核心角色客户端Client、服务器Server和资源Resources。它们之间的关系有点像电脑Client、USB设备Server和设备提供的功能/文件Resources。2.1 客户端大模型应用的“调度中心”客户端是MCP协议的消费者通常是一个AI应用框架或运行时环境。它的核心职责是发现与连接按照配置去连接一个或多个MCP服务器。连接方式可以是本地进程间通信stdio、HTTP或SSH。获取工具清单连接成功后向服务器请求它提供的所有工具Tools和资源Resources的列表。这相当于获取了一份“服务菜单”。路由调用请求当大模型或用户需要执行某个操作时客户端根据模型输出的结构化请求比如一个包含工具名和参数的JSON找到对应的服务器并将调用请求转发过去。处理返回结果接收服务器执行工具后的返回结果通常是文本或结构化数据并将其整理后返回给大模型进行后续处理或直接呈现给用户。一个常见的客户端例子是Claude Desktop或Cursor IDE中的AI助手。它们内置了MCP客户端可以加载本地的MCP服务器从而让Claude或Cursor内部的AI模型能够使用你自定义的工具比如读取你电脑上的特定文件、执行本地脚本等。2.2 服务器工具能力的“封装器”服务器是MCP协议的生产者是具体工具或数据源的封装。它的核心职责是声明能力在初始化时明确告诉客户端“我提供了哪些工具Tools暴露了哪些资源Resources”。这是通过协议定义的消息来完成的。实现逻辑包含工具调用的实际执行代码。例如一个“天气查询”服务器其get_weather工具的实现里包含了调用第三方天气API、解析返回数据的全部逻辑。执行与返回接收客户端发来的工具调用请求执行内部逻辑并将结果按照协议格式返回给客户端。管理资源对于资源如文件、数据库表视图服务器负责在客户端请求时提供资源的内容或更新通知。服务器的实现非常灵活。它可以是一个简单的Python脚本封装几个本地命令也可以是一个复杂的后端服务连接着企业内部的数据库、CRM系统。关键在于它通过标准的MCP协议与客户端对话隐藏了内部所有的复杂性。2.3 资源与工具能力的不同表现形式这是MCP中非常精妙的设计它区分了两种暴露给大模型的能力形式工具是“主动”的需要模型“思考后决定去调用”。它们代表一个可执行的操作。每个工具都有名称、描述和参数模式通常是一个JSON Schema。例如search_web(query: string): 执行网络搜索。send_email(to: string, subject: string, body: string): 发送邮件。run_calculator(expression: string): 运行计算器。当模型认为需要调用工具时它会输出一个结构化的调用请求客户端负责转发并执行。资源是“被动”的可以被“自动注入”到模型的上下文Context中。它们代表一些静态或动态的内容比如文件、数据库表的结构描述DDL、API文档等。资源有唯一的URI如file:///path/to/notes.md或postgresql://table/schema和MIME类型。资源的核心价值在于上下文管理。客户端可以告诉模型“在你开始回答之前我已经把相关文档资源放在你的上下文窗口里了。” 这比让模型主动去调用一个“读取文件”工具要更高效、更自然。例如你可以配置一个MCP服务器将项目根目录下的README.md和requirements.txt作为资源暴露出来。每当用户询问项目相关问题时这些文件的内容会自动加载到提示词中模型无需额外操作就能获知项目信息。这种“工具资源”的双模式使得MCP既能处理需要模型决策的交互式任务也能高效地提供背景信息大大提升了AI应用的智能性和实用性。3. 实战从零构建你的第一个MCP服务器理论说得再多不如动手一试。我们以最常见的场景为例构建一个MCP服务器让AI助手能够查询你本地系统的信息比如当前时间、磁盘使用情况。我们将使用TypeScript/Node.js环境因为官方提供了完善的SDK生态也最活跃。3.1 环境准备与项目初始化首先确保你的环境已安装 Node.js (版本18或以上) 和 npm。然后我们创建一个新的项目目录并初始化。mkdir my-first-mcp-server cd my-first-mcp-server npm init -y接下来安装MCP的核心依赖包。这里我们使用modelcontextprotocol/sdk它提供了实现服务器和客户端所需的所有类型和工具函数。npm install modelcontextprotocol/sdk同时由于我们要用TypeScript开发安装TypeScript和相关的类型定义。npm install --save-dev typescript types/node npx tsc --init编辑生成的tsconfig.json确保包含基本的配置比如target: ES2022,module: NodeNext, 以及outDir: ./dist。3.2 构建系统信息查询服务器现在我们来创建服务器的核心代码。在项目根目录下创建src/server.ts文件。// src/server.ts import { Server } from modelcontextprotocol/sdk/server/index.js; import { StdioServerTransport } from modelcontextprotocol/sdk/server/stdio.js; import { CallToolRequestSchema, ListToolsRequestSchema, ToolSchema, } from modelcontextprotocol/sdk/types.js; import { exec } from child_process; import { promisify } from util; const execAsync promisify(exec); // 1. 创建Server实例 const server new Server( { name: system-info-server, version: 0.1.0, }, { capabilities: { tools: {}, // 声明我们支持工具 }, } ); // 2. 定义我们的工具 const tools: ToolSchema[] [ { name: get_current_time, description: 获取服务器当前的系统时间和日期。, inputSchema: { type: object, properties: {}, // 这个工具不需要参数 additionalProperties: false, }, }, { name: get_disk_usage, description: 获取指定磁盘路径的使用情况总空间、已用空间、可用空间。, inputSchema: { type: object, properties: { path: { type: string, description: 要查询的磁盘路径例如 / 或 C:\\。默认为当前目录。, default: ., }, }, additionalProperties: false, }, }, ]; // 3. 实现工具处理逻辑 server.setRequestHandler(ListToolsRequestSchema, async () { return { tools, }; }); server.setRequestHandler(CallToolRequestSchema, async (request) { const { name, arguments: args } request.params; try { switch (name) { case get_current_time: { const now new Date(); return { content: [ { type: text, text: 当前系统时间${now.toLocaleString()}, }, ], }; } case get_disk_usage: { const path (args as { path?: string })?.path || .; // 注意此命令在Linux/macOS和Windows上不同此处以Unix-like系统为例 // 实际生产环境需要做平台判断和兼容处理 const { stdout } await execAsync(df -h ${path} | tail -1); const [filesystem, size, used, avail, capacity, mountedOn] stdout.trim().split(/\s/); return { content: [ { type: text, text: 路径 ${path} 的磁盘使用情况\n 文件系统: ${filesystem}\n 总空间: ${size}\n 已用空间: ${used} (${capacity})\n 可用空间: ${avail}\n 挂载点: ${mountedOn}, }, ], }; } default: throw new Error(未知的工具${name}); } } catch (error) { return { content: [ { type: text, text: 执行工具 ${name} 时出错${error instanceof Error ? error.message : String(error)}, }, ], isError: true, }; } }); // 4. 启动服务器使用标准输入输出作为传输层 async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(MCP 系统信息服务器已启动正在通过 stdio 监听...); } main().catch((error) { console.error(服务器启动失败:, error); process.exit(1); });代码解读与注意事项能力声明在创建Server实例时我们在capabilities里声明了tools: {}这告诉客户端本服务器支持工具调用。工具定义tools数组详细定义了每个工具。inputSchema使用了 JSON Schema 来描述参数这对于大模型理解如何调用工具至关重要。description字段要清晰明了这直接作为提示词的一部分给到大模型。请求处理器我们为两种请求设置了处理器。ListToolsRequestSchema请求来时我们直接返回定义好的工具列表。CallToolRequestSchema请求来时我们根据工具名name分派到具体的执行逻辑。执行与返回在执行工具时我们返回的content是一个数组其中包含type为text的对象。这是MCP协议规定的标准返回格式。如果出错设置isError: true可以帮助客户端识别。传输层我们使用了StdioServerTransport这意味着服务器通过标准输入stdin和标准输出stdout与客户端通信。这是本地集成最常见、最简单的方式。安全警告上面的get_disk_usage实现直接执行了 shell 命令 (df)。在真实场景中必须对输入参数path进行严格的验证和清理防止命令注入攻击。例如检查路径是否只包含安全字符或者使用 Node.js 的fs模块的statfs相关函数来替代命令行调用。3.3 编译与运行测试首先编译TypeScript代码npx tsc这会在dist目录下生成编译后的server.js文件。为了便于测试我们在package.json中添加一个启动脚本{ name: my-first-mcp-server, version: 0.1.0, main: dist/server.js, scripts: { build: tsc, start: node dist/server.js }, dependencies: { modelcontextprotocol/sdk: ^0.1.0 }, devDependencies: { typescript: ^5.0.0, types/node: ^20.0.0 } }现在你可以直接运行npm start来启动服务器。你会看到它打印出启动日志然后等待来自标准输入的命令。但目前我们还没有客户端来连接它。3.4 使用MCP Inspector进行本地测试手动编写客户端来测试太麻烦。幸运的是MCP生态提供了一个强大的调试工具MCP Inspector。它是一个图形化界面可以连接MCP服务器浏览其提供的工具和资源并手动发起调用是开发调试的利器。首先全局安装MCP Inspector假设你使用npmnpm install -g modelcontextprotocol/inspector然后在一个终端运行你的服务器npm start在另一个终端启动Inspector并连接到你的服务器mcp-inspector node dist/server.jsInspector会启动一个本地网页通常是http://localhost:5173。打开浏览器你就能看到左侧连接列表显示已连接的服务器system-info-server。点击服务器右侧会展示它提供的所有工具get_current_time,get_disk_usage。你可以点击工具在界面中输入参数对于get_disk_usage可以输入{“path”: “/”}然后点击“Call”按钮。下方会显示工具调用的原始请求、响应以及执行结果。通过Inspector你可以直观地验证你的服务器是否按预期工作工具定义是否清晰参数处理是否正确。这是开发MCP服务器过程中不可或缺的一步。4. 进阶集成将自定义服务器接入AI应用客户端让服务器跑起来只是第一步真正的价值在于让它被AI应用使用。我们以目前支持MCP最成熟的客户端之一——Claude Desktop为例演示如何集成。4.1 配置Claude Desktop加载本地MCP服务器Claude Desktop允许通过配置文件来加载本地开发的MCP服务器。配置文件通常位于macOS:~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:%APPDATA%\Claude\claude_desktop_config.jsonLinux:~/.config/Claude/claude_desktop_config.json如果文件不存在可以手动创建。我们需要在配置文件中添加一个mcpServers字段。以下是一个配置示例它同时配置了我们刚写的系统信息服务器和一个假设的“笔记”服务器。{ mcpServers: { system-info: { command: node, args: [/ABSOLUTE/PATH/TO/YOUR/PROJECT/dist/server.js], env: {} }, my-notes: { command: python, args: [/ABSOLUTE/PATH/TO/notes_server/notes.py] } } }关键配置解析与避坑指南绝对路径args中的路径必须使用绝对路径。使用相对路径或~家目录符号会导致Claude Desktop启动服务器失败。这是新手最容易踩的坑。命令查找command可以是系统PATH中的任何命令如node、python、bash等。确保你指定的命令在Claude Desktop的运行环境中可用通常就是你的系统环境。环境变量env对象可以用于设置服务器进程的环境变量。如果你的服务器需要访问特定的API密钥或配置文件路径可以在这里设置例如env”: { “MY_API_KEY”: “your_key_here” }。重启生效修改配置文件后必须完全退出并重启Claude Desktop应用配置才会被加载。4.2 在Claude Desktop中验证与使用重启Claude Desktop后你可以通过以下方式验证集成是否成功打开Claude Desktop新建一个对话。在输入框里尝试直接问“我当前的系统时间是什么” 或者 “我的根目录磁盘还剩下多少空间”如果配置正确Claude的回复中会显示它正在“思考”然后可能会显示它调用了get_current_time或get_disk_usage工具并给出工具返回的结果。一个更可靠的测试方法是查看Claude Desktop的日志macOS: 在终端运行tail -f ~/Library/Logs/Claude/ClaudeDesktop.log查看日志中是否有类似“Connected to MCP server ‘system-info’ at …”的信息或者是否有运行你服务器命令 (node …) 的日志。如果看到错误比如“Failed to start MCP server…”通常就是路径错误或命令执行失败。成功集成后你的自定义工具就成为了Claude AI能力的一部分。你可以像使用内置功能一样通过自然语言让Claude调用这些工具。这种体验是革命性的——你不再需要切换应用或复制粘贴信息AI助手真正成为了你数字工作流的中心枢纽。4.3 其他客户端的集成思路除了Claude Desktop其他支持MCP或正在积极集成的客户端/框架包括Cursor IDE: 在其Agent设置中可以配置MCP服务器让Cursor内部的AI能使用你的工具。Continue.dev: 一个开源的VS Code插件也支持MCP可以让你在编码时使用自定义工具。自建AI应用如果你在使用LangChain、LlamaIndex或Semantic Kernel等框架构建自己的AI应用可以寻找或开发对应的MCP客户端库将MCP服务器作为工具来源动态加载进去。这为构建可扩展的企业级AI Agent平台提供了标准化的基础。5. 生产级MCP服务器开发要点与最佳实践构建一个用于演示的玩具服务器相对简单但要构建一个稳定、安全、可用于生产环境的MCP服务器需要考虑更多因素。5.1 错误处理与健壮性上面的示例代码只有最基础的错误捕获。在生产环境中错误处理需要更细致参数验证在工具执行逻辑的最开始严格验证输入参数是否符合预期类型、范围、格式。可以使用zod或joi等库进行验证。超时控制为每个工具调用设置超时。如果一个工具如网络请求挂起会阻塞整个MCP会话。在CallToolRequestSchema的处理器中可以用Promise.race实现超时控制。优雅降级对于依赖外部服务的工具如查询数据库、调用API要有降级策略。例如当主要API不可用时是否有一个备用的数据源或者至少返回一个友好的错误信息而不是让服务器崩溃。状态管理MCP服务器理论上应该是无状态的。但某些工具可能需要维持会话例如一个需要登录的爬虫工具。这时需要谨慎管理内存中的状态并考虑设置会话过期时间。5.2 安全性考量MCP服务器本质上是将你系统的某些能力暴露给了AI模型安全是重中之重。最小权限原则服务器进程应该以尽可能低的权限运行。不要用root或管理员权限去运行一个只是查询日志的MCP服务器。输入净化Sanitization对所有来自客户端的输入尤其是工具参数进行净化处理防止注入攻击SQL注入、命令注入、路径遍历等。永远不要像我们示例中那样将用户输入直接拼接进shell命令。访问控制不是所有工具都应该对所有用户或所有对话开放。可以在服务器层面实现简单的认证例如通过环境变量传递令牌并在初始化时验证或者在工具逻辑内部检查上下文但这需要客户端支持传递用户身份信息目前协议支持有限。审计日志记录所有工具调用的详细信息谁会话ID、什么时候、调用了什么工具、参数是什么、结果是什么。这对于调试、监控和合规性检查至关重要。网络隔离如果服务器需要访问内部网络资源确保其网络访问范围受到严格控制避免成为跳板。5.3 性能与可观测性资源使用监控服务器的内存和CPU使用情况。一个设计不良的工具如读取超大文件可能会耗尽服务器资源。异步处理对于耗时的操作如大量数据处理、网络I/O务必使用异步模式避免阻塞事件循环。指标暴露考虑使用OpenTelemetry等工具为服务器添加指标Metrics、追踪Traces和日志Logs方便集成到现有的可观测性体系中。连接管理一个MCP服务器实例可能同时服务多个客户端连接。确保你的代码是线程安全/并发安全的特别是在有状态的情况下。5.4 设计优雅的工具与资源工具描述Description是黄金大模型完全依赖你的描述来理解工具用途。描述要清晰、具体最好包含一两个使用示例。例如“search_internal_wiki: 在公司内部知识库中搜索文档。参数keywords: 搜索关键词字符串类型。”合理的参数设计参数不宜过多过杂。如果某个工具功能复杂考虑拆分成多个更专注的工具。使用enum类型来限定可选值使用default提供合理的默认值。资源的有效利用不要一股脑把所有文件都作为资源暴露。思考哪些信息是模型在对话前就需要知道的“背景信息”。例如暴露一个project_goals.md资源比暴露整个源代码目录更有效。对于动态资源如数据库中最新的10条日志可以实现资源的subscribe和notify机制让客户端能收到更新通知。版本化当你的工具更新了参数或行为时考虑通过工具名或资源URI进行版本化如search_v2以免破坏现有客户端的兼容性。6. MCP生态现状、局限与未来展望MCP协议由Anthropic公司牵头推出时间不长但发展迅猛因为它切中了AI应用开发中的一个核心痛点——工具集成的标准化。6.1 当前生态与工具集目前MCP生态已经涌现出不少优秀的服务器实现覆盖了常见场景文件系统读写本地文件、目录列表。这是最基础也是最实用的服务器之一。源代码仓库与Git集成可以执行git status,git log, 甚至根据diff生成提交信息。数据库连接PostgreSQL、MySQL、SQLite等执行安全的查询通常只读将查询结果或表结构作为资源或工具结果返回。网络搜索连接Serper、Exa等搜索API让模型能获取实时信息。日历与邮件读取Google Calendar事件发送邮件。企业内部系统这是MCP潜力最大的地方许多公司开始开发内部MCP服务器将CRM、ERP、工单系统等接入AI助手。社区也出现了像MCP Registry这样的想法类似于npm或PyPI用于发现和分享他人开发的MCP服务器。6.2 当前协议的限制与挑战尽管前景光明MCP目前仍处于早期阶段存在一些限制协议仍在演进MCP协议本身还在快速迭代中这意味着不同版本之间可能存在不兼容。开发时需要关注所用SDK和客户端支持的协议版本。身份验证与多租户支持薄弱协议层面对复杂的身份验证如OAuth、权限控制和多用户隔离的支持还比较初级。在企业级多用户场景下需要自己在服务器逻辑或上层网关中实现。复杂交互模式支持有限目前的工具调用是“一问一答”式的。对于需要多轮交互的复杂操作例如引导用户完成一个多步骤的配置缺乏标准的支持。这通常需要结合AI应用自身的对话逻辑来处理。客户端支持不均虽然Claude Desktop支持良好但其他客户端如一些IDE插件的支持可能不完整或存在bug。生态的完善需要时间。6.3 未来可能的发展方向从我个人的观察和项目实践来看MCP可能会朝以下几个方向发展更丰富的资源类型除了文本未来可能支持图像、音频等多媒体资源作为上下文让多模态模型能直接“看到”或“听到”资源内容。双向流式通信支持服务器主动向客户端推送信息例如监控告警自动触发AI通知而不仅仅是响应请求。标准化工具组合与工作流定义更高级的抽象让多个工具可以按特定顺序和逻辑组合成一个“工作流”暴露给模型降低模型的规划负担。更强的安全与审计框架协议层面可能会引入更细粒度的权限模型、操作签名和不可否认的审计日志以满足企业级的安全合规要求。给开发者的建议现在无疑是开始学习和实验MCP的好时机。即使你目前的需求用传统API集成也能解决尝试用MCP的思路重构一下你会对“AI原生应用”的架构有更深的理解。可以从封装一个你最常用的小工具开始比如一个查询服务器状态的工具或者一个管理本地待办事项的工具。这个过程本身就是一次宝贵的架构思维训练。