概览
本文将会介绍一些 mcp 的基本前置知识,并手摸手带大家完成一个 **具有 github 仓库搜索功能的 mcp 工具 **的小demo,借此和大家介绍一遍 mcp工具的开发流程!
什么是MCP
MCP(Model Context Protocol)是 Anthropic 提出并开源的一种协议,旨在让 基于 LLM 的 AI 系统具备使用工具和访问资源的能力。简单来说,MCP 为大型语言模型和外部资源之间提供了一个统一的“插头”或接口。以前每当需要让 AI 连接新的数据库、文件系统或服务时,开发者都必须为每种资源写专门的对接代码;有了 MCP,基于LLM 的 AI 系统可以通过标准协议“即插即用”地访问各种数据源,再也不必困在“信息孤岛”中。
暂时无法在飞书文档外展示此内容
工作原理
暂时无法在飞书文档外展示此内容
初始化连接:客户端向服务器发送连接请求,建立通信通道。
发送请求:客户端根据需求构建请求消息,并发送给服务器。
处理请求:服务器接收到请求后,解析请求内容,执行相应的操作(如查询数据库、读取文件等)。
返回结果:服务器将处理结果封装成响应消息,发送回客户端。
断开连接:任务完成后,客户端可以主动关闭连接或等待服务器超时关闭。
数据安全性
暂时无法在飞书文档外展示此内容
从上面这个结构图可以看出,MCP 服务器可以自己控制资源,不需要将 API 密钥等敏感信息提供给 LLM 提供商。这样也可以在使用ai工具的过程中很大程度上降低敏感数据泄露的风险。
什么是大语言模型中的 Function Calling
在介绍过mcp的定义中,大家肯定会想起我们的另外一个老朋友Function Calling,那么他和 mcp 是什么关系呢?
首先我们先回顾一下 Function Calling的定义
Function Calling 是一种让大型语言模型(LLM)调用外部函数或API的功能。它不是直接执行函数,而是通过返回一个结构化的输出(比如JSON)来告诉应用程序应该调用哪个函数以及使用什么参数。然后,应用程序会执行这个函数并把结果返回给模型。所以,LLM就像一个规划者,它决定是否、何时、如何使用某个函数,而实际的执行则由你的代码来完成。
举个 GPT-4 的API工作流程的例子
暂时无法在飞书文档外展示此内容
1、定义可用的函数:开发者在提示或 API 调用中向模型提供一个函数定义的列表(名称、描述和预期参数)
2、模型选择函数:用户提出问题,模型处理查询以及函数定义。如果它决定需要使用某个函数,则会以 JSON 格式返回一个 Function Calling(包括函数名和参数),而不是正常答案。例如,如果用户询问天气,模型可能会输出一个调用 get_current_weather 的函数,参数为位置。
3、执行函数:应用程序解析模型的 JSON 输出,识别要调用的函数,然后使用提供的参数在代码中调用相应的函数。它收集函数的结果(例如,从 API 获取的实际天气数据)。
4、将结果返回给模型:函数的输出被反馈回模型,通常作为对话中的一条特殊消息,或通过另一个调用模型的请求的结果。然后模型可以使用这些数据来形成其最终回答。
5、如果任务需要多个 Function Calling,它可能会在后续步骤中重复执行3、4两步。这个循环会持续,直到模型为用户产生最终答案。
MCP 与 Function Calling 的区别
类别 | MCP | Function Calling |
---|---|---|
性质 | 协议 | 功能 |
实现 | 基于标准协议 | 依赖于特定的模型 |
交互的灵活性 | 引入了分层上下文的概念(通过资源、提示等),使 AI 能够与涉及工具的更复杂的对话进行交互 | 提供了结构化但相对刚性的接口(模型的动作局限于事先定义的一组固定函数) |
适应性和工具发现 | AI Agent可以在运行时动态查询每个服务器可用的工具和资源 | 模型提前被告知哪些函数存在,不太适合一些用户需要创造性的组合步骤,或超出了预定义函数的场景。 |
可扩展性和生态系统 | 只需一次实现 MCP 服务器,而任何兼容的 AI 客户端都可以使用它们。 | 当有 n 个不同的工具时,你需要定义 n 个函数并保持更新。当切换 不同的 LLM 供应商的时候,可能需要重新定义这些函数。 |
具体可以参考文章 https://geekpm.com/archives/mcp-fuctioncalling
如何开发一个mcp工具
接下来本文将以一个 github 仓库搜索工具为例,从 0 - 1 手摸手教程❤️
项目初始化
npx @modelcontextprotocol/create-server github_mcp
? What is the name of your MCP server? github_mcp
? What is the description of your server? A Model Context Protocol server
? Would you like to install this server for Claude.app? No
✔ MCP server created successfully!
Next steps:
cd github_mcp
npm install
npm run build # or: npm run watch
npm link # optional, to make available globally
可以看到我们的项目结构已经初始化好了
功能编写
#!/usr/bin/env node
/**
* index.ts
**/
import * as repository from "./search_repositories.js";
// 创建MCP服务器实例
const server = new Server(
{
name: "github_mcp",
version: "0.1.0",
},
{
capabilities: {
resources: {},
tools: {},
prompts: {},
},
}
);
// 定义工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "search_repositories",
description: "Search for GitHub repositories",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "搜索关键词",
},
page: {
type: "number",
optional: true,
description: "页码 (默认: 1)",
},
size: {
type: "number",
optional: true,
description: "每页的条数 (默认: 30, 最大: 100)",
},
},
required: ["query"],
},
}
],
};
});
//使用对应的工具并返回数据
server.setRequestHandler(CallToolRequestSchema, async request => {
if (!request.params.arguments) {
throw new Error("Arguments are required");
}
switch (request.params.name) {
case "search_repositories": {
const args = repository.SearchRepositoriesSchema.parse(request.params.arguments);
const results = await repository.searchRepositories(
args.query,
args.page,
args.size
);
return {
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
};
}
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}
});
// 使用stdio传输启动服务器。这允许服务器通过标准输入/输出流进行通信
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("GitHub MCP Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
/**
* search_repositories.ts
**/
export const SearchRepositoriesSchema = z.object({
query: z.string().describe("搜索关键词"),
page: z.number().optional().describe("页码 (默认: 1)"),
size: z.number().optional().describe("每页的条数 (默认: 30, 最大: 100)"),
});
export async function searchRepositories(
query: string,
page: number = 1,
size: number = 30
) {
const url = new URL("https://api.github.com/search/repositories");
url.searchParams.append("q", query);
url.searchParams.append("page", page.toString());
url.searchParams.append("size", size.toString());
const response = await fetch(url.toString());
const data = await response.json();
return data;
}
调试
cd github_mcp
pnpm i
pnpm run build
pnpm run inspector
成功运行之后我们访问 http://localhost:5173 就会看到一个调试界面,按步骤点击就可以开始调试啦。
我们用 react 作为关键词搜索看看
工具链路成功走通了。
ps: 这里有一点地方要注意,@modelcontextprotocol/inspector 这个调试工具 默认的超时时间是 10s,如果超过10s会提示超时如下图
博主在 @ent-platform/inspector 中将默认超时时间改成了 80s,大家可以直接使用(有什么问题或者好玩的想法的话也可以联系博主,欢迎互相交流学习❤️!)使用姿势如下
只需要把 @modelcontextprotocol/inspector 替换为 @ent-platform/inspector 就好了。
接下来就是让我们 ai 来使用工具了!
配置mcp
如何配置的帖子已经有很多了,本文以 cline 为例。
{
"mcpServers": {
"github_mcp": {
"command": "node",
"args": [ "/Users/bytedance/code/github_mcp/build/index.js"], // 本地产物路径
"disabled": false,
"autoApprove": []
}
}
}
到这,cline上的mcp配置就ok了,想了解其他ai工具中如何配置mcp的可以参考文章 https://bytetech.info/articles/7470141463196598298#Scgddm5gjoYt1VxzAV1cLHhAnPf
使用
好了让我们来看看我们工具的效果
ai通过我们的自然语言描述自己找到了对应的工具并使用了它!
但是这个返回结构很不直观,我们让他在这里面挑选3条点赞数最高的。
可以看到,ai已经帮我们总结了一下内容!
到这,我们已经成功走完了一个 mcp 工具的开发链路!快亲自动手试试吧🎉,看到这方便的话给博主留个赞吧嘻嘻
如果有对这块感兴趣,或者有什么好玩的想法的也可以联系博主 @林昊鹏 ,一起交流学习!happy coding🎉