Refactor GitLab MCP server implementation
- Removed gitlab-index.js and integrated its functionality into index.js. - Updated server name to "gitlab-mcp-server" and version to "1.1.0". - Enhanced GitLab API client with methods for creating, merging, retrieving, and closing merge requests. - Added tools for handling merge requests with detailed input validation using Zod. - Implemented command-line argument parsing for transport mode (stdio or http) and port configuration. - Introduced Streamable HTTP transport for remote access and debugging. - Updated package dependencies to the latest versions.
This commit is contained in:
1 parent
09d624a464
commit
347096b2e1
5 files changed
+1060
-663
No files matched your search
@@ -1,113 +1,587 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// 引入必要的模块
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
||||
import { z } from "zod";
|
||||
|
||||
// 创建 MCP 服务器实例
|
||||
// 为其设置一个名字和版本
|
||||
const server = new McpServer({
|
||||
name: "my-first-nodejs-mcp-server",
|
||||
name: "gitlab-mcp-server",
|
||||
version: "1.0.0",
|
||||
});
|
||||
|
||||
/**
|
||||
* 完善版的打招呼工具
|
||||
* 1. 使用 registerTool 方法进行更完整的描述
|
||||
* 2. 利用 Zod 库定义严格的参数模式和验证规则
|
||||
* GitLab API 客户端配置
|
||||
* 用于连接到GitLab实例并执行操作
|
||||
*/
|
||||
server.registerTool('say_hello',
|
||||
const gitlabApiClient = {
|
||||
baseUrl: process.env.GITLAB_API_BASE_URL || "https://gitlab.com/api/v4",
|
||||
token: process.env.GITLAB_ACCESS_TOKEN,
|
||||
|
||||
async makeRequest(endpoint, options = {}) {
|
||||
if (!this.token) {
|
||||
throw new Error("GITLAB_ACCESS_TOKEN 环境变量未设置");
|
||||
}
|
||||
|
||||
const url = `${this.baseUrl}${endpoint}`;
|
||||
const defaultOptions = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"PRIVATE-TOKEN": this.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
};
|
||||
|
||||
const requestOptions = { ...defaultOptions, ...options };
|
||||
const response = await fetch(url, requestOptions);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`GitLab API请求失败: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
},
|
||||
|
||||
/**
|
||||
* 创建Merge Request
|
||||
*/
|
||||
async createMergeRequest(projectId, sourceBranch, targetBranch, title, description = "", assigneeId, reviewerIds) {
|
||||
const body = {
|
||||
id: projectId,
|
||||
source_branch: sourceBranch,
|
||||
target_branch: targetBranch,
|
||||
title,
|
||||
description,
|
||||
...(assigneeId !== undefined && { assignee_id: assigneeId }),
|
||||
...(reviewerIds !== undefined && { reviewer_ids: reviewerIds }),
|
||||
};
|
||||
|
||||
return await this.makeRequest(
|
||||
`/projects/${projectId}/merge_requests`,
|
||||
{
|
||||
method: "POST",
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
"PRIVATE-TOKEN": this.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* 合并Merge Request
|
||||
*/
|
||||
async mergeMergeRequest(projectId, mrIid, mergeCommitMessage = "", shouldRemoveSourceBranch = false, mergeWhenPipelineSucceeds = false) {
|
||||
const body = {
|
||||
id: projectId,
|
||||
merge_request_iid: mrIid,
|
||||
...(mergeCommitMessage && { merge_commit_message: mergeCommitMessage }),
|
||||
remove_source_branch: shouldRemoveSourceBranch,
|
||||
merge_when_pipeline_succeeds: mergeWhenPipelineSucceeds,
|
||||
};
|
||||
|
||||
return await this.makeRequest(
|
||||
`/projects/${projectId}/merge_requests/${mrIid}/merge`,
|
||||
{
|
||||
method: "PUT",
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
"PRIVATE-TOKEN": this.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取Merge Request详情
|
||||
*/
|
||||
async getMergeRequest(projectId, mrIid) {
|
||||
return await this.makeRequest(`/projects/${projectId}/merge_requests/${mrIid}`);
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭Merge Request
|
||||
*/
|
||||
async closeMergeRequest(projectId, mrIid) {
|
||||
return await this.makeRequest(
|
||||
`/projects/${projectId}/merge_requests/${mrIid}`,
|
||||
{
|
||||
method: "PUT",
|
||||
body: JSON.stringify({ state_event: "close" }),
|
||||
headers: {
|
||||
"PRIVATE-TOKEN": this.token,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建Merge Request工具
|
||||
*/
|
||||
server.registerTool(
|
||||
"create_merge_request",
|
||||
{
|
||||
// 工具描述至关重要,它直接帮助AI理解工具的用途、适用场景和参数要求
|
||||
description: "向指定的用户生成一条个性化的问候语。该工具适用于在对话开始时或需要欢迎用户时使用。",
|
||||
// 使用 Zod 定义参数模式,这是确保AI正确传参的关键[8](@ref)
|
||||
description: "在指定的GitLab项目中创建一个新的Merge Request",
|
||||
inputSchema: z.object({
|
||||
// 第一个参数:用户名
|
||||
name: z.string()
|
||||
.min(1, { message: "姓名不能为空字符串。" }) // 基础验证:确保非空字符串
|
||||
.max(50, { message: "姓名长度不能超过50个字符。" }) // 防止过长输入
|
||||
.describe("被问候者的姓名。请尽量提供完整的姓名或常用称呼。"), // 清晰的参数描述
|
||||
// 第二个参数(可选):语言/风格
|
||||
language: z.enum(["casual", "formal", "warm"]) // 枚举类型,将AI的输入严格限制在预设的选项内
|
||||
.default("casual") // 设置默认值,当AI未提供此参数时使用
|
||||
.describe("问候的语言风格。'casual'为随意,'formal'为正式,'warm'为热情。默认为'casual'。"),
|
||||
projectId: z.string().describe("GitLab项目的ID或URL编码的路径"),
|
||||
sourceBranch: z.string().describe("源分支名称"),
|
||||
targetBranch: z.string().describe("目标分支名称"),
|
||||
title: z.string().describe("Merge Request的标题"),
|
||||
description: z.string().optional().describe("Merge Request的描述"),
|
||||
assigneeId: z.number().optional().describe("负责人ID"),
|
||||
reviewerIds: z.array(z.number()).optional().describe("审阅者ID数组"),
|
||||
}),
|
||||
},
|
||||
async ({ name, language }) => {
|
||||
// 根据不同的语言风格生成不同的问候语
|
||||
const greetings = {
|
||||
casual: `嘿,${name}!最近怎么样?`,
|
||||
formal: `您好,${name}。很高兴见到您。`,
|
||||
warm: `欢迎你,${name}!真高兴你今天能来!`,
|
||||
};
|
||||
async ({ projectId, sourceBranch, targetBranch, title, description = "", assigneeId, reviewerIds }) => {
|
||||
try {
|
||||
const result = await gitlabApiClient.createMergeRequest(
|
||||
projectId,
|
||||
sourceBranch,
|
||||
targetBranch,
|
||||
title,
|
||||
description,
|
||||
assigneeId,
|
||||
reviewerIds
|
||||
);
|
||||
|
||||
const greetingMessage = greetings[language] || greetings.casual;
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: true,
|
||||
message: "成功创建Merge Request",
|
||||
data: {
|
||||
url: result.web_url,
|
||||
title: result.title,
|
||||
state: result.state,
|
||||
iid: result.iid,
|
||||
id: result.id
|
||||
}
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: false,
|
||||
message: "创建Merge Request失败",
|
||||
error: error.message
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// MCP 工具要求返回特定的格式
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text", // 内容类型为文本
|
||||
text: greetingMessage,
|
||||
},
|
||||
],
|
||||
};
|
||||
/**
|
||||
* 合并Merge Request工具
|
||||
*/
|
||||
server.registerTool(
|
||||
"merge_merge_request",
|
||||
{
|
||||
description: "合并指定的GitLab Merge Request",
|
||||
inputSchema: z.object({
|
||||
projectId: z.string().describe("GitLab项目的ID或URL编码的路径"),
|
||||
mrIid: z.number().describe("Merge Request的内部ID"),
|
||||
mergeCommitMessage: z.string().optional().describe("合并提交的消息"),
|
||||
shouldRemoveSourceBranch: z.boolean().optional().describe("是否在合并后删除源分支"),
|
||||
mergeWhenPipelineSucceeds: z.boolean().optional().describe("是否在CI管道成功后合并"),
|
||||
}),
|
||||
},
|
||||
async ({
|
||||
projectId,
|
||||
mrIid,
|
||||
mergeCommitMessage = "",
|
||||
shouldRemoveSourceBranch = false,
|
||||
mergeWhenPipelineSucceeds = false
|
||||
}) => {
|
||||
try {
|
||||
const result = await gitlabApiClient.mergeMergeRequest(
|
||||
projectId,
|
||||
mrIid,
|
||||
mergeCommitMessage,
|
||||
shouldRemoveSourceBranch,
|
||||
mergeWhenPipelineSucceeds
|
||||
);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: true,
|
||||
message: `成功合并Merge Request #${mrIid}`,
|
||||
data: {
|
||||
iid: result.iid,
|
||||
state: result.state,
|
||||
mergedAt: result.merged_at,
|
||||
mergedBy: result.merged_by?.name,
|
||||
id: result.id
|
||||
}
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: false,
|
||||
message: "合并Merge Request失败",
|
||||
error: error.message
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 获取Merge Request详情工具
|
||||
*/
|
||||
server.registerTool(
|
||||
"get_merge_request",
|
||||
{
|
||||
description: "获取指定Merge Request的详细信息",
|
||||
inputSchema: z.object({
|
||||
projectId: z.string().describe("GitLab项目的ID或URL编码的路径"),
|
||||
mrIid: z.number().describe("Merge Request的内部ID"),
|
||||
}),
|
||||
},
|
||||
async ({ projectId, mrIid }) => {
|
||||
try {
|
||||
const result = await gitlabApiClient.getMergeRequest(projectId, mrIid);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: true,
|
||||
message: `Merge Request #${mrIid} 详情`,
|
||||
data: {
|
||||
iid: result.iid,
|
||||
title: result.title,
|
||||
author: result.author.name,
|
||||
state: result.state,
|
||||
sourceBranch: result.source_branch,
|
||||
targetBranch: result.target_branch,
|
||||
createdAt: result.created_at,
|
||||
updatedAt: result.updated_at,
|
||||
description: result.description,
|
||||
url: result.web_url
|
||||
}
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: false,
|
||||
message: "获取Merge Request详情失败",
|
||||
error: error.message
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 关闭Merge Request工具
|
||||
*/
|
||||
server.registerTool(
|
||||
"close_merge_request",
|
||||
{
|
||||
description: "关闭指定的GitLab Merge Request而不合并",
|
||||
inputSchema: z.object({
|
||||
projectId: z.string().describe("GitLab项目的ID或URL编码的路径"),
|
||||
mrIid: z.number().describe("Merge Request的内部ID"),
|
||||
}),
|
||||
},
|
||||
async ({ projectId, mrIid }) => {
|
||||
try {
|
||||
const result = await gitlabApiClient.closeMergeRequest(projectId, mrIid);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: true,
|
||||
message: `成功关闭Merge Request #${mrIid}`,
|
||||
data: {
|
||||
iid: result.iid,
|
||||
title: result.title,
|
||||
state: result.state,
|
||||
updatedAt: result.updated_at,
|
||||
id: result.id
|
||||
}
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: false,
|
||||
message: "关闭Merge Request失败",
|
||||
error: error.message
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
server.registerTool(
|
||||
'merge_to_test',
|
||||
{
|
||||
description: "把源分支名合并到测试分支,并部署到测试环境",
|
||||
inputSchema: z.object({
|
||||
projectId: z.string().describe("GitLab项目projectId"),
|
||||
sourceBranch: z.string().describe("源分支名称,默认请使用git branch --show-current获取当前分支传递"),
|
||||
}),
|
||||
},
|
||||
|
||||
async ({ projectId, sourceBranch, }) => {
|
||||
if (!sourceBranch) {
|
||||
// 获取当前分支,用 git branch --show-current 命令
|
||||
const { execSync } = await import('child_process');
|
||||
try {
|
||||
sourceBranch = execSync('git branch --show-current', { encoding: 'utf-8' }).trim();
|
||||
} catch (error) {
|
||||
console.error('执行 git branch --show-current 命令失败:', error);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: false,
|
||||
message: "获取当前分支失败",
|
||||
error: error.message
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
try {
|
||||
const result = await gitlabApiClient.createMergeRequest(
|
||||
projectId,
|
||||
sourceBranch,
|
||||
'test',
|
||||
`Merge ${sourceBranch} to test`, // 标题
|
||||
`Automated merge request from ${sourceBranch} to test branch` // 描述
|
||||
);
|
||||
|
||||
const mrIid = result.iid;
|
||||
|
||||
// 等待3秒钟,让GitLab有时间处理新创建的MR
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
// 尝试合并MR
|
||||
try {
|
||||
const mergeResult = await gitlabApiClient.mergeMergeRequest(
|
||||
projectId,
|
||||
mrIid
|
||||
);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: true,
|
||||
message: "成功创建并合并Merge Request",
|
||||
data: {
|
||||
url: mergeResult.web_url,
|
||||
title: mergeResult.title,
|
||||
state: mergeResult.state,
|
||||
mergedAt: mergeResult.merged_at,
|
||||
iid: mergeResult.iid,
|
||||
id: mergeResult.id
|
||||
}
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (mergeError) {
|
||||
// 如果合并失败,返回MR创建成功的信息,但提示合并需要审核
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: false,
|
||||
message: "成功创建Merge Request,但自动合并失败,请手动审核",
|
||||
data: {
|
||||
url: result.web_url,
|
||||
title: result.title,
|
||||
state: result.state,
|
||||
iid: result.iid,
|
||||
id: result.id
|
||||
},
|
||||
error: mergeError.message
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify({
|
||||
success: false,
|
||||
message: "合并到test分支失败",
|
||||
error: error.message
|
||||
}, null, 2),
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* 解析命令行参数
|
||||
*/
|
||||
function parseArgs() {
|
||||
const args = process.argv.slice(2);
|
||||
const config = {
|
||||
transport: 'stdio', // 默认使用 stdio
|
||||
port: 3000, // 默认 HTTP 端口
|
||||
};
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const arg = args[i];
|
||||
|
||||
if (arg === '--transport' || arg === '-t') {
|
||||
const value = args[i + 1];
|
||||
if (value === 'stdio' || value === 'http') {
|
||||
config.transport = value;
|
||||
i++; // 跳过下一个参数
|
||||
} else {
|
||||
console.error(`错误: --transport 参数必须是 'stdio' 或 'http'`);
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (arg === '--port' || arg === '-p') {
|
||||
const value = parseInt(args[i + 1], 10);
|
||||
if (!isNaN(value) && value > 0 && value < 65536) {
|
||||
config.port = value;
|
||||
i++; // 跳过下一个参数
|
||||
} else {
|
||||
console.error(`错误: --port 参数必须是 1-65535 之间的有效端口号`);
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (arg === '--help' || arg === '-h') {
|
||||
console.log(`
|
||||
GitLab MCP 服务器
|
||||
|
||||
用法:
|
||||
node index.js [选项]
|
||||
npx gitlab-mcp-server [选项]
|
||||
|
||||
选项:
|
||||
-t, --transport <type> 传输方式: 'stdio' 或 'http' (默认: stdio)
|
||||
-p, --port <number> HTTP 服务器端口 (默认: 3000, 仅在 http 模式下有效)
|
||||
-h, --help 显示帮助信息
|
||||
|
||||
说明:
|
||||
- stdio 模式: 使用标准输入/输出(MCP 标准),适合与 MCP 客户端集成
|
||||
- http 模式: 使用 Streamable HTTP 传输,适合远程访问和调试
|
||||
|
||||
示例:
|
||||
node index.js # stdio 模式
|
||||
node index.js --transport http --port 3000 # HTTP 模式
|
||||
npx gitlab-mcp-server # stdio 模式
|
||||
npx gitlab-mcp-server -t http -p 8080 # HTTP 模式
|
||||
|
||||
环境变量:
|
||||
GITLAB_ACCESS_TOKEN GitLab 访问令牌(必需)
|
||||
GITLAB_API_BASE_URL GitLab API 基础 URL(可选,默认: https://gitlab.com/api/v4)
|
||||
`);
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动 HTTP 服务器(使用 Streamable HTTP 传输)
|
||||
*/
|
||||
async function startHttpServer(port) {
|
||||
const transport = new StreamableHTTPServerTransport({
|
||||
endpoint: '/message',
|
||||
port: port,
|
||||
});
|
||||
|
||||
// // 在 server.tool() 方法内,实现一个获取天气的工具
|
||||
// server.registerTool(
|
||||
// "get_weather",
|
||||
// {
|
||||
// city: z.string().describe("要查询天气的城市名称"),
|
||||
// },
|
||||
// async ({ city }) => {
|
||||
// // 重要:API密钥等敏感信息应从环境变量读取
|
||||
// const apiKey = process.env.WEATHER_API_KEY;
|
||||
// if (!apiKey) {
|
||||
// throw new Error("WEATHER_API_KEY 环境变量未设置");
|
||||
// }
|
||||
await server.connect(transport);
|
||||
|
||||
console.error(`GitLab MCP 服务器已启动 (Streamable HTTP 模式)`);
|
||||
console.error(`监听端口: ${port}`);
|
||||
console.error(`MCP 端点: http://localhost:${port}/message`);
|
||||
console.error(`\n使用 POST 请求发送消息到 /message 端点`);
|
||||
}
|
||||
|
||||
// try {
|
||||
// // 调用外部天气 API
|
||||
// const response = await fetch(
|
||||
// `https://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${city}&aqi=no`
|
||||
// );
|
||||
// const weatherData = await response.json();
|
||||
|
||||
// // 处理并返回需要的信息
|
||||
// return {
|
||||
// content: [
|
||||
// {
|
||||
// type: "text",
|
||||
// text: `城市 ${city} 的天气情况:
|
||||
// 温度:${weatherData.current.temp_c}°C
|
||||
// 天气状况:${weatherData.current.condition.text}
|
||||
// `,
|
||||
// },
|
||||
// ],
|
||||
// };
|
||||
// } catch (error) {
|
||||
// // 良好的错误处理至关重要
|
||||
// return {
|
||||
// content: [
|
||||
// {
|
||||
// type: "text",
|
||||
// text: `获取天气信息失败: ${error.message}`,
|
||||
// },
|
||||
// ],
|
||||
// isError: true, // 标记这是一个错误结果
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
|
||||
// 启动服务器,使用标准输入/输出(stdio)与客户端通信
|
||||
async function main() {
|
||||
/**
|
||||
* 启动 Stdio 服务器
|
||||
*/
|
||||
async function startStdioServer() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
console.error("MCP 服务器已通过 stdio 启动"); // 日志输出到 stderr
|
||||
console.error("GitLab MCP 服务器已启动 (Stdio 模式)");
|
||||
}
|
||||
|
||||
// 主函数:根据参数选择传输方式启动服务器
|
||||
async function main() {
|
||||
const config = parseArgs();
|
||||
|
||||
if (config.transport === 'http') {
|
||||
await startHttpServer(config.port);
|
||||
} else {
|
||||
await startStdioServer();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 运行主函数,并捕获可能的错误
|
||||
main().catch((error) => {
|
||||
console.error("服务器运行出现致命错误:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user