构建 MCP 服务器:第 2 部分 — 使用资源模板扩展资源
该图像是使用 AI 图像创建程序创建的。
这个故事是在多位人工智能助手的帮助下写成的。
这是构建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 themserver.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 itserver.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 Templatesserver.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 codeconst 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 部分将通过向我们的服务器添加工具来完成我们的课程。
资源和其他阅读材料:
- Introduction - Model Context Protocol
- Cline - Visual Studio Marketplace
- RFC 6570: URI Template
- App unavailable \ Anthropic