当前位置: 首页 > news >正文

【MCP Node.js SDK 全栈进阶指南】中级篇(3):MCP高级资源设计

前言

在MCP TypeScript-SDK的初级篇中,我们介绍了资源开发的基础知识,包括静态资源与动态资源的创建、资源模板设计与参数提取,以及基本的资源列表与发现机制。随着应用规模的扩大和复杂性的提高,我们需要更加高级的资源设计方案来应对各种挑战。

本文作为中级篇的第三篇,将深入探讨MCP高级资源设计,包括复杂资源结构设计、资源分页与过滤、资源缓存策略以及大规模资源管理方案。通过学习这些高级技术,你将能够构建更加高效、灵活且可扩展的MCP资源系统,满足复杂企业级应用的需求。

一、复杂资源结构设计

在实际应用场景中,资源往往具有复杂的结构和关系,简单的扁平资源结构无法满足复杂业务需求。本节将探讨如何设计和实现复杂的资源结构。

1.1 嵌套资源设计

嵌套资源是指资源之间存在层级关系,子资源依附于父资源存在。嵌套资源设计可以更好地表达业务实体之间的从属关系。

import { McpServer } from '@modelcontextprotocol/sdk';
import { z } from 'zod';const server = new McpServer({name: 'nested-resource-server',description: '嵌套资源示例服务器',version: '1.0.0',
});// 创建公司资源
server.registerResource({name: 'companies',description: '公司列表资源',parameters: z.object({filter: z.string().optional().describe('过滤条件'),}),resolve: async ({ filter }) => {const companies = await fetchCompanies(filter);return {content: JSON.stringify(companies),metadata: {count: companies.length,filter: filter || 'none',}};}
});// 创建部门资源(作为公司的子资源)
server.registerResource({name: 'companies/:companyId/departments',description: '部门列表资源',parameters: z.object({companyId: z.string().describe('公司ID'),filter: z.string().optional().describe('过滤条件'),}),resolve: async ({ companyId, filter }) => {const departments = await fetchDepartments(companyId, filter);return {content: JSON.stringify(departments),metadata: {companyId,count: departments.length,filter: filter || 'none',}};}
});// 创建员工资源(作为部门的子资源)
server.registerResource({name: 'companies/:companyId/departments/:departmentId/employees',description: '员工列表资源',parameters: z.object({companyId: z.string().describe('公司ID'),departmentId: z.string().describe('部门ID'),filter: z.string().optional().describe('过滤条件'),}),resolve: async ({ companyId, departmentId, filter }) => {const employees = await fetchEmployees(companyId, departmentId, filter);return {content: JSON.stringify(employees),metadata: {companyId,departmentId,count: employees.length,filter: filter || 'none',}};}
});

1.2 资源关系与引用

在复杂系统中,资源之间往往存在各种关系,如一对一、一对多、多对多等。通过资源引用可以表达这些复杂关系,并允许客户端遍历相关资源。

// 使用URI引用表达资源之间的关系
server.registerResource({name: 'projects/:projectId',description: '项目详情',parameters: z.object({projectId: z.string().describe('项目ID'),}),resolve: async ({ projectId }) => {const project = await fetchProject(projectId);// 构建关联资源的URI引用const teamUri = `teams://${project.teamId}`;const tasksUri = `projects://${projectId}/tasks`;const documentsUri = `projects://${projectId}/documents`;return {content: JSON.stringify({...project,// 添加资源引用_links: {team: teamUri,tasks: tasksUri,documents: documentsUri,}}),metadata: {relatedResources: [teamUri, tasksUri, documentsUri],}};}
});

1.3 资源版本控制

对于频繁变化的资源,版本控制是确保一致性和兼容性的重要机制。

// 实现资源版本控制
server.registerResource({name: 'api-specs/:version',description: 'API规范文档',parameters: z.object({version: z.string().describe('API版本,格式为v1, v2等,或latest表示最新版本'),}),resolve: async ({ version }) => {// 处理特殊版本标识符const actualVersion = version === 'latest' ? await getLatestApiVersion(): version;const apiSpec = await fetchApiSpec(actualVersion);if (!apiSpec) {throw new Error(`API规范版本 ${actualVersion} 不存在`);}return {content: apiSpec.content,metadata: {version: actualVersion,publishedAt: apiSpec.publishedAt,isLatest: await isLatestVersion(actualVersion),previousVersion: apiSpec.previousVersion,nextVersion: apiSpec.nextVersion,}};}
});

1.4 多语言资源支持

全球化应用需要支持多种语言的资源内容,MCP可以轻松实现多语言资源。

// 实现多语言资源支持
server.registerResource({name: 'localized-content/:contentId',description: '多语言内容资源',parameters: z.object({contentId: z.string().describe('内容ID'),language: z.string().default('en').describe('语言代码,如en, zh-CN, ja等'),}),resolve: async ({ contentId, language }) => {const content = await fetchLocalizedContent(contentId, language);// 如果请求的语言版本不存在,返回默认语言版本const actualContent = content || await fetchLocalizedContent(contentId, 'en');if (!actualContent) {throw new Error(`内容 ${contentId} 不存在`);}return {content: actualContent.content,metadata: {contentId,language: actualContent.language, // 返回实际使用的语言availableLanguages: await getAvailableLanguages(contentId),defaultLanguage: 'en',translatedAt: actualContent.translatedAt,}};}
});

二、资源分页与过滤

当资源数据量很大时,一次性加载所有资源会导致性能问题。资源分页和过滤机制可以帮助客户端高效地访问大型数据集。

2.1 基于游标的分页实现

MCP推荐使用基于游标的分页机制,而不是传统的基于页码的分页。基于游标的分页更加高效,特别是对于大型数据集,且能够有效处理数据集动态变化的情况。

import { McpServer } from '@modelcontextprotocol/sdk';
import { z } from 'zod';const server = new McpServer({name: 'pagination-server',description: '分页资源示例服务器',version: '1.0.0',
});// 基于游标的分页实现
server.registerResource({name: 'articles',description: '文章列表资源',parameters: z.object({limit: z.number().int().min(1).max(100).default(20).describe('每页条数'),cursor: z.string().optional().describe('分页游标'),sortBy: z.enum(['date', 'title', 'views']).default('date').describe('排序字段'),sortOrder: z.enum(['asc', 'desc']).default('desc').describe('排序方向'),}),resolve: async ({ limit, cursor, sortBy, sortOrder }) => {// 解析游标(如果有)let cursorData = { offset: 0 };if (cursor) {try {cursorData = JSON.parse(Buffer.from(cursor, 'base64').toString());} catch (error) {throw new Error('无效的分页游标');}}// 查询数据const { articles, totalCount } = await fetchArticles({offset: cursorData.offset,limit,sortBy,sortOrder,});// 计算下一页游标const nextOffset = cursorData.offset + articles.length;const hasNextPage = nextOffset < totalCount;const nextCursor = hasNextPage? Buffer.from(JSON.stringify({ offset: nextOffset })).toString('base64'): null;return {content: JSON.stringify(articles),metadata: {pagination: {totalCount,returnedCount: articles.length,hasNextPage,nextCursor,},sortBy,sortOrder,}};}
});

2.2 高级过滤与搜索

过滤和搜索机制允许客户端精确获取所需的资源子集,减少不必要的数据传输。

// 实现高级过滤与搜索
server.registerResource({name: 'products',description: '产品列表资源',parameters: z.object({cursor: z.string().optional().describe('分页游标'),limit: z.number().int().min(1).max(100).default(20).describe('每页条数'),// 高级过滤参数category: z.string().optional().describe('产品类别'),minPrice: z.number().optional().describe('最低价格'),maxPrice: z.number().optional().describe('最高价格'),inStock: z.boolean().optional().describe('是否有库存'),tags: z.array(z.string()).optional().describe('标签列表'),// 搜索参数query: z.string().optional().describe('搜索关键词'),searchFields: z.array(z.enum(['name', 'description', 'sku'])).optional().describe('搜索字段'),}),resolve: async (params) => {// 构建过滤条件const filters = [

相关文章:

  • API路由大法:统一前缀,化繁为简
  • C# MP3 伴奏
  • 仓储物流管理系统开发:提升企业供应链效率的关键技术
  • 为啥低速MCU单板辐射测试会有200M-1Ghz的辐射信号
  • 【教程】ESP32制作为ISP烧录器
  • 三网通电玩城平台系统结构与源码工程详解(一):系统概述与前端搭建
  • 如何精准查询住宅IP?工具、方法与注意事项
  • 凤凰架构-笔记
  • 精益数据分析(13/126):洞察数据关系,灵活调整创业方向
  • 近几年字节测开部分面试题整理
  • 【YOLOv8改进 - C2f融合】C2f融合SHViTBlock:保证计算效率的同时,能够有效地捕捉图像的局部和全局特征
  • 智慧城市新标配:苏州金龙无人清扫车开启城市清洁“智”时代
  • 同样的html标记,不同语言的文本,显示的字体和粗细会不一样吗
  • 【AAudio】A2dp sink创建音频轨道的源码流程分析
  • TCP/IP协议新手友好详解
  • 使用C#写的HTTPS简易服务器
  • Rest Client插件写http文件直接发送请求
  • 深度解析:基于卷积神经网络的宠物识别
  • Feign 深度解析:Java 声明式 HTTP 客户端的终极指南
  • Linux操作系统--进程程序替换and做一个简单的shell
  • 宁德时代与广汽等五车企发布10款巧克力换电新车型:年内将将完成30城1000站计划
  • 央行副行长陆磊:国际化程度有效提升是上海国际金融中心建设的一个主要方向
  • 阻燃材料点火就着引发一场火灾,河北一企业的产品被指不达标且涉嫌欺诈
  • 杨国荣丨《儒耶对话与中国现代思想的生成和发展》序
  • 拍北京地铁上的读书人第七年:数字风吹散读书人了吗?
  • 细说汇率 ⑬ 美元进入“全是坏消息”阶段