@physihan/deepwiki-mcp
Version:
DeepWiki MCP server implementation
174 lines (173 loc) • 7.3 kB
JavaScript
#!/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();