构建 MCP 服务器:第 2 部分 — 使用资源模板扩展资源
- AIGC
- 2天前
- 19热度
- 0评论
的视图.png)
这个故事是在多位人工智能助手的帮助下写成的。
这是构建MCP 服务器教程(共四部分)的第二部分。在第一部分中,我们使用基本资源创建了第一个 MCP 服务器。现在,我们将使用资源模板扩展服务器的功能。本文中的代码假设您从上次中断的地方继续学习。
什么是资源模板?
资源模板允许您使用 URI 模式定义动态资源。与具有固定 URI 的静态资源不同,模板允许您创建可根据参数生成 URI 和内容的资源。
可以将它们想象成 Web 框架中的 URL 模式,其中资源更具动态性,通常基于某些标签或 ID - 它们允许您使用单个定义匹配和处理整个资源系列。
为什么要使用资源模板?
当您需要处理动态数据、按需生成内容或创建基于参数的资源等时,资源模板非常强大。
以下是一些示例:
动态数据
“users://{userId}” ->用户资料
“products://{sku}” ->产品信息
用户:“你能告诉我关于用户 12345 的情况吗?”
AI 助手:“正在查找用户 12345......他于 2023 年加入,已进行 50 次购买。”
按需生成内容
“reports://{year}/{month}” ->月度报告
“analytics://{dateRange}” ->自定义分析
用户:“给我看看2024年3月的报告”
AI助手:“正在访问2024年3月的报告……与2月份相比,收入增长了15%。”
基于参数的资源
"search://{query}" ->搜索结果
"filter://{type}/{value}" ->过滤数据
用户:“查找所有超过 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 => {
// List available resources when clients request them
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: "hello://world",
name: "Hello World Message",
description: "A simple greeting message",
mimeType: "text/plain",
},
],
};
});
// Return resource content when clients request it
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
if (request.params.uri === "hello://world") {
return {
contents: [
{
uri: "hello://world",
text: "Hello, World! This is my first MCP resource.",
},
],
};
}
throw new Error("Resource not found");
});
};
更新我们的主要文件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);
// Start server using stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.info('{"jsonrpc": "2.0", "method": "log", "params": { "message": "Server running..." }}');
添加新资源
现在是时候添加我们的新资源模板了。
首先,让我们添加清单,以便 AI 助手知道它在那里。在资源清单(第一个参数为 的那个)src/handlers.js
之后添加以下代码。hello://world
ListResourcesRequestSchema
export const setupHandlers = (server: Server): void => {
// Existing "hello://world" resource listing here ...
// Resource Templates
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
resourceTemplates: [
{
greetings: {
uriTemplate: 'greetings://{name}',
name: 'Personal Greeting',
description: 'A personalized greeting message',
mimeType: 'text/plain',
},
},
],
}));
// Existing "hello://world" resource content here ...
};
接下来我们可以添加内容处理程序。这不需要额外的请求处理程序。我们只需为这种格式的请求添加一个新的检查即可。
// Return resource content when clients request it
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
// ... Existing content handler code
// Template-based resource code
const greetingExp = /^greetings:\/\/(.+)$/;
const greetingMatch = request.params.uri.match(greetingExp);
if (greetingMatch) {
const name = decodeURIComponent(greetingMatch[1]);
return {
contents: [
{
uri: request.params.uri,
text: `Hello, ${name}! Welcome to MCP.`,
},
],
};
}
// ...
});
理解代码
处理程序组织
- 我们已将处理程序移至单独的文件以便更好地组织
- setupHandlers 函数封装了所有处理程序的设置
- 主文件保持干净且专注
模板定义
- 处理程序公开可用的模板
ListResourceTemplateRequestSchema
- 模板名称格式遵循RFC 6570
{text}
(用于表达参数化的URL ) - 模板包括名称和描述等元数据
模板处理
- 处理程序现在检查模板匹配
ReadResourceRequestSchema
- 我们使用正则表达式格式从 URI 中提取名称参数
- 我们根据参数生成动态内容
使用检查器进行测试
在上一篇文章中,我们讨论了如何使用MCP 检查器。现在启动检查器:
npx tsc
npx @modelcontextprotocol/inspector node build/index.js
测试我们上次创建的静态资源,以确保它仍然有效:
- 点击“资源”选项卡
- 找到并点击“Hello World Message”
- 您应该会看到“Hello, World! 这是我的第一个 MCP 资源。”的消息。
测试模板:
- 点击“资源模板”选项卡
- 找到“个人问候语”
- 输入名字“Alice”
{
"contents": [
{
"uri": "greetings://Alice",
"text": "Hello, Alice! Welcome to MCP."
}
]
}
使用 Claude Desktop 进行测试
这次您不需要在 Claude 中更新任何内容,但您可能需要重新加载(同时,确保您已经使用构建了服务npx tsc
)。
正如我上一篇文章所述,我为 Mac 使用的 Claude Desktop 似乎还不支持资源,但您可以在其他支持 MCP 的工具中尝试此操作,例如 Cline,您可能必须专门使用支持 MCP 的模型,例如 Anthropic 的 Sonnet 3.5。
尝试以下示例(响应可能有所不同):
静态资源:
用户: “问候语里有什么?”
Claude: “问候语是:‘来自 MCP 的问候!这是您的第一个资源。’”
模板资源:
用户: “你能给爱丽丝找个问候语吗?”
克劳德: “我去看看个性化问候语……上面写着:‘你好,爱丽丝!欢迎来到 MCP。’”
列出可用资源:
用户:“有哪些资源和模板可用?”
Claude:“服务器提供:
1. 静态‘问候语’资源
2. 可以为任何姓名创建自定义问候语的‘个人问候语’模板”
下一步是什么?
在第 3 部分中,我们将:
- 通过将资源和模板分离到各自的文件中,进一步改善代码组织
- 了解 MCP 提示以及它们与资源的区别
- 为我们的服务器添加提示功能
- 看看提示如何增强我们的问候功能
第 4 部分将通过向我们的服务器添加工具来完成我们的课程。