ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

MCP协议与Agent Plugins:构建统一AI插件标准的实战指南

MCP协议与Agent Plugins:构建统一AI插件标准的实战指南 最近在尝试为AI助手扩展本地文件读写、数据库查询等能力时发现不同AI平台如Claude Desktop、Cursor、Dify的插件生态各自为战开发一个功能往往需要为每个平台重复适配费时费力。直到看到Anthropic联合多家公司推出的Model Context Protocol (MCP)和相关的Agent Plugins标准才意识到一个统一的“AI代理插件标准”正在形成这有望彻底改变AI工具生态的碎片化现状。本文将从开发者的角度深入解析MCP协议和Agent Plugins标准手把手教你如何从零开始构建一个符合标准的MCP Server插件并集成到主流的AI平台中。无论你是想为团队内部AI工具增加自定义能力还是希望将自己的服务开放给更广泛的AI生态这篇文章都能提供一套完整的实战方案。1. 背景与核心概念为什么需要统一的AI插件标准在深入代码之前我们首先要理解当前AI应用生态面临的核心问题以及MCP试图解决的痛点。1.1 AI应用生态的现状与挑战目前大型语言模型LLM本身并不具备直接操作外部系统如数据库、文件系统、API的能力。为了让AI助手能“动手做事”开发者通常采用以下几种方式Function Calling函数调用开发者预定义一组函数工具及其描述AI模型在对话中决定何时调用哪个函数并生成符合要求的参数。这是目前最常见的方式。平台特定插件Platform-specific Plugins如ChatGPT Plugins、Claude Desktop自定义功能等。开发者需要遵循特定平台的开发规范插件通常无法跨平台使用。自定义后端集成在应用后端硬编码一系列工具通过API暴露给前端AI界面。这些方式带来了显著的挑战开发碎片化为Claude、Cursor、Dify等不同平台开发相同功能的插件需要学习多套SDK和规范。部署复杂插件可能需要伴随主应用发布更新和运维成本高。能力边界模糊工具Tools、技能Skills、插件Plugins等概念在不同平台定义不一容易混淆。1.2 MCP (Model Context Protocol) 是什么Model Context Protocol (MCP)是一个开放协议旨在为LLM提供一个标准化的方式来发现、调用外部工具和资源。你可以把它想象成AI世界的“USB协议”或“驱动程序模型”。它的核心思想是解耦MCP Server服务器提供实际能力的后端服务。例如一个提供“读写本地文件”能力的服务或一个“查询数据库”的服务。它独立于任何特定的AI客户端运行。MCP Client客户端集成在AI应用如Claude Desktop、Cursor中的组件。它负责发现、连接MCP Server并将Server提供的工具和资源“暴露”给内部的LLM使用。标准通信协议Server和Client通过基于JSON-RPC的标准化协议进行通信定义了工具调用、资源访问等核心操作。这样一来开发者只需编写一次MCP Server任何支持MCP协议的AI客户端都能无缝使用其提供的功能。1.3 Agent Plugins 与 plugin.json“Agent Plugins”可以看作是MCP生态中对“插件”的一种具体实现和分发形式的约定。它让插件的安装、配置和管理变得更简单。一个Agent Plugin的核心是一个包含plugin.json清单文件的包。这个文件描述了插件的基本信息、所需的MCP Server配置以及如何启动它。关键概念辨析Tool工具一个可供AI调用的具体操作如read_file,query_database。这是MCP协议层定义的概念。Skill技能在一些AI平台语境下指一组相关工具的集合或一种高阶能力概念相对模糊。Plugin插件在MCP/Agent Plugins语境下特指一个封装好的、包含plugin.json和MCP Server实现的可安装包。一个Plugin可以包含多个Tool。简单来说MCP是底层通信协议Agent Plugins是基于该协议的上层封装和分发标准。开发一个Plugin本质就是开发一个MCP Server并为其编写一个plugin.json清单。2. 环境准备与版本说明我们将使用Node.js环境来开发一个MCP Server示例因为它有官方完善的SDK支持且跨平台性好。基础环境要求操作系统Windows 10/11, macOS, 或 Linux (如Ubuntu 20.04)Node.js版本 18 或更高。推荐使用LTS版本如20.x。包管理器npm 或 yarn。代码编辑器VS Code推荐或其他任何编辑器。AI客户端用于测试我们将使用Claude Desktop作为测试客户端。请确保已安装最新版本。验证环境打开终端或PowerShell、CMD运行以下命令检查环境node --version npm --version3. 核心原理与MCP协议拆解在动手编码前理解MCP协议的核心组件和通信流程至关重要。3.1 MCP 的核心组件Server服务器作用能力的提供者。它向Client宣告自己具备哪些“工具”Tools和“资源”Resources。工具Tools可供调用的函数有输入参数和输出结果。例如read_file,search_web。资源Resources可供读取的静态或动态内容以URI标识。例如file:///path/to/doc.md或dynamic://news/latest。Client可以“读取”资源内容并将其作为上下文提供给LLM。实现一个长期运行的程序通过stdio或HTTP与Client通信。Client客户端作用能力的消费者。集成在AI应用中负责管理Server连接将Server提供的工具和资源列表提供给LLM并转发LLM的调用请求。实现AI应用如Claude Desktop内部实现。Transport传输层作用定义Server与Client如何通信。MCP主要支持两种方式stdio标准输入输出最常见Client启动Server子进程通过管道通信。适合本地插件。HTTP/SSE通过网络通信适合远程服务。3.2 通信流程概览一个典型的工具调用流程如下初始化Client启动Server交换初始化信息Server宣告其能力列表工具和资源。列出工具Client向LLM展示可用的工具列表。用户请求用户向AI提出涉及外部能力的请求如“请总结我桌面上的report.txt文件”。LLM决策LLM判断需要调用哪个工具read_file并生成调用参数{“path”: “~/Desktop/report.txt”}。调用工具Client通过MCP协议向Server发送tools/call请求。执行并返回Server执行实际操作读取文件将结果或错误通过tools/call响应返回给Client。结果交付Client将结果提供给LLMLLM生成最终回答给用户。整个过程中LLM只关心“有什么工具”和“调用结果”完全无需感知Server的具体实现和技术栈。4. 完整实战开发你的第一个MCP Server文件阅读插件现在我们来实现一个简单的MCP Server它提供一个read_file工具允许AI读取指定路径的文本文件内容。4.1 创建项目并初始化首先创建一个新的项目目录并初始化Node.js项目。# 创建项目目录并进入 mkdir mcp-file-reader cd mcp-file-reader # 初始化npm项目使用默认配置即可 npm init -y4.2 安装依赖我们需要安装官方的modelcontextprotocol/sdk来简化MCP Server的开发。npm install modelcontextprotocol/sdk4.3 编写MCP Server核心代码创建主文件server.js。// server.js - MCP Server 主文件 const { Server } require(modelcontextprotocol/sdk/server/index.js); const { StdioServerTransport } require(modelcontextprotocol/sdk/server/stdio.js); const fs require(fs).promises; const path require(path); // 1. 创建Server实例并指定名称和版本 const server new Server( { name: file-reader-mcp-server, version: 1.0.0, }, { // Server的能力描述 capabilities: { tools: {}, // 声明我们提供工具 resources: {}, // 本例不提供资源留空 }, } ); // 2. 定义工具 (Tool): read_file // 此工具允许AI读取文本文件内容 server.setRequestHandler(tools/list, async () { return { tools: [ { name: read_file, description: 读取指定路径的文本文件内容。请确保路径正确且文件可读。, inputSchema: { type: object, properties: { path: { type: string, description: 要读取的文件的绝对路径或相对于用户主目录的路径。, }, }, required: [path], }, }, ], }; }); // 3. 处理工具调用 (Tool Call) server.setRequestHandler(tools/call, async (request) { // 确保调用的是我们定义的工具 if (request.params.name ! read_file) { throw new Error(未知工具: ${request.params.name}); } const filePath request.params.arguments?.path; if (!filePath) { throw new Error(缺少必要参数: path); } try { // 解析路径这里简单处理实际产品需考虑安全性和路径解析 let resolvedPath filePath; if (filePath.startsWith(~/)) { const homeDir process.env.HOME || process.env.USERPROFILE; resolvedPath path.join(homeDir, filePath.slice(2)); } // 读取文件内容 const content await fs.readFile(resolvedPath, utf-8); // 返回成功结果 return { content: [ { type: text, text: 文件 ${filePath} 的内容如下\n\\\\n${content}\n\\\, }, ], }; } catch (error) { // 返回错误信息 return { content: [ { type: text, text: 读取文件失败: ${error.message}, }, ], isError: true, }; } }); // 4. 启动Server使用stdio传输方式 async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(MCP File Reader Server 已启动正在通过 stdio 通信...); } main().catch((error) { console.error(Server 启动失败:, error); process.exit(1); });代码关键点解析Server实例创建时需定义服务器名称和版本以及声明capabilities本例声明了tools能力。工具列表 (tools/list)当Client查询时返回一个工具描述数组。每个工具需要定义name、description和inputSchema输入参数的JSON Schema。清晰的描述有助于LLM理解工具用途。工具调用 (tools/call)这是核心处理逻辑。根据工具名和参数执行实际操作此处是fs.readFile。必须返回固定格式的结果包含content数组。isError: true表示调用失败。传输层 (StdioServerTransport)使用标准输入输出与Client通信这是本地插件最常用的方式。错误处理用try...catch包裹核心逻辑确保Server不会因单个调用失败而崩溃并将错误信息友好地返回给Client。4.4 创建Plugin清单文件 (plugin.json)为了让Claude Desktop等客户端能自动识别和安装我们的插件我们需要在项目根目录创建plugin.json。{ schema_version: v1, name: file-reader, display_name: 本地文件阅读器, description: 一个安全的MCP插件允许AI助手读取用户指定的本地文本文件内容。, version: 1.0.0, author: Your Name, license: MIT, repository: https://github.com/yourusername/mcp-file-reader, contact_email: your.emailexample.com, mcp_server: { command: node, args: [/ABSOLUTE/PATH/TO/YOUR/mcp-file-reader/server.js], env: {} }, capabilities: { tools: [read_file] } }重要配置说明mcp_server.command启动Server的命令。这里是node。mcp_server.args命令的参数。你必须将路径替换为你本地server.js文件的绝对路径。例如[/Users/username/projects/mcp-file-reader/server.js]。capabilities.tools声明此插件提供的工具列表需与Server代码中定义的名称一致。4.5 在Claude Desktop中配置并测试插件找到Claude Desktop配置目录macOS:~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:%APPDATA%\Claude\claude_desktop_config.jsonLinux:~/.config/Claude/claude_desktop_config.json编辑配置文件如果文件不存在就创建它。添加mcpServers配置项指向我们的plugin.json。// claude_desktop_config.json { mcpServers: { file-reader: { command: node, args: [/ABSOLUTE/PATH/TO/YOUR/mcp-file-reader/server.js] } } }注意Claude Desktop也支持直接指向plugin.json文件但上述直接配置Server的方式更直接。你也可以将plugin.json放在Claude能扫描的特定目录。重启Claude Desktop完全退出并重新启动Claude Desktop应用。进行测试打开Claude Desktop新建对话。你应该能在输入框上方或工具列表中看到可用的工具可能显示为“文件阅读器”或read_file。尝试提问“请帮我读取桌面上的notes.txt文件内容。” 或 “使用 read_file 工具查看/etc/hosts文件。”Claude会识别你的意图自动调用read_file工具并将文件内容作为上下文返回然后给出总结或回答。5. 进阶实战构建更复杂的数据库查询MCP Server仅读取文件还不够。让我们构建一个更实用的插件一个可以查询SQLite数据库的MCP Server。这将涉及更复杂的参数处理和异步操作。5.1 创建新项目并安装依赖mkdir mcp-sqlite-query cd mcp-sqlite-query npm init -y npm install modelcontextprotocol/sdk sqlite35.2 准备示例数据库创建一个简单的SQLite数据库文件example.db并插入一些数据。你可以使用sqlite3命令行工具或下面的Node.js脚本init-db.js// init-db.js const sqlite3 require(sqlite3).verbose(); const db new sqlite3.Database(./example.db); db.serialize(() { // 创建用户表 db.run(CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, age INTEGER )); // 插入示例数据 const stmt db.prepare(INSERT OR IGNORE INTO users (name, email, age) VALUES (?, ?, ?)); stmt.run(张三, zhangsanexample.com, 28); stmt.run(李四, lisiexample.com, 35); stmt.run(王五, wangwuexample.com, 22); stmt.finalize(); console.log(示例数据库已初始化完成。); }); db.close();运行node init-db.js来创建数据库。5.3 编写SQLite查询MCP Server创建server.js文件// server.js - SQLite 查询 MCP Server const { Server } require(modelcontextprotocol/sdk/server/index.js); const { StdioServerTransport } require(modelcontextprotocol/sdk/server/stdio.js); const sqlite3 require(sqlite3).verbose(); const path require(path); // 数据库文件路径可配置 const DB_PATH path.resolve(__dirname, ./example.db); const server new Server( { name: sqlite-query-mcp-server, version: 1.0.0, }, { capabilities: { tools: {}, }, } ); // 定义工具query_sqlite server.setRequestHandler(tools/list, async () { return { tools: [ { name: query_sqlite, description: 对指定的SQLite数据库执行安全的SELECT查询语句并返回结果。仅支持查询操作。, inputSchema: { type: object, properties: { sql: { type: string, description: 要执行的SELECT SQL查询语句。请确保语句是只读的。, }, // 未来可扩展参数如指定不同的db文件路径 }, required: [sql], }, }, ], }; }); server.setRequestHandler(tools/call, async (request) { if (request.params.name ! query_sqlite) { throw new Error(未知工具: ${request.params.name}); } const sql request.params.arguments?.sql; if (!sql) { throw new Error(缺少必要参数: sql); } // 基础安全校验只允许SELECT查询 const trimmedSql sql.trim().toLowerCase(); if (!trimmedSql.startsWith(select)) { return { content: [ { type: text, text: 出于安全考虑本工具仅支持SELECT查询语句。, }, ], isError: true, }; } return new Promise((resolve, reject) { const db new sqlite3.Database(DB_PATH, sqlite3.OPEN_READONLY, (err) { if (err) { resolve({ content: [{ type: text, text: 无法连接数据库: ${err.message} }], isError: true, }); return; } db.all(sql, [], (err, rows) { db.close(); // 记得关闭连接 if (err) { resolve({ content: [{ type: text, text: SQL执行错误: ${err.message} }], isError: true, }); return; } if (rows.length 0) { resolve({ content: [{ type: text, text: 查询成功但未找到匹配的数据。 }], }); return; } // 将结果格式化为易读的表格文本 const headers Object.keys(rows[0]); const tableRows rows.map(row headers.map(h row[h]).join( | )); const tableHeader headers.join( | ); const separator headers.map(() ---).join( | ); const table [| ${tableHeader} |, | ${separator} |, ...tableRows.map(r | ${r} |)].join(\n); resolve({ content: [ { type: text, text: 查询成功返回 ${rows.length} 条记录\n\n${table}, }, ], }); }); }); }); }); async function main() { const transport new StdioServerTransport(); await server.connect(transport); console.error(MCP SQLite Query Server 已启动。); } main().catch(console.error);5.4 创建对应的plugin.json并配置参照第一个示例创建plugin.json并更新Claude Desktop的配置文件添加新的Server配置。// 在 claude_desktop_config.json 中添加 { mcpServers: { file-reader: { ... }, sqlite-query: { command: node, args: [/ABSOLUTE/PATH/TO/mcp-sqlite-query/server.js] } } }重启Claude Desktop后你就可以提问“查询用户表中所有年龄大于25岁的人”或“使用query_sqlite工具查看users表的结构”。AI会生成相应的SQL并调用工具将查询结果返回给你。6. 常见问题与排查思路 (FAQ)在开发和集成MCP插件过程中你可能会遇到以下问题问题现象可能原因排查步骤与解决方案Claude Desktop 启动后看不到新工具1. 配置文件路径或格式错误。2. plugin.json中Server路径错误。3. Server程序启动失败。1. 检查claude_desktop_config.json的语法可用JSON验证器。2. 确认args中的Node.js路径和脚本绝对路径正确。3. 在终端手动运行node /path/to/server.js看是否有错误输出。AI无法正确调用工具或调用后无反应1. 工具描述(description)不清晰LLM无法理解。2. 输入参数Schema定义有误LLM生成的参数格式不对。3. Server端处理请求时崩溃。1. 优化工具描述明确用途、输入和限制。2. 检查inputSchema确保其是有效的JSON Schema且required字段正确。3. 查看Server运行日志如果配置了日志或使用调试模式运行Server。工具调用返回权限错误 (如读取文件失败)1. Server进程权限不足。2. 路径解析错误如~未展开。3. 文件不存在。1. 确保Server有访问目标资源的权限。2. 在Server代码中完善路径解析逻辑处理用户主目录(~)。3. 在返回错误时提供更详细的信息。Server启动立即退出1. Node.js依赖未安装。2. 代码中存在语法错误。3. 端口冲突或传输层配置错误。1. 运行npm install确保依赖完整。2. 使用node -c server.js检查语法。3. 确认使用的是正确的Transport本地插件多用StdioServerTransport。如何调试MCP通信过程默认通信不可见。1. 在Server代码中使用console.error()输出调试信息stderr通常会被Client捕获并记录。2. 查阅Client如Claude Desktop的日志文件。3. 考虑使用MCP协议的调试工具或中间代理。7. 最佳实践与工程建议将MCP Server投入生产环境或团队共享时遵循以下最佳实践能提升安全性、可靠性和可维护性。7.1 安全性是第一要务MCP插件让AI获得了执行代码的能力安全风险显著增加。最小权限原则Server进程应以最低必要权限运行。不要用root或管理员权限。输入验证与消毒对所有来自Client的输入如文件路径、SQL语句进行严格验证和消毒。防止路径遍历../、命令注入等攻击。// 示例简单的路径安全校验 function isSafePath(userPath) { const resolved path.resolve(safeBaseDir, userPath); return resolved.startsWith(safeBaseDir); // 确保路径在允许的基目录下 }操作限制明确限制工具的能力。例如数据库工具只允许SELECT文件工具禁止写入系统目录。环境隔离考虑在容器如Docker或沙箱中运行不受信任的MCP Server。7.2 提升插件用户体验清晰的工具描述description和参数描述要足够详细、自然帮助LLM准确理解工具用途和使用场景。结构化的输出尽量返回结构清晰、易于LLM理解和呈现的结果。例如数据库结果可以格式化为Markdown表格列表数据可以格式化为JSON。友好的错误信息错误信息应能指导用户或AI下一步该怎么做而不仅仅是技术堆栈。例如“未找到文件请检查路径是否正确”比“ENOENT: no such file or directory”更好。7.3 工程化与部署配置化将数据库路径、API密钥、允许的目录等配置通过环境变量或配置文件管理而不是硬编码在代码中。日志记录为Server添加日志功能记录工具调用、参数、结果和错误便于监控和调试。健康检查实现一个简单的健康检查端点或机制方便运维。版本管理在plugin.json中清晰定义版本号遵循语义化版本规范便于用户升级。打包与分发对于复杂插件可以考虑打包为Docker镜像或提供一键安装脚本。plugin.json中的repository字段应指向你的代码仓库。7.4 与其他AI平台集成除了Claude Desktop其他平台也在积极集成MCP或类似标准。Cursor最新版本已支持MCP。配置方式类似通常也是在设置文件中指定MCP Server。Dify作为AI应用开发平台可以通过其“工具”或“插件”功能集成MCP Server通常需要将其封装为一个自定义工具API。自定义客户端你可以使用MCP Client SDK为自己开发的AI应用添加插件支持从而复用海量的MCP生态插件。开发一个稳定、安全、易用的MCP插件不仅能极大扩展AI助手的能力边界也能让你的服务无缝接入快速发展的AI Agent生态。从简单的文件阅读器开始逐步扩展到数据库、内部API、云服务你将构建起属于自己或团队的AI能力中间层。
返回列表