
该图像是使用 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 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://worldListResourcesRequestSchema
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 部分将通过向我们的服务器添加工具来完成我们的课程。
资源和其他阅读材料:
- Introduction - Model Context Protocol
- Cline - Visual Studio Marketplace
- RFC 6570: URI Template
- App unavailable \ Anthropic



















