#!/usr/bin/env node
/**
* CodeMind MCP Server
* @version 0.4.2
* @description A stable MCP server for GitCode and Mindmap generation, refactored to use McpServer.
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { gitcodeRequest } from "./gitcode.js";
import { generateReadmeMindmap } from "./mindmap.js";
const SERVER_VERSION = '3.0.0';
// Define Zod schemas for tool inputs for robust validation
const GitcodeArgsSchema = z.object({
baseUrl: z.string().optional(),
token: z.string(),
method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
path: z.string(),
query: z.record(z.any()).optional(),
body: z.record(z.any()).optional(),
headers: z.record(z.string()).optional(),
});
const ReadmeMindmapArgsSchema = z.object({
prompt: z.string().optional(),
});
const GitRepoArgsSchema = z.object({
repoUrl: z.string(),
targetDir: z.string().optional(),
});
class CodeMindMCPServer {
private server: McpServer;
constructor() {
console.log(`[CodeMind] Initializing server v${SERVER_VERSION}...`);
this.server = new McpServer({
name: '@lucianaib/codemind-mcp',
version: SERVER_VERSION,
// CRITICAL: This capability flag is required by the SDK version to enable tools.
capabilities: {
tools: true,
},
});
this.registerTools();
}
private registerTools(): void {
// Tool 1: gitcode_request
this.server.registerTool(
"gitcode_request",
{
description: "调用GitCode API的通用工具",
inputSchema: GitcodeArgsSchema.shape,
},
async (args: z.infer<typeof GitcodeArgsSchema>) => {
try {
console.log(`[CodeMind] 调用工具 'gitcode_request' 处理路径: ${args.path}`);
// 检查路径格式,确保以/开头
let path = args.path;
if (!path.startsWith('/')) {
path = '/' + path;
}
// 针对常见的仓库访问错误提供更好的错误提示
if (path.includes('/repos/') && !path.includes('?')) {
// 这是一个仓库查询,检查可能的错误
const pathParts = path.split('/');
if (pathParts.length >= 4 && pathParts[2] === 'repos') {
const owner = pathParts[3];
const repo = pathParts[4];
console.log(`[CodeMind] 访问仓库: ${owner}/${repo}`);
}
}
const result = await gitcodeRequest({
...args,
path: path
});
// 如果是404错误,提供更有用的错误信息
if (result.status === 404) {
return {
isError: true,
content: [{ type: "text", text: `GitCode API 返回404错误:仓库不存在或路径不正确\n\n请检查:\n1. 仓库URL是否正确\n2. 仓库名称是否拼写正确\n3. 是否有访问权限\n\n请求URL: ${result.url}\n错误详情: ${JSON.stringify(result.data, null, 2)}` }],
};
}
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
} catch (error) {
console.error(`[CodeMind] 执行 'gitcode_request' 时出错:`, error);
return {
isError: true,
content: [{ type: "text", text: `GitCode 请求失败: ${error instanceof Error ? error.message : String(error)}` }],
};
}
}
);
// Tool 2: mindmap
this.server.registerTool(
"mindmap",
{
description: "对当前项目的README.md文件制作思维导图",
inputSchema: ReadmeMindmapArgsSchema.shape,
},
async (args: z.infer<typeof ReadmeMindmapArgsSchema>) => {
try {
console.log(`[CodeMind] 调用工具 'mindmap' 生成README思维导图`);
const result = await generateReadmeMindmap();
const text = `思维导图链接: ${result.mindmapUrl}\n\n${result.message}`;
return { content: [{ type: "text", text }] };
} catch (error) {
console.error(`[CodeMind] 执行 'mindmap' 时出错:`, error);
return {
isError: true,
content: [{ type: "text", text: `README思维导图生成失败: ${error instanceof Error ? error.message : String(error)}` }],
};
}
}
);
// Tool 3: git_project_info
this.server.registerTool(
"git_project_info",
{
description: "获取Git项目信息,支持不同Git平台",
inputSchema: GitRepoArgsSchema.shape,
},
async (args: z.infer<typeof GitRepoArgsSchema>) => {
try {
console.log(`[CodeMind] 调用工具 'git_project_info' 获取项目信息: ${args.repoUrl}`);
// 解析仓库URL,提取平台、所有者和仓库名
let platform = "";
let owner = "";
let repo = "";
let apiUrl = "";
try {
// 尝试匹配不同的Git平台URL格式
const githubMatch = args.repoUrl.match(/https?:\/\/(?:www\.)?github\.com\/([^\/]+)\/([^\/\.]+)/);
const gitcodeMatch = args.repoUrl.match(/https?:\/\/(?:www\.)?([^\/]+)\/([^\/]+)\/([^\/\.]+)/);
if (githubMatch) {
[, owner, repo] = githubMatch;
platform = "GitHub";
apiUrl = `https://api.github.com/repos/${owner}/${repo}`;
} else if (gitcodeMatch) {
let domain = "";
[, domain, owner, repo] = gitcodeMatch;
platform = domain;
if (domain === "gitcode.net") {
apiUrl = `https://gitcode.net/api/v5/repos/${owner}/${repo}`;
} else {
apiUrl = `https://gitcode.com/api/v5/repos/${owner}/${repo}`;
}
} else {
throw new Error("不支持的Git平台URL格式");
}
// 尝试获取仓库信息
let result;
if (platform === "GitHub") {
// GitHub API需要不同的处理方式
const response = await fetch(apiUrl);
const data = await response.json();
result = {
status: response.status,
ok: response.ok,
url: apiUrl,
data
};
} else {
// GitCode平台使用gitcodeRequest
result = await gitcodeRequest({
token: "", // 公开仓库不需要token
method: "GET",
path: `/repos/${owner}/${repo}`,
baseUrl: apiUrl.includes('/api/v5') ? apiUrl.replace(/\/repos\/[^\/]+\/[^\/]+$/, '') : apiUrl
});
}
if (result.status === 404) {
// 尝试直接使用URL作为API路径
const apiPath = `/repos/${owner}/${repo}`;
let baseApiUrl = "";
if (platform === "GitHub") {
baseApiUrl = "https://api.github.com";
} else if (platform.includes("gitcode")) {
baseApiUrl = `https://${platform}/api/v5`;
} else {
baseApiUrl = apiUrl.replace(/\/repos\/[^\/]+\/[^\/]+$/, "");
}
const retryResult = await gitcodeRequest({
token: "",
method: "GET",
path: apiPath,
baseUrl: baseApiUrl
});
if (retryResult.ok) {
return { content: [{ type: "text", text: `平台: ${platform}\n所有者: ${owner}\n仓库名: ${repo}\n仓库信息:\n${JSON.stringify(retryResult.data, null, 2)}` }] };
} else {
throw new Error(`仓库不存在或无法访问: ${args.repoUrl}`);
}
}
return { content: [{ type: "text", text: `平台: ${platform}\n所有者: ${owner}\n仓库名: ${repo}\n仓库信息:\n${JSON.stringify(result.data, null, 2)}` }] };
} catch (parseError) {
throw new Error(`无法解析仓库URL: ${args.repoUrl}\n错误: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
}
} catch (error) {
console.error(`[CodeMind] 执行 'git_project_info' 时出错:`, error);
return {
isError: true,
content: [{ type: "text", text: `获取项目信息失败: ${error instanceof Error ? error.message : String(error)}` }],
};
}
}
);
}
public async run(): Promise<void> {
const transport = new StdioServerTransport();
process.on('SIGINT', () => {
console.log('[CodeMind] SIGINT received, shutting down.');
this.server.close();
process.exit(0);
});
await this.server.connect(transport);
console.error(`CodeMind MCP Server v${SERVER_VERSION} is running successfully.`);
}
}
// Main execution block
try {
const server = new CodeMindMCPServer();
server.run();
} catch (error) {
console.error('Failed to initialize or run the server:', error);
process.exit(1);
}