继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

构建MCP服务器:第二部分——使用资源模板扩展资源

RISEBY
关注TA
已关注
手记 497
粉丝 70
获赞 317

View of a robot picking a specific item \(resource\) from a vending machine

这张图片是用一个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中提取名称参数
  • 我们根据参数动态生成内容
使用 Inspector 进行检查

在我们上一篇帖子里,我们讨论了使用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发展得太快了,我得尽快更新接下来的两部分内容。

相关资源和阅读材料:

(注:以上链接分别指向模型上下文协议网站、Claude插件的Visual Studio Marketplace页面、RFC6570规范文档以及Claude官方下载页面。)

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP