diff --git a/package-lock.json b/package-lock.json index 6d52562..cace66f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "@modelcontextprotocol/sdk": "^1.0.0", "dotenv": "^16.5.0", "uuid": "^9.0.1", - "zod": "^3.22.4" + "zod": "^3.22.4", + "zod-to-json-schema": "^3.24.5" }, "bin": { "mcp-shrimp-task-manager": "dist/index.js" @@ -1141,6 +1142,7 @@ "version": "3.24.5", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "license": "ISC", "peerDependencies": { "zod": "^3.24.1" } diff --git a/package.json b/package.json index 7a06c3e..e5951b7 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "@modelcontextprotocol/sdk": "^1.0.0", "dotenv": "^16.5.0", "uuid": "^9.0.1", - "zod": "^3.22.4" + "zod": "^3.22.4", + "zod-to-json-schema": "^3.24.5" }, "devDependencies": { "@types/node": "^20.8.2", diff --git a/src/index.ts b/src/index.ts index bd8e461..0594943 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,12 @@ import "dotenv/config"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { zodToJsonSchema } from "zod-to-json-schema"; +import { + CallToolRequest, + CallToolRequestSchema, + ListToolsRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; // 導入工具函數 import { @@ -51,160 +57,301 @@ async function main() { console.log("啟動蝦米任務管理器服務..."); // 創建MCP服務器 - const server = new McpServer({ - name: "蝦米任務管理器", - version: "1.0.0", + const server = new Server( + { + name: "蝦米任務管理器", + version: "1.0.0", + }, + { + capabilities: { + tools: {}, + }, + } + ); + + // { + // type: "object" as const, + // } + server.setRequestHandler(ListToolsRequestSchema, async () => { + return { + tools: [ + { + name: "plan_task", + description: + "初始化並詳細規劃任務流程,建立明確的目標與成功標準,可選擇參考現有任務進行延續規劃", + inputSchema: zodToJsonSchema(planTaskSchema), + }, + { + name: "analyze_task", + description: + "深入分析任務需求並系統性檢查代碼庫,評估技術可行性與潛在風險,如果需要提供程式碼請使用 pseudocode 格式且盡量精簡只保留核心實現部分", + inputSchema: zodToJsonSchema(analyzeTaskSchema), + }, + { + name: "reflect_task", + description: + "批判性審查分析結果,評估方案完整性並識別優化機會,確保解決方案符合最佳實踐,如果需要提供程式碼請使用 pseudocode 格式且盡量精簡只保留核心實現部分", + inputSchema: zodToJsonSchema(reflectTaskSchema), + }, + { + name: "split_tasks", + description: + "將複雜任務分解為獨立且可追蹤的子任務,建立明確的依賴關係和優先順序。支援四種任務更新模式:追加(append)、覆蓋(overwrite)、選擇性更新(selective)和清除所有任務(clearAllTasks),其中覆蓋模式只會刪除未完成的任務並保留已完成任務,選擇性更新模式可根據任務名稱智能匹配更新現有任務,同時保留其他任務,如果你需要規劃全新的任務請使用清除所有任務模式會清除所有任務並創建備份。請優先使用清除所有任務模式,只有用戶要求變更或修改計畫內容才使用其他模式。\n\n**請參考之前的分析結果提供 pseudocode\n\n**如果任務太多或內容過長,請分批使用「split_tasks」工具,每次只提交一小部分任務,**嚴重警告**你每次呼叫 split_tasks 傳遞的參數不能超過8000個字,如果超出 8000 個字請多次呼叫工具完成", + inputSchema: zodToJsonSchema(splitTasksSchema), + }, + { + name: "list_tasks", + description: + "生成結構化任務清單,包含完整狀態追蹤、優先級和依賴關係", + inputSchema: zodToJsonSchema(listTasksSchema), + }, + { + name: "execute_task", + description: + "按照預定義計劃執行特定任務,確保每個步驟的輸出符合質量標準", + inputSchema: zodToJsonSchema(executeTaskSchema), + }, + { + name: "verify_task", + description: + "全面驗證任務完成度,確保所有需求與技術標準都已滿足,並無遺漏細節", + inputSchema: zodToJsonSchema(verifyTaskSchema), + }, + { + name: "complete_task", + description: + "正式標記任務為完成狀態,生成詳細的完成報告,並更新關聯任務的依賴狀態", + inputSchema: zodToJsonSchema(completeTaskSchema), + }, + { + name: "delete_task", + description: + "刪除未完成的任務,但不允許刪除已完成的任務,確保系統記錄的完整性", + inputSchema: zodToJsonSchema(deleteTaskSchema), + }, + { + name: "clear_all_tasks", + description: + "刪除系統中所有未完成的任務,該指令必須由用戶明確確認才能執行", + inputSchema: zodToJsonSchema(clearAllTasksSchema), + }, + { + name: "update_task", + description: + "更新任務內容,包括名稱、描述和注記,但不允許修改已完成的任務", + inputSchema: zodToJsonSchema(updateTaskContentSchema), + }, + { + name: "update_task_files", + description: + "更新任務相關文件列表,用於記錄與任務相關的代碼文件、參考資料等", + inputSchema: zodToJsonSchema(updateTaskRelatedFilesSchema), + }, + { + name: "query_task", + description: "根據關鍵字或ID搜尋任務,顯示省略版的任務資訊", + inputSchema: zodToJsonSchema(queryTaskSchema), + }, + { + name: "get_task_detail", + description: + "根據任務ID獲取任務的完整詳細信息,包括未截斷的實現指南和驗證標準等", + inputSchema: zodToJsonSchema(getTaskDetailSchema), + }, + { + name: "process_thought", + description: + "任何需要思考或分析的時候,透過該工具進行靈活的、可適應和發展的思考過程來分析問題,隨著理解的加深,每個想法都可以建立、質疑或修改先前的見解。你可以質疑想法、假設想法、驗證想法,並且可以建立新的想法。你將重複這個過程,直到你對問題有足夠的理解,並且能夠提出有效的解決方案。如果你覺得思考已經充分可以把 nextThoughtNeeded 設為 false 並且停止思考,如果你覺得需要更多的思考你可以隨時變更 total_thoughts 來增加步驟。", + inputSchema: zodToJsonSchema(processThoughtSchema), + }, + { + name: "init_project_rules", + description: + "初始化專案規範,當用戶要求產生或初始化專案規範文件時呼叫該工具,如果用戶要求變更或更新專案規範也呼叫該工具", + inputSchema: zodToJsonSchema(initProjectRulesSchema), + }, + ], + }; }); - // 註冊工具 - 使用已定義的schema物件,並添加內嵌錯誤處理 - server.tool( - "plan_task", - "初始化並詳細規劃任務流程,建立明確的目標與成功標準,可選擇參考現有任務進行延續規劃", - planTaskSchema.shape, - async (args) => { - return await planTask(args); - } - ); + server.setRequestHandler( + CallToolRequestSchema, + async (request: CallToolRequest) => { + try { + if (!request.params.arguments) { + throw new Error("No arguments provided"); + } - server.tool( - "analyze_task", - "深入分析任務需求並系統性檢查代碼庫,評估技術可行性與潛在風險,如果需要提供程式碼請使用 pseudocode 格式且盡量精簡只保留核心實現部分", - analyzeTaskSchema.shape, - async (args) => { - return await analyzeTask(args); - } - ); - - server.tool( - "reflect_task", - "批判性審查分析結果,評估方案完整性並識別優化機會,確保解決方案符合最佳實踐,如果需要提供程式碼請使用 pseudocode 格式且盡量精簡只保留核心實現部分", - reflectTaskSchema.shape, - async (args) => { - return await reflectTask(args); - } - ); - - server.tool( - "split_tasks", - "將複雜任務分解為獨立且可追蹤的子任務,建立明確的依賴關係和優先順序。支援四種任務更新模式:追加(append)、覆蓋(overwrite)、選擇性更新(selective)和清除所有任務(clearAllTasks),其中覆蓋模式只會刪除未完成的任務並保留已完成任務,選擇性更新模式可根據任務名稱智能匹配更新現有任務,同時保留其他任務,如果你需要規劃全新的任務請使用清除所有任務模式會清除所有任務並創建備份。請優先使用清除所有任務模式,只有用戶要求變更或修改計畫內容才使用其他模式。\n\n**請參考之前的分析結果提供 pseudocode\n\n**如果任務太多或內容過長,請分批使用「split_tasks」工具,每次只提交一小部分任務", - splitTasksSchema.shape, - async (args) => { - return await splitTasks(args); - } - ); - - server.tool( - "list_tasks", - "生成結構化任務清單,包含完整狀態追蹤、優先級和依賴關係", - listTasksSchema.shape, - async (args) => { - return await listTasks(args); - } - ); - - server.tool( - "execute_task", - "按照預定義計劃執行特定任務,確保每個步驟的輸出符合質量標準", - executeTaskSchema.shape, - async (args) => { - return await executeTask(args); - } - ); - - server.tool( - "verify_task", - "全面驗證任務完成度,確保所有需求與技術標準都已滿足,並無遺漏細節", - verifyTaskSchema.shape, - async (args) => { - return await verifyTask(args); - } - ); - - server.tool( - "complete_task", - "正式標記任務為完成狀態,生成詳細的完成報告,並更新關聯任務的依賴狀態", - completeTaskSchema.shape, - async (args) => { - return await completeTask(args); - } - ); - - server.tool( - "delete_task", - "刪除未完成的任務,但不允許刪除已完成的任務,確保系統記錄的完整性", - deleteTaskSchema.shape, - async (args) => { - return await deleteTask(args); - } - ); - - // 註冊清除所有任務工具 - server.tool( - "clear_all_tasks", - "刪除系統中所有未完成的任務,該指令必須由用戶明確確認才能執行", - clearAllTasksSchema.shape, - async (args) => { - return await clearAllTasks(args); - } - ); - - // 註冊更新任務工具 - server.tool( - "update_task", - "更新任務內容,包括名稱、描述和注記,但不允許修改已完成的任務", - updateTaskContentSchema.shape, - async (args) => { - return await updateTaskContent(args); - } - ); - - // 註冊更新任務相關文件工具 - server.tool( - "update_task_files", - "更新任務相關文件列表,用於記錄與任務相關的代碼文件、參考資料等", - updateTaskRelatedFilesSchema.shape, - async (args) => { - return await updateTaskRelatedFiles(args); - } - ); - - // 註冊查詢任務工具 - server.tool( - "query_task", - "根據關鍵字或ID搜尋任務,顯示省略版的任務資訊", - queryTaskSchema.shape, - async (args) => { - return await queryTask(args); - } - ); - - // 註冊取得任務完整詳情工具 - server.tool( - "get_task_detail", - "根據任務ID獲取任務的完整詳細信息,包括未截斷的實現指南和驗證標準等", - getTaskDetailSchema.shape, - async (args) => { - return await getTaskDetail(args); - } - ); - - // 註冊思維鏈工具 - server.tool( - "process_thought", - "任何需要思考或分析的時候,透過該工具進行靈活的、可適應和發展的思考過程來分析問題,隨著理解的加深,每個想法都可以建立、質疑或修改先前的見解。你可以質疑想法、假設想法、驗證想法,並且可以建立新的想法。你將重複這個過程,直到你對問題有足夠的理解,並且能夠提出有效的解決方案。如果你覺得思考已經充分可以把 nextThoughtNeeded 設為 false 並且停止思考,如果你覺得需要更多的思考你可以隨時變更 total_thoughts 來增加步驟。", - processThoughtSchema.shape, - async (args) => { - return await processThought(args); - } - ); - - // 註冊初始化專案規範工具 - server.tool( - "init_project_rules", - "初始化專案規範,當用戶要求產生或初始化專案規範文件時呼叫該工具,如果用戶要求變更或更新專案規範也呼叫該工具", - initProjectRulesSchema.shape, - async () => { - return await initProjectRules(); + let parsedArgs; + switch (request.params.name) { + case "plan_task": + parsedArgs = await planTaskSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await planTask(parsedArgs.data); + case "analyze_task": + parsedArgs = await analyzeTaskSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await analyzeTask(parsedArgs.data); + case "reflect_task": + parsedArgs = await reflectTaskSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await reflectTask(parsedArgs.data); + case "split_tasks": + parsedArgs = await splitTasksSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await splitTasks(parsedArgs.data); + case "list_tasks": + parsedArgs = await listTasksSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await listTasks(parsedArgs.data); + case "execute_task": + parsedArgs = await executeTaskSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await executeTask(parsedArgs.data); + case "verify_task": + parsedArgs = await verifyTaskSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await verifyTask(parsedArgs.data); + case "complete_task": + parsedArgs = await completeTaskSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await completeTask(parsedArgs.data); + case "delete_task": + parsedArgs = await deleteTaskSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await deleteTask(parsedArgs.data); + case "clear_all_tasks": + parsedArgs = await clearAllTasksSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await clearAllTasks(parsedArgs.data); + case "update_task": + parsedArgs = await updateTaskContentSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await updateTaskContent(parsedArgs.data); + case "update_task_files": + parsedArgs = await updateTaskRelatedFilesSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await updateTaskRelatedFiles(parsedArgs.data); + case "query_task": + parsedArgs = await queryTaskSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await queryTask(parsedArgs.data); + case "get_task_detail": + parsedArgs = await getTaskDetailSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await getTaskDetail(parsedArgs.data); + case "process_thought": + parsedArgs = await processThoughtSchema.safeParseAsync( + request.params.arguments + ); + if (!parsedArgs.success) { + throw new Error( + `Invalid arguments for tool ${request.params.name}: ${parsedArgs.error.message}` + ); + } + return await processThought(parsedArgs.data); + case "init_project_rules": + return await initProjectRules(); + default: + throw new Error(`工具 ${request.params.name} 不存在`); + } + } catch (error) { + console.error("Error executing tool:", error); + const errorMsg = + error instanceof Error ? error.message : String(error); + return { + content: [ + { + type: "text", + text: `發生錯誤: ${errorMsg} \n 請嘗試修正錯誤並重新嘗試呼叫工具`, + }, + ], + }; + } } ); diff --git a/src/prompts/templates/reflectTask.ts b/src/prompts/templates/reflectTask.ts index d8ce715..c9cdec9 100644 --- a/src/prompts/templates/reflectTask.ts +++ b/src/prompts/templates/reflectTask.ts @@ -46,7 +46,8 @@ export const knowledgeTransferTemplate = `## 知識傳遞機制 // 任務過多處理模板 export const taskOverflowTemplate = `## split_tasks 任務太多或內容過長導致「split_tasks」工具無法正常運作時 - 請使用多次使用「split_tasks」工具,每次只提交一小部分任務 -- 如果每次只新增一個任務還是無法正常運作,請考慮再次拆分任務,或者簡化任務但必須保留核心內容`; +- 如果每次只新增一個任務還是無法正常運作,請考慮再次拆分任務,或者簡化任務但必須保留核心內容 +- **嚴重警告** 你每次呼叫 split_tasks 傳遞的參數不能超過8000個字,如果超出 8000 個字請多次呼叫工具完成`; // 結尾提醒模板 export const conclusionTemplate = `請嚴格審查方案,確保解決方案質量。`; diff --git a/src/prompts/templates/splitTasks.ts b/src/prompts/templates/splitTasks.ts index 8b0668a..112e84a 100644 --- a/src/prompts/templates/splitTasks.ts +++ b/src/prompts/templates/splitTasks.ts @@ -43,4 +43,4 @@ export const dependencyManagementContent4 = `- 平衡關鍵路徑,優化並行 // 決策點模板 export const decisionPointsTemplate = `## 決策點\n\n`; export const decisionPointContent1 = `- 發現任務拆分不合理:重新呼叫「split_tasks」調整\n`; -export const decisionPointContent2 = `- 確認任務拆分完善:生成執行計劃,確定優先順序\n`; +export const decisionPointContent2 = `- 確認任務拆分完善:生成執行計劃,確定優先順序\n- **嚴重警告** 你每次呼叫 split_tasks 傳遞的參數不能超過8000個字,如果超出 8000 個字請多次呼叫工具完成`;