这张图片是用一个AI生成的图像程序生成的。
这个故事是在几位AI助手的帮助下写成的呢。
这是关于如何构建MCP服务器的四部分教程的第二部分。在第一部分,我们使用基本资源创建了第一个MCP服务器。现在我们将会利用资源模板来扩展服务器的功能。本篇中的代码假设您已从上一部分继续。
什么是资源模板?资源模板允许您使用 URI 模式定义动态资源。与静态资源不同,这些资源具有固定的 URI,模板可以让您根据参数生成具有动态 URI 和内容的资源。
可以把它们想象成 Web 框架中的 URL 模式,其中资源更具动态性,通常基于某个标签或ID — 它们让你能够使用一个定义就可以匹配和处理整个资源系列。
要为什么要使用资源模板? 为什么要使用资源模板?经过调整,最终翻译为:
要为什么使用资源模板? 为什么要使用资源模板?简化后确定最终版本:
为什么要使用资源模板?资源模板在处理动态数据、按需生成或创建参数驱动的资源等场景时非常有用。
这里有一些例子:
动态的数据,即可以改变的数据 "users://{userId}" -> 用户资料页面:{用户ID}
"products://{sku}" -> 产品详情页面:{产品ID}
按需生成内容用户: "你能告诉我关于用户12345的信息吗?"
AI助手: "正在查找... 用户12345是在2023年加入的,已经买了50次。"
"reports://{year}/{month}" -> 每月的报告
"analytics://{dateRange}" -> 自定义日期分析
参数驱动的资源用户:“给我看一下2024年3月的报告。”
AI助手:“正在获取3月的报告……与2月相比,收入增长了15个百分点。”
"搜索://{查询}" -> 搜索到的结果
"过滤://{类型}/{值}" -> 筛选后的数据
整理我们的代码 —用户:“查找所有超过1000美元的交易记录”
AI助手:“使用过滤功能...找到了23笔符合您条件的交易记录。”
让我们也优化一下代码结构,将一些关注点分离。首先,让我们将处理程序移到一个新的文件(handlers.ts)中,这样我们的代码就不会那么杂乱无章了。
// src/handlers.ts
import {
ListResourcesRequestSchema,
ReadResourceRequestSchema,
ListResourceTemplatesRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { type Server } from "@modelcontextprotocol/sdk/server/index.js";
export const setupHandlers = (server: Server): void => {
// 列出可供客户端使用的资源
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "hello://world",
name: "Hello World 消息",
description: "一个简单的问候",
mimeType: "text/plain",
},
],
};
});
// 返回资源的内容
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
if (request.params.uri === "hello://world") {
return {
contents: [
{
uri: "hello://world",
text: "你好,世界!这是我的第一个MCP资源,内容如下。",
},
],
};
}
throw new Error("未找到请求的资源");
});
};
更新主文件 src/index.ts
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { setupHandlers } from './handlers.js';
const server = new Server(
{
name: "hello-mcp",
version: "1.0.0",
},
{
capabilities: {
resources: {},
},
}
);
setupHandlers(server);
// 使用标准输入输出启动服务器
const transport = new StdioServerTransport();
await server.connect(transport);
console.info('{"jsonrpc": "2.0", "method": "log", "params": { "message": "服务器已启动..." }}');
加入新资源
我们该添加新的资源模板了。
首先,让我们添加我们的项目,让AI助手知道我们已经添加了这个项目。在src/handlers.js
中,在hello://world
资源项后面添加以下的代码段:
export const setupHandlers = (server: Server): void => {
// 现有的 "hello://world" 资源清单在这里 ...
// 资源模板部分
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
resourceTemplates: [
{
greetings: {
uriTemplate: 'greetings://{name}', // 占位符
name: '个性化问候',
description: '个性化的问候消息',
mimeType: 'text/plain', // 媒体类型
},
},
],
}));
// 现有 "hello://world" 资源的内容在这里 ...
};
接下来我们可以添加我们的内容处理程序。这不需要额外的请求处理程序。我们只需在这个格式的请求中添加一个新的检查条件。
// 当客户端请求资源时返回资源内容
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
// ... 现有的内容处理代码
// 这里是基于模板生成资源的代码
const greetingExp = /^greetings:\/\/(.+)$/;
const greetingMatch = request.params.uri.match(greetingExp);
if (greetingMatch) {
const name = decodeURIComponent(greetingMatch[1]);
return {
contents: [
{
uri: request.params.uri,
text: `你好,${name}!欢迎来到MCP。`, // 注:MCP是公司内部的简称
},
],
};
}
// ...
});
读懂代码
处理程序团队
- 我们将处理程序移到了单独的文件中,以便更好地组织代码
- setupHandlers 函数封装了所有处理程序的设置
- 主文件因此保持简洁和专注
ListResourceTemplateRequestSchema
会展示可用的模板- 模板名称格式遵循RFC 6570(使用
{text}
来表示URL中的参数) - 模板还包括名称和描述等信息
ReadResourceRequestSchema
处理器现在检查模板匹配情况- 我们使用正则表达式从URI中提取名称参数
- 我们根据参数动态生成内容
在我们上一篇帖子里,我们讨论了使用MCP Inspector。现在就启动 Inspector 吧:
npx tsc
npx @modelcontextprotocol/inspector node build/index.js
测试一下我们上次创建的静态资源,确保它仍然好用。
- 点击“资源”标签
- 找到并点击“示例问候消息”(Hello World Message)
- 你应该能看到“Hello, World! 这是我第一个MCP资源消息。”这条消息
试试这个模板:
- 点击“资源模板选项卡”
- 找到“个人问候模板”
- 输入“Alice”
{
"contents": [
{
"uri": "greetings://Alice",
"text": "你好,Alice!欢迎来到MCP(MCP)!"
}
]
}
在Claude桌面版上测试
这次在Claude里你不需要更新任何东西,不过可能需要刷新一下,记得用npx tsc
构建服务。
和我上一篇帖子一样,我用的 Mac 版 Claude Desktop 看起来还不支持资源,但你可以在其他支持 MCP 的工具(如 Cline)里试试看,你可能需要特别使用支持 MCP 的模型,比如 Anthropic 的 Sonnet 3.5。
试试这些例子看看(回答可能有所不同。)
静态文件: 用户:「问候信息里写了些什么?」
克劳德:「问候信息是这么说的:‘来自MCP的问候:!这是你的第一个资源。’」
模板资源如下:
用户:“你能给爱丽丝一个问候语吗?”
克劳德:“我来看看个性化问候……写着:‘嗨,爱丽丝!欢迎来到MCP。’”
列一下可用的资源吧
用户:「有哪些可用的资源和模板?」
Claude:「服务器提供:
1. 一个静态的问候消息资源
2. 一个可以为任何名称生成个性化的问候的个人问候模板」
接下来会怎么样?
在第三部分,我们将会:
- 进一步优化代码结构,将资源和模板分别放入各自的文件中
- 了解MCP提示和其他资源的区别
- 给我们的服务器添加提示功能
- 看看提示如何提升我们的问候功能
第四部分 将通过给服务器添加工具来完成我们的课程。
不过,AI发展得太快了,我得尽快更新接下来的两部分内容。
相关资源和阅读材料:- https://modelcontextprotocol.io/
- https://marketplace.visualstudio.com/items?itemName=saoudrizwan.claude-dev
- https://www.rfc-editor.org/rfc/rfc6570
- https://claude.ai/download
(注:以上链接分别指向模型上下文协议网站、Claude插件的Visual Studio Marketplace页面、RFC6570规范文档以及Claude官方下载页面。)