UNPKG

@physihan/deepwiki-mcp

Version:

DeepWiki MCP server implementation

174 lines (173 loc) 7.3 kB
#!/usr/bin/env node import { FastMCP } from "fastmcp"; // src/server.ts import { z } from "zod"; import { askRepository, searchDeepWiki } from "./tools.js"; // 定义HTTP端口 const HTTP_PORT = 3456; // 异步函数来初始化 MCP 服务器 export async function setupMCPServer(options = { transportType: "stdio" }) { try { // 动态导入 FastMCP // 创建 FastMCP 服务器 const server = new FastMCP({ name: "DeepWiki MCP Server", version: "1.0.0", }); // 添加 search_deepwiki 工具 server.addTool({ name: "search_deepwiki", description: "Search for GitHub repository information using a single keyword, which must be the exact name of a GitHub repository. Return a list of matching repositories. Let the LLM select the most relevant repository based on the keyword and provide the full repository name (in the format 'owner/repo') for use with the ask_repository tool.", parameters: z.object({ keyword: z.string().describe("The keyword to search for."), }), execute: async (args, { reportProgress, log }) => { // 记录查询开始 log.info("Starting DeepWiki repository search", { keyword: args.keyword }); // 报告初始进度 reportProgress({ progress: 0, total: 100 }); const params = { keyword: args.keyword }; // 模拟搜索延迟 await new Promise((resolve) => setTimeout(resolve, 500)); reportProgress({ progress: 30, total: 100 }); // 执行搜索 const result = await searchDeepWiki({ params }); log.info("result", { data: result, }); // 报告搜索完成 reportProgress({ progress: 100, total: 100 }); // 记录查询结果 if (result.success && result.data) { const data = result.data; const repos = data.results || []; log.info("Search completed successfully", { resultsCount: repos.length, keyword: args.keyword, }); // 每个仓库作为单独的消息发送,演示流式输出 if (repos.length > 0) { return { content: repos.map((repo) => ({ type: "text", text: JSON.stringify(repo), })), }; } log.info("No repositories found", { keyword: args.keyword }); return { content: [{ type: "text", text: "No matching repositories found." }], }; } // 记录错误 log.error("Search failed", { error: result.error?.message || "Unknown error", keyword: args.keyword, }); return { content: [ { type: "text", text: `Error: ${result.error?.message || "Unknown error"}` }, ], }; }, }); // 添加 ask_repository 工具 server.addTool({ name: "ask_repository", description: "Ask a question to a DeepWiki repository and get the complete answer.", parameters: z.object({ repo: z.string().describe("The name of the repository to query (e.g., 'owner/repo')."), question: z.string().optional().describe("The question to ask about the repository."), }), execute: async (args, { log }) => { // 设置默认问题 const question = args.question || "这是一个默认问题"; try { // 记录提问开始 log.info("Processing question", { repositoryName: args.repo, question: question, }); // 调用 askRepository 工具,获取完整回答 const params = { questionId: args.repo, question }; log.debug("Calling askRepository", { params }); // 调用工具获取结果 const result = await askRepository({ params }); if (!result.success || !result.data) { throw new Error(result.error?.message || "Failed to get answer"); } // 提取回答 let answer = ""; if (typeof result.data === "object" && "data" in result.data && result.data.data && typeof result.data.data === "object" && "answer" in result.data.data) { answer = String(result.data.data.answer); } else { log.warn("Unexpected response format", { result }); throw new Error("Unable to extract answer from response"); } return { content: [{ type: "text", text: answer }], }; } catch (error) { log.error("Error generating answer", { error: error.message, repositoryName: args.repo, }); return { content: [{ type: "text", text: `Error: ${error.message}` }], }; } }, }); // 启动 MCP 服务器 if (options.transportType === "stdio") { await server.start({ transportType: "stdio", }); } else if (options.transportType === "httpStream") { await server.start({ transportType: "httpStream", httpStream: { endpoint: "/stream", port: HTTP_PORT, }, }); } else if (options.transportType === "sse") { await server.start({ transportType: "sse", sse: { endpoint: "/sse", port: HTTP_PORT, }, }); console.log(`MCP Server started in SSE mode. Listening on http://localhost:${HTTP_PORT}/sse`); } return server; } catch (error) { console.error("Failed to initialize MCP server:", error); throw error; } } // 从 DeepWiki ID 中提取仓库名称 function extractRepoName(deepwikiId) { try { // 格式:v1.9.9.5/PUBLIC/gxr404/go-deepwiki/6c753797 const parts = deepwikiId.split("/"); if (parts.length >= 4) { return `${parts[2]}/${parts[3]}`; } return "unknown"; } catch (error) { return "unknown"; } } setupMCPServer();