Community程式設計與開發github.com

joshi-chinmay-016/AgentProof

在 NitroStack 應用程式中定義工具、資源和提示的指南與模式,並透過 Zod 進行 schema 驗證,包含快取、速率限制和 Base64 檔案上傳。

AgentProof 是什麼?

此技能提供在 NitroStack MCP 伺服器上定義、編輯或驗證工具、資源與提示的完整指南。它詳細說明如何使用 `@Tool`、`@Resource` 和 `@Prompt` 裝飾器,並搭配 Zod 進行嚴格的輸入與輸出 schema 驗證。對於開發者而言,此技能至關重要,因為它不僅涵蓋了核心元件的定義方式,還包含了進階的執行控制機制,例如透過 `@Cache` 裝飾器實作結果快取以提升效能,以及透過 `@RateLimit` 裝飾器實作速率限制以防止用戶端濫用。此外,技能也完整說明了如何在工具中處理來自 MCP 用戶端(如 NitroStudio)的檔案上傳,包括宣告輸入 schema、解碼 Base64 編碼的內容(支援 Data URL 與原始 Base64 兩種格式),以及安全地將檔案儲存至磁碟,並包含防止目錄遍歷攻擊的範例程式碼。支援的平台為任何使用 NitroStack 核心函式庫的應用程式。

相容平台~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:使用烤肉串命名法或蛇形命名法的唯一識別碼。
  • description:詳細描述,說明用戶端應何時以及如何使用此工具。
  • inputSchema:用於嚴格驗證輸入的 Zod 物件 schema。
  • outputSchema(可選):用於驗證輸出結構的 Zod schema。
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: '晴朗',
    };
  }
}

使用 @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. 宣告用於檔案上傳的輸入 Schema

若要接受上傳的檔案,請在工具的 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.

相關技能