使用時機
當您在 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 負載
檔案上傳可能以兩種格式送達,取決於用戶端:
- Data URL 格式:
data:image/png;base64,iVBORw0KGgo... - 原始 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 };
}
}