Community编程与开发github.com

joshi-chinmay-016/AgentProof

在 NitroStack 应用中定义工具、资源和提示的指南与模式,包含通过 Zod 进行模式验证、缓存、限流和 base64 文件上传。

AgentProof 是什么?

此技能为在 NitroStack MCP(模型上下文协议)服务器上定义和管理工具、资源和提示提供了全面指南。它详细说明了如何使用 `@Tool`、`@Resource` 和 `@Prompt` 装饰器来创建结构化的可调用端点,并利用 Zod 进行严格的数据验证。该技能还涵盖了通过 `@Cache` 实现性能优化(支持基于 TTL 的自定义键缓存)以及通过 `@RateLimit` 实现访问控制(支持基于上下文的限流键)。此外,它还提供了一个安全的文件上传处理框架,能够解析 data URL 和原始 base64 格式,并包含防止路径遍历攻击的安全保存逻辑。该技能适用于 Claude Code、Cursor 等 AI 编码助手,帮助开发者遵循 NitroStack 的最佳实践,构建可靠且高性能的 MCP 服务器。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/joshi-chinmay-016/AgentProof/tree/HEAD/agentproof/.agents/skills/tools-resources-prompts

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

何时使用

在 NitroStack MCP 服务器上定义、编辑或验证工具、资源或提示时,请使用此技能。

使用 @Tool 定义工具

MCP 工具暴露一个可由 AI 客户端调用的函数。使用 @Tool 装饰服务或控制器方法。

关键工具选项:

  • name:采用 kebab-case 或 snake_case 的唯一标识符。
  • description:详细说明客户端何时以及如何使用该工具的描述。
  • inputSchema:用于严格验证输入的 Zod 对象模式。
  • outputSchema(可选):验证输出结构的 Zod 模式。
import { ToolDecorator as Tool, ControllerDecorator as Controller, InitialTool, z, ExecutionContext } from '@nitrostack/core';

@Controller('weather')
export class WeatherService {
  @Tool({
    name: 'get_current_weather',
    description: '获取指定城市的当前天气预报。',
    inputSchema: z.object({
      city: z.string().describe('城市名称,例如 San Francisco'),
      unit: z.enum(['celsius', 'fahrenheit']).default('celsius'),
    }),
  })
  @InitialTool() // 当 AI 客户端初始化/启动时自动调用
  async getWeather(
    input: { city: string; unit: 'celsius' | 'fahrenheit' },
    ctx: ExecutionContext
  ) {
    ctx.logger.info(`正在获取 ${input.city} 的天气信息`);
    // 实现
    return {
      city: input.city,
      temp: 22,
      condition: 'Sunny',
    };
  }
}

使用 @Resource 定义资源

MCP 资源暴露 AI 客户端可以读取的静态或动态数据文件/URI。

关键资源选项:

  • uri:URI 模式(例如 git://{owner}/{repo}/file 或静态 app://config)。
  • name:资源的唯一名称。
  • description:说明此资源提供什么数据。
  • mimeType:响应的 MIME 类型(例如 text/plain、application/json)。
import { Resource, ExecutionContext } from '@nitrostack/core';

export class ConfigResources {
  @Resource({
    uri: 'app://settings',
    name: '应用程序设置',
    description: '系统级的配置设置和参数。',
    mimeType: 'application/json',
  })
  async getSettings(ctx: ExecutionContext) {
    return {
      environment: 'development',
      debugMode: true,
    };
  }
}

使用 @Prompt 定义提示

MCP 提示暴露可重用的模板或指令集,用于指导 LLM。

关键提示选项:

  • name:提示的名称。
  • description:描述此提示有助于完成什么任务。
  • arguments:声明客户端可以提供哪些参数来自定义提示模板。
import { Prompt, ExecutionContext } from '@nitrostack/core';

export class PromptTemplates {
  @Prompt({
    name: 'code_review',
    description: '对给定的代码片段进行深入的代码审查。',
    arguments: [
      { name: 'language', description: '编程语言,例如 TypeScript', required: true },
      { name: 'code', description: '要审查的代码片段', required: true },
    ],
  })
  async getCodeReviewPrompt(
    args: { language: string; code: string },
    ctx: ExecutionContext
  ) {
    return {
      messages: [
        {
          role: 'user',
          content: `你是一名专业的软件工程师。请审查以下 ${args.language} 代码:\n\n${args.code}`,
        },
      ],
    };
  }
}

工具策略:缓存(@Cache)和限流(@RateLimit)

你可以使用方法装饰器来控制工具的执行行为(例如性能优化和限流)。

1. 使用 @Cache 进行缓存

使用 @Cache 在指定的时间内(TTL,以秒为单位)缓存工具的执行输出。这可以减少对频繁相同请求的数据库或 API 开销。

选项:

  • ttl:缓存生存时间,以秒为单位(必填)。
  • key(可选):自定义函数 (input: any, context?: any) => string,根据输入返回唯一的缓存键。如果未定义,则会根据序列化的输入参数自动生成键。

示例:

import { ToolDecorator as Tool, Cache, z } from '@nitrostack/core';

export class StationTools {
  @Tool({
    name: 'get_system_status',
    description: '获取实时的空间站指标。响应会被缓存。',
    inputSchema: z.object({}),
  })
  @Cache({ ttl: 60 }) // 缓存状态 60 秒
  async getSystemStatus() {
    return { temperature: 21.5, oxygen: 0.98 };
  }

  @Tool({
    name: 'get_crew_status',
    description: '获取指定乘员的状态。按乘员 ID 缓存。',
    inputSchema: z.object({ id: z.string() }),
  })
  @Cache({
    ttl: 300,
    key: (input) => `crew:status:${input.id}`
  })
  async getCrewStatus(input: { id: string }) {
    // ...
  }
}

2. 使用 @RateLimit 进行限流

使用 @RateLimit 限制在指定时间窗口内的工具调用次数,以防止客户端滥用。

选项:

  • requests:在窗口内允许的请求数(必填)。
  • window:限流时间窗口(必填)。支持 '1s'、'1m'、'1h' 等格式。
  • key(可选):自定义函数 (context: ExecutionContext) => string,用于分组的限流键。可用于根据用户角色或 API 密钥进行限流。

示例:

import { ToolDecorator as Tool, RateLimit, z, ExecutionContext } from '@nitrostack/core';

export class DiagnosticTools {
  @Tool({
    name: 'run_deep_diagnostic',
    description: '运行密集型诊断。已限流。',
    inputSchema: z.object({}),
  })
  @RateLimit({ requests: 3, window: '1m' }) // 全局每分钟最多 3 个请求
  async runDeepDiagnostic() {
    return { diagnosticReport: '所有系统运行正常。' };
  }

  @Tool({
    name: 'request_supply_drop',
    description: '请求库存物资。按用户限流。',
    inputSchema: z.object({ item: z.string() }),
  })
  @RateLimit({
    requests: 5,
    window: '1h',
    key: (ctx: ExecutionContext) => ctx.auth?.subject || 'anonymous'
  })
  async requestSupply(input: { item: string }, ctx: ExecutionContext) {
    // ...
  }
}

在工具中处理文件上传

NitroStack 支持从 MCP 客户端(如 NitroStudio)上传文件,方式是将文件作为 base64 编码的字符串传递到工具的输入参数中。

1. 声明文件上传的输入模式

要接受上传的文件,请在工具的 inputSchema 中定义三个 Zod 字段:

  • file_name:文件名(例如 report.csv)。
  • file_type:MIME 类型(例如 text/csv)。
  • file_content:包含文件数据的 base64 编码字符串。
import { ToolDecorator as Tool, ExecutionContext, z } from '@nitrostack/core';

export class FileTools {
  @Tool({
    name: 'upload_document',
    description: '上传文本文档或图片。',
    inputSchema: z.object({
      file_name: z.string().describe('上传文件的名称'),
      file_type: z.string().describe('上传文件的 MIME 类型'),
      file_content: z.string().describe('Base64 编码的文件内容'),
    })
  })
  async uploadDocument(input: any, ctx: ExecutionContext) {
    // 处理逻辑
  }
}

2. 解码 Base64 负载

根据客户端的不同,文件上传可能以两种格式到达:

  1. Data URL 格式:data:image/png;base64,iVBORw0KGgo...
  2. 原始 Base64 格式:iVBORw0KGgo...

使用以下通用解码模式将任一格式解析为 Node Buffer:

import * as fs from 'fs';
import * as path from 'path';

function decodeBase64File(content: string): Buffer {
  const matches = content.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
  
  if (matches && matches.length === 3) {
    // Data URL 格式 - 解码 matches[2]
    return Buffer.from(matches[2], 'base64');
  } else {
    // 原始 base64 格式 - 直接解码输入
    return Buffer.from(content, 'base64');
  }
}

3. 安全保存文件示例

在将文件保存到磁盘时,务必验证目录路径以防止目录遍历攻击。

import { ToolDecorator as Tool, ExecutionContext, z } from '@nitrostack/core';
import * as fs from 'fs';
import * as path from 'path';

const UPLOAD_DIR = path.join(process.cwd(), 'uploads');

export class SecureUploadTools {
  @Tool({
    name: 'save_uploaded_file',
    description: '安全地解码并保存上传的文件。',
    inputSchema: z.object({
      file_name: z.string().describe('上传文件的名称'),
      file_type: z.string().describe('上传文件的 MIME 类型'),
      file_content: z.string().describe('Base64 编码的文件内容'),
    })
  })
  async saveFile(input: any, ctx: ExecutionContext) {
    // 确保上传目录存在
    if (!fs.existsSync(UPLOAD_DIR)) {
      fs.mkdirSync(UPLOAD_DIR, { recursive: true });
    }

    // 安全的文件路径,防止路径遍历
    const safeName = path.basename(input.file_name);
    const filePath = path.join(UPLOAD_DIR, safeName);
    if (!filePath.startsWith(UPLOAD_DIR)) {
      throw new Error('检测到无效的文件路径(路径遍历)。');
    }

    // 解码并写入磁盘
    const buffer = decodeBase64File(input.file_content);
    fs.writeFileSync(filePath, buffer);

    ctx.logger.info(`成功保存文件:${safeName}`);
    return { success: true, path: filePath };
  }
}

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相关技能