2025-04-11 15:45:35 +08:00
|
|
|
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
|
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
|
|
|
import { z } from "zod";
|
2025-04-11 18:02:56 +08:00
|
|
|
|
import { RelatedFileType } from "./types/index.js";
|
2025-04-11 15:45:35 +08:00
|
|
|
|
|
|
|
|
|
// 導入工具函數
|
|
|
|
|
import {
|
|
|
|
|
planTask,
|
|
|
|
|
planTaskSchema,
|
|
|
|
|
analyzeTask,
|
|
|
|
|
analyzeTaskSchema,
|
|
|
|
|
reflectTask,
|
|
|
|
|
reflectTaskSchema,
|
|
|
|
|
splitTasks,
|
|
|
|
|
splitTasksSchema,
|
|
|
|
|
listTasks,
|
|
|
|
|
executeTask,
|
|
|
|
|
executeTaskSchema,
|
|
|
|
|
verifyTask,
|
|
|
|
|
verifyTaskSchema,
|
|
|
|
|
completeTask,
|
|
|
|
|
completeTaskSchema,
|
2025-04-11 16:17:55 +08:00
|
|
|
|
deleteTask,
|
|
|
|
|
deleteTaskSchema,
|
2025-04-11 18:02:56 +08:00
|
|
|
|
clearAllTasks,
|
|
|
|
|
clearAllTasksSchema,
|
|
|
|
|
updateTaskContent,
|
|
|
|
|
updateTaskContentSchema,
|
|
|
|
|
updateTaskRelatedFiles,
|
|
|
|
|
updateTaskRelatedFilesSchema,
|
2025-04-11 15:45:35 +08:00
|
|
|
|
} from "./tools/taskTools.js";
|
|
|
|
|
|
2025-04-11 16:51:29 +08:00
|
|
|
|
// 導入日誌工具函數
|
|
|
|
|
import {
|
|
|
|
|
listConversationLog,
|
|
|
|
|
listConversationLogSchema,
|
|
|
|
|
clearConversationLog,
|
|
|
|
|
clearConversationLogSchema,
|
|
|
|
|
} from "./tools/logTools.js";
|
|
|
|
|
|
2025-04-11 15:45:35 +08:00
|
|
|
|
async function main() {
|
|
|
|
|
try {
|
|
|
|
|
console.log("啟動蝦米任務管理器服務...");
|
|
|
|
|
|
|
|
|
|
// 創建MCP服務器
|
|
|
|
|
const server = new McpServer({
|
|
|
|
|
name: "蝦米任務管理器",
|
|
|
|
|
version: "1.0.0",
|
|
|
|
|
});
|
|
|
|
|
|
2025-04-12 00:17:05 +08:00
|
|
|
|
// 註冊工具 - 使用已定義的schema物件,並添加內嵌錯誤處理
|
2025-04-11 15:45:35 +08:00
|
|
|
|
server.tool(
|
|
|
|
|
"plan_task",
|
|
|
|
|
"初始化並詳細規劃任務流程,建立明確的目標與成功標準",
|
|
|
|
|
{
|
|
|
|
|
description: z
|
|
|
|
|
.string()
|
2025-04-12 00:17:05 +08:00
|
|
|
|
.min(10, {
|
|
|
|
|
message:
|
|
|
|
|
"任務描述不能少於10個字符,請提供更詳細的描述以確保任務目標明確",
|
|
|
|
|
})
|
2025-04-11 15:45:35 +08:00
|
|
|
|
.describe("完整詳細的任務問題描述,應包含任務目標、背景及預期成果"),
|
|
|
|
|
requirements: z
|
|
|
|
|
.string()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe("任務的特定技術要求、業務約束條件或品質標準(選填)"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await planTask(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
server.tool(
|
|
|
|
|
"analyze_task",
|
|
|
|
|
"深入分析任務需求並系統性檢查代碼庫,評估技術可行性與潛在風險",
|
|
|
|
|
{
|
|
|
|
|
summary: z
|
|
|
|
|
.string()
|
2025-04-12 00:17:05 +08:00
|
|
|
|
.min(20, {
|
|
|
|
|
message:
|
|
|
|
|
"任務摘要太短,請提供更詳細的摘要,包含任務目標、範圍與關鍵技術挑戰",
|
|
|
|
|
})
|
2025-04-11 15:45:35 +08:00
|
|
|
|
.describe("結構化的任務摘要,包含任務目標、範圍與關鍵技術挑戰"),
|
|
|
|
|
initialConcept: z
|
|
|
|
|
.string()
|
2025-04-12 00:17:05 +08:00
|
|
|
|
.min(50, {
|
|
|
|
|
message:
|
|
|
|
|
"初步解答構想過於簡短,請提供更完整的技術方案和實施策略詳情",
|
|
|
|
|
})
|
2025-04-11 15:45:35 +08:00
|
|
|
|
.describe("初步解答構想,包含技術方案、架構設計和實施策略"),
|
|
|
|
|
previousAnalysis: z
|
|
|
|
|
.string()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe(
|
|
|
|
|
"前次迭代的分析結果,用於持續改進方案(僅在重新分析時需提供)"
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await analyzeTask(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
server.tool(
|
|
|
|
|
"reflect_task",
|
|
|
|
|
"批判性審查分析結果,評估方案完整性並識別優化機會,確保解決方案符合最佳實踐",
|
|
|
|
|
{
|
|
|
|
|
summary: z
|
|
|
|
|
.string()
|
2025-04-12 00:17:05 +08:00
|
|
|
|
.min(20, {
|
|
|
|
|
message:
|
|
|
|
|
"任務摘要太短,請確保包含完整的任務目標和範圍以維持分析連續性",
|
|
|
|
|
})
|
2025-04-11 15:45:35 +08:00
|
|
|
|
.describe("結構化的任務摘要,保持與分析階段一致以確保連續性"),
|
|
|
|
|
analysis: z
|
|
|
|
|
.string()
|
2025-04-12 00:17:05 +08:00
|
|
|
|
.min(100, {
|
|
|
|
|
message:
|
|
|
|
|
"技術分析結果過於簡略,請提供更詳盡的技術細節、依賴組件和實施方案說明",
|
|
|
|
|
})
|
2025-04-11 15:45:35 +08:00
|
|
|
|
.describe(
|
|
|
|
|
"完整詳盡的技術分析結果,包括所有技術細節、依賴組件和實施方案"
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await reflectTask(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
server.tool(
|
|
|
|
|
"split_tasks",
|
2025-04-12 02:01:28 +08:00
|
|
|
|
"將複雜任務分解為獨立且可追蹤的子任務,建立明確的依賴關係和優先順序,dependencies 是一個字串陣列,支援任務名稱或任務ID(UUID)",
|
2025-04-11 15:45:35 +08:00
|
|
|
|
{
|
|
|
|
|
isOverwrite: z
|
|
|
|
|
.boolean()
|
|
|
|
|
.describe(
|
|
|
|
|
"任務覆蓋模式選擇(true:清除並覆蓋所有現有任務;false:保留現有任務並新增)"
|
|
|
|
|
),
|
|
|
|
|
tasks: z
|
|
|
|
|
.array(
|
|
|
|
|
z.object({
|
|
|
|
|
name: z
|
|
|
|
|
.string()
|
2025-04-12 00:17:05 +08:00
|
|
|
|
.max(100, {
|
|
|
|
|
message: "任務名稱過長,請保持簡潔,不超過100個字符",
|
|
|
|
|
})
|
2025-04-11 15:45:35 +08:00
|
|
|
|
.describe("簡潔明確的任務名稱,應能清晰表達任務目的"),
|
|
|
|
|
description: z
|
|
|
|
|
.string()
|
2025-04-12 01:06:17 +08:00
|
|
|
|
.min(10, {
|
2025-04-12 00:17:05 +08:00
|
|
|
|
message:
|
|
|
|
|
"任務描述太短,請詳細說明實施要點、技術細節和驗收標準",
|
|
|
|
|
})
|
2025-04-11 15:45:35 +08:00
|
|
|
|
.describe("詳細的任務描述,包含實施要點、技術細節和驗收標準"),
|
|
|
|
|
notes: z
|
|
|
|
|
.string()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe("補充說明、特殊處理要求或實施建議(選填)"),
|
|
|
|
|
dependencies: z
|
2025-04-12 02:01:28 +08:00
|
|
|
|
.array(z.string(), {
|
|
|
|
|
message: "必須是字串陣列,支援任務名稱或任務ID(UUID)",
|
|
|
|
|
})
|
2025-04-11 15:45:35 +08:00
|
|
|
|
.optional()
|
|
|
|
|
.describe(
|
2025-04-12 02:01:28 +08:00
|
|
|
|
"此任務依賴的前置任務ID或任務名稱列表,支持兩種引用方式,名稱引用更直觀,是一個字串陣列"
|
2025-04-11 15:45:35 +08:00
|
|
|
|
),
|
2025-04-12 02:01:28 +08:00
|
|
|
|
relatedFiles: z
|
|
|
|
|
.array(
|
|
|
|
|
z.object({
|
|
|
|
|
path: z
|
|
|
|
|
.string()
|
|
|
|
|
.min(1, { message: "檔案路徑不能為空" })
|
|
|
|
|
.describe("檔案路徑,相對於專案根目錄"),
|
|
|
|
|
type: z
|
|
|
|
|
.nativeEnum(RelatedFileType)
|
|
|
|
|
.describe("檔案類型,用於區分不同類型的檔案"),
|
|
|
|
|
description: z
|
|
|
|
|
.string()
|
|
|
|
|
.min(1, { message: "檔案描述不能為空" })
|
|
|
|
|
.describe("檔案描述,用於說明檔案的用途和內容"),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
.optional()
|
|
|
|
|
.describe("與任務相關的檔案列表,包含檔案路徑、類型和描述"),
|
2025-04-11 15:45:35 +08:00
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
.describe("結構化的任務清單,每個任務應保持原子性且有明確的完成標準"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await splitTasks(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
server.tool(
|
|
|
|
|
"list_tasks",
|
|
|
|
|
"生成結構化任務清單,包含完整狀態追蹤、優先級和依賴關係",
|
|
|
|
|
{},
|
2025-04-12 00:17:05 +08:00
|
|
|
|
async (args) => {
|
2025-04-11 15:45:35 +08:00
|
|
|
|
return await listTasks();
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
server.tool(
|
|
|
|
|
"execute_task",
|
|
|
|
|
"按照預定義計劃執行特定任務,確保每個步驟的輸出符合質量標準",
|
|
|
|
|
{
|
|
|
|
|
taskId: z
|
|
|
|
|
.string()
|
|
|
|
|
.describe("待執行任務的唯一標識符,必須是系統中存在的有效任務ID"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await executeTask(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
server.tool(
|
|
|
|
|
"verify_task",
|
|
|
|
|
"全面驗證任務完成度,確保所有需求與技術標準都已滿足,並無遺漏細節",
|
|
|
|
|
{
|
|
|
|
|
taskId: z
|
|
|
|
|
.string()
|
|
|
|
|
.describe("待驗證任務的唯一標識符,必須是系統中存在的有效任務ID"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await verifyTask(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
server.tool(
|
|
|
|
|
"complete_task",
|
|
|
|
|
"正式標記任務為完成狀態,生成詳細的完成報告,並更新關聯任務的依賴狀態",
|
|
|
|
|
{
|
|
|
|
|
taskId: z
|
|
|
|
|
.string()
|
|
|
|
|
.describe("待完成任務的唯一標識符,必須是系統中存在的有效任務ID"),
|
2025-04-11 16:57:05 +08:00
|
|
|
|
summary: z
|
|
|
|
|
.string()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe(
|
|
|
|
|
"任務完成摘要,簡潔描述實施結果和重要決策(選填,如未提供將自動生成)"
|
|
|
|
|
),
|
2025-04-11 15:45:35 +08:00
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await completeTask(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2025-04-11 16:17:55 +08:00
|
|
|
|
server.tool(
|
|
|
|
|
"delete_task",
|
|
|
|
|
"刪除未完成的任務,但不允許刪除已完成的任務,確保系統記錄的完整性",
|
|
|
|
|
{
|
|
|
|
|
taskId: z
|
|
|
|
|
.string()
|
|
|
|
|
.describe("待刪除任務的唯一標識符,必須是系統中存在且未完成的任務ID"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await deleteTask(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2025-04-11 18:02:56 +08:00
|
|
|
|
// 註冊清除所有任務工具
|
|
|
|
|
server.tool(
|
|
|
|
|
"clear_all_tasks",
|
|
|
|
|
"刪除系統中所有未完成的任務,該指令必須由用戶明確確認才能執行",
|
|
|
|
|
{
|
|
|
|
|
confirm: z
|
|
|
|
|
.boolean()
|
|
|
|
|
.describe("確認刪除所有未完成的任務(此操作不可逆)"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await clearAllTasks(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 註冊更新任務工具
|
|
|
|
|
server.tool(
|
|
|
|
|
"update_task",
|
|
|
|
|
"更新任務內容,包括名稱、描述和注記,但不允許修改已完成的任務",
|
|
|
|
|
{
|
|
|
|
|
taskId: z
|
|
|
|
|
.string()
|
|
|
|
|
.describe("待更新任務的唯一標識符,必須是系統中存在且未完成的任務ID"),
|
|
|
|
|
name: z.string().optional().describe("任務的新名稱(選填)"),
|
|
|
|
|
description: z.string().optional().describe("任務的新描述內容(選填)"),
|
|
|
|
|
notes: z.string().optional().describe("任務的新補充說明(選填)"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await updateTaskContent(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 註冊更新任務相關文件工具
|
|
|
|
|
server.tool(
|
|
|
|
|
"update_task_files",
|
|
|
|
|
"更新任務相關文件列表,用於記錄與任務相關的代碼文件、參考資料等",
|
|
|
|
|
{
|
|
|
|
|
taskId: z
|
|
|
|
|
.string()
|
|
|
|
|
.describe("待更新任務的唯一標識符,必須是系統中存在且未完成的任務ID"),
|
|
|
|
|
relatedFiles: z
|
|
|
|
|
.array(
|
|
|
|
|
z.object({
|
|
|
|
|
path: z
|
|
|
|
|
.string()
|
|
|
|
|
.describe("文件路徑,可以是相對於項目根目錄的路徑或絕對路徑"),
|
|
|
|
|
type: z
|
|
|
|
|
.enum([
|
|
|
|
|
RelatedFileType.TO_MODIFY,
|
|
|
|
|
RelatedFileType.REFERENCE,
|
|
|
|
|
RelatedFileType.OUTPUT,
|
|
|
|
|
RelatedFileType.DEPENDENCY,
|
|
|
|
|
RelatedFileType.OTHER,
|
|
|
|
|
])
|
|
|
|
|
.describe("文件與任務的關係類型"),
|
|
|
|
|
description: z
|
|
|
|
|
.string()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe("文件的補充描述(選填)"),
|
|
|
|
|
lineStart: z
|
|
|
|
|
.number()
|
|
|
|
|
.int()
|
|
|
|
|
.positive()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe("相關代碼區塊的起始行(選填)"),
|
|
|
|
|
lineEnd: z
|
|
|
|
|
.number()
|
|
|
|
|
.int()
|
|
|
|
|
.positive()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe("相關代碼區塊的結束行(選填)"),
|
|
|
|
|
})
|
|
|
|
|
)
|
|
|
|
|
.describe("與任務相關的文件列表"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await updateTaskRelatedFiles(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2025-04-11 16:51:29 +08:00
|
|
|
|
// 註冊日誌查詢工具
|
|
|
|
|
server.tool(
|
|
|
|
|
"list_conversation_log",
|
|
|
|
|
"查詢系統對話日誌,支持按任務 ID 或時間範圍過濾,提供分頁功能處理大量記錄",
|
|
|
|
|
{
|
|
|
|
|
taskId: z
|
|
|
|
|
.string()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe("按任務 ID 過濾對話記錄(選填)"),
|
|
|
|
|
startDate: z
|
|
|
|
|
.string()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe("起始日期過濾,格式為 ISO 日期字串(選填)"),
|
|
|
|
|
endDate: z
|
|
|
|
|
.string()
|
|
|
|
|
.optional()
|
|
|
|
|
.describe("結束日期過濾,格式為 ISO 日期字串(選填)"),
|
|
|
|
|
limit: z
|
|
|
|
|
.number()
|
|
|
|
|
.int()
|
|
|
|
|
.positive()
|
|
|
|
|
.max(100)
|
|
|
|
|
.default(20)
|
|
|
|
|
.describe("返回結果數量限制,最大 100(預設:20)"),
|
|
|
|
|
offset: z
|
|
|
|
|
.number()
|
|
|
|
|
.int()
|
|
|
|
|
.nonnegative()
|
|
|
|
|
.default(0)
|
|
|
|
|
.describe("分頁偏移量(預設:0)"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await listConversationLog(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 註冊日誌清除工具
|
|
|
|
|
server.tool(
|
|
|
|
|
"clear_conversation_log",
|
|
|
|
|
"清除所有對話日誌記錄,需要明確確認以避免意外操作",
|
|
|
|
|
{
|
|
|
|
|
confirm: z.boolean().describe("確認刪除所有日誌記錄(此操作不可逆)"),
|
|
|
|
|
},
|
|
|
|
|
async (args) => {
|
|
|
|
|
return await clearConversationLog(args);
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2025-04-11 15:45:35 +08:00
|
|
|
|
// 建立連接
|
|
|
|
|
const transport = new StdioServerTransport();
|
|
|
|
|
await server.connect(transport);
|
|
|
|
|
|
|
|
|
|
console.log("蝦米任務管理器服務已啟動");
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("啟動服務失敗:", error);
|
|
|
|
|
process.exit(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main().catch(console.error);
|