初版gitlab合并test mcp
This commit is contained in:
1 parent
227d77f586
commit
0ccc8b625b
6 files changed
+2866
-35
No files matched your search
@@ -1,41 +1,53 @@
|
|||||||
# Qwen Code Context: Node.js MCP Server
|
# Qwen Code Context: GitLab MCP 服务器
|
||||||
|
|
||||||
## Project Overview
|
## 项目概述
|
||||||
This is a Node.js-based Model Context Protocol (MCP) server implementation that provides tools for external clients to interact with. The server uses the `@modelcontextprotocol/sdk` to implement the MCP specification and offers tools like greeting and weather information retrieval.
|
这是一个 Node.js 实现的 Model Context Protocol (MCP) 服务器,专门用于与 GitLab 交互。主要功能包括创建和合并 Merge Request,获取 MR 详情以及关闭 MR。项目基于 `@modelcontextprotocol/sdk` 实现 MCP 规范,并提供了一组工具供外部客户端调用。
|
||||||
|
|
||||||
## Project Structure
|
## 项目结构
|
||||||
- `index.js`: Main server implementation containing tool registrations and server startup logic
|
- `gitlab-index.js`: GitLab 专用的 MCP 服务器实现,包含所有 GitLab 相关的工具
|
||||||
- `package.json`: Project metadata and dependencies
|
- `index.js`: 原始的通用 MCP 服务器示例实现(包含打招呼工具)
|
||||||
- `package-lock.json`: Dependency lock file
|
- `package.json`: 项目依赖和脚本配置
|
||||||
- `node_modules/`: Project dependencies
|
- `package-lock.json`: 依赖版本锁定文件
|
||||||
|
- `README.md`: 项目说明文档
|
||||||
|
- `test.js`: 测试文件
|
||||||
|
- `.git/`: Git 版本控制目录
|
||||||
|
- `.qwen/`: Qwen 工作空间配置目录
|
||||||
|
- `node_modules/`: 项目依赖包目录
|
||||||
|
|
||||||
## Key Dependencies
|
## 核心依赖
|
||||||
- `@modelcontextprotocol/sdk`: Core SDK for MCP implementation
|
- `@modelcontextprotocol/sdk`: 核心 MCP SDK,用于实现协议规范
|
||||||
- `zod`: For runtime type checking and validation of tool parameters
|
- `zod`: 用于运行时类型检查和验证工具参数
|
||||||
|
- `@modelcontextprotocol/inspector`: 用于开发和调试 MCP 服务器的开发工具
|
||||||
|
|
||||||
## Architecture
|
## 架构设计
|
||||||
The server implements the Model Context Protocol and registers two tools:
|
该项目采用模块化设计,使用一个封装的 `gitlabApiClient` 对象处理所有与 GitLab API 的交互。API 客户端包含以下方法:
|
||||||
1. `say_hello`: A simple greeting tool that takes a name and returns a personalized greeting
|
- `createMergeRequest`: 创建 Merge Request
|
||||||
2. `get_weather`: A weather information tool that fetches current weather data for a given city using WeatherAPI
|
- `mergeMergeRequest`: 合并 Merge Request
|
||||||
|
- `getMergeRequest`: 获取 Merge Request 详情
|
||||||
|
- `closeMergeRequest`: 关闭 Merge Request
|
||||||
|
|
||||||
## Building and Running
|
服务器实现通过 `server.registerTool` 方法注册不同的工具,每个工具都使用 Zod 定义了严格的输入参数模式和验证规则。所有工具通过标准输入/输出(stdio)与客户端通信。
|
||||||
- To run the server: `node index.js`
|
|
||||||
- The server uses stdio transport for communication with clients
|
|
||||||
- For the weather tool to work, a `WEATHER_API_KEY` environment variable must be set
|
|
||||||
|
|
||||||
## Development Conventions
|
## 功能工具
|
||||||
- Uses ES modules (indicated by "type": "module" in package.json)
|
1. `create_merge_request`: 在指定的 GitLab 项目中创建一个新的 Merge Request
|
||||||
- Implements async/await for handling asynchronous operations
|
2. `merge_merge_request`: 合并指定的 GitLab Merge Request
|
||||||
- Uses Zod for parameter validation
|
3. `get_merge_request`: 获取指定 Merge Request 的详细信息
|
||||||
- Includes proper error handling for robust operation
|
4. `close_merge_request`: 关闭指定的 GitLab Merge Request 而不合并
|
||||||
- Logs important messages to stderr to separate them from data output
|
|
||||||
- Follows MCP specification for return value formatting
|
|
||||||
|
|
||||||
## Configuration
|
## 构建和运行
|
||||||
The weather tool requires a `WEATHER_API_KEY` environment variable to be set to access the weather API service.
|
- 要启动 GitLab 专用的 MCP 服务器:`npm run gitlab`
|
||||||
|
- 要启动原始示例服务器:`npm start`
|
||||||
|
- 开发模式运行:`npm run dev`
|
||||||
|
|
||||||
## Purpose
|
服务器需要通过设置 `GITLAB_ACCESS_TOKEN` 环境变量来配置 GitLab 访问令牌,并可选择性地设置 `GITLAB_API_BASE_URL` 以支持自托管的 GitLab 实例。
|
||||||
This project serves as an example MCP server implementation that demonstrates how to register and implement tools that can be called by MCP-compatible clients. It's designed to be extensible for adding more tools as needed.
|
|
||||||
|
|
||||||
|
## 开发约定
|
||||||
|
- 使用 ES 模块(由 package.json 中 "type": "module" 指定)
|
||||||
|
- 实现 async/await 处理异步操作
|
||||||
|
- 使用 Zod 进行参数验证
|
||||||
|
- 包含适当的错误处理机制
|
||||||
|
- 将日志输出到 stderr 以与数据输出分离
|
||||||
|
- 遵循 MCP 规范的返回值格式
|
||||||
|
|
||||||
Always respond in 中文
|
## 项目目的
|
||||||
|
该项目提供了一个 GitLab 的 MCP 服务器实现,展示了如何使用 Model Context Protocol 创建可扩展的工具服务器。它可以作为基础,用于实现更多 GitLab 相关的自动化操作功能。
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
# GitLab MCP 服务器
|
||||||
|
|
||||||
|
这是一个 Model Context Protocol (MCP) 服务器,专门用于与 GitLab 交互,主要功能包括创建和合并 Merge Request。
|
||||||
|
|
||||||
|
## 功能
|
||||||
|
|
||||||
|
- `create_merge_request`: 在指定的 GitLab 项目中创建一个新的 Merge Request
|
||||||
|
- `merge_merge_request`: 合并指定的 GitLab Merge Request
|
||||||
|
- `get_merge_request`: 获取指定 Merge Request 的详细信息
|
||||||
|
- `close_merge_request`: 关闭指定的 GitLab Merge Request 而不合并
|
||||||
|
|
||||||
|
## 环境变量
|
||||||
|
|
||||||
|
- `GITLAB_ACCESS_TOKEN`: 用于访问 GitLab API 的访问令牌
|
||||||
|
- `GITLAB_API_BASE_URL`: GitLab API 的基础 URL(默认为 `https://gitlab.com/api/v4`)
|
||||||
|
|
||||||
|
## 安装和运行
|
||||||
|
|
||||||
|
1. 首先,确保你已经安装了 Node.js 和 npm。
|
||||||
|
2. 安装依赖:
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
3. 设置环境变量:
|
||||||
|
```bash
|
||||||
|
export GITLAB_ACCESS_TOKEN="your_gitlab_access_token"
|
||||||
|
export GITLAB_API_BASE_URL="https://your-gitlab-instance.com/api/v4" # 可选,仅当使用自托管的 GitLab 时
|
||||||
|
```
|
||||||
|
4. 运行 GitLab MCP 服务器:
|
||||||
|
```bash
|
||||||
|
npm run gitlab
|
||||||
|
```
|
||||||
|
|
||||||
|
## 架构
|
||||||
|
|
||||||
|
该项目使用一个封装的 `gitlabApiClient` 对象来处理所有与 GitLab API 的交互,这使得代码更模块化和可维护。API 客户端包含以下方法:
|
||||||
|
|
||||||
|
- `createMergeRequest`: 创建 Merge Request
|
||||||
|
- `mergeMergeRequest`: 合并 Merge Request
|
||||||
|
- `getMergeRequest`: 获取 Merge Request 详情
|
||||||
|
- `closeMergeRequest`: 关闭 Merge Request
|
||||||
|
|
||||||
|
## 工具详情
|
||||||
|
|
||||||
|
### create_merge_request
|
||||||
|
|
||||||
|
创建一个新的 Merge Request。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- `projectId`: GitLab 项目的 ID 或 URL 编码的路径
|
||||||
|
- `sourceBranch`: 源分支名称
|
||||||
|
- `targetBranch`: 目标分支名称
|
||||||
|
- `title`: Merge Request 的标题
|
||||||
|
- `description`: (可选) Merge Request 的描述
|
||||||
|
- `assigneeId`: (可选) 负责人 ID
|
||||||
|
- `reviewerIds`: (可选) 审阅者 ID 数组
|
||||||
|
|
||||||
|
### merge_merge_request
|
||||||
|
|
||||||
|
合并一个 Merge Request。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- `projectId`: GitLab 项目的 ID 或 URL 编码的路径
|
||||||
|
- `mrIid`: Merge Request 的内部 ID
|
||||||
|
- `mergeCommitMessage`: (可选) 合并提交的消息
|
||||||
|
- `shouldRemoveSourceBranch`: (可选) 是否在合并后删除源分支
|
||||||
|
- `mergeWhenPipelineSucceeds`: (可选) 是否在 CI 管道成功后合并
|
||||||
|
|
||||||
|
### get_merge_request
|
||||||
|
|
||||||
|
获取 Merge Request 的详细信息。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- `projectId`: GitLab 项目的 ID 或 URL 编码的路径
|
||||||
|
- `mrIid`: Merge Request 的内部 ID
|
||||||
|
|
||||||
|
### close_merge_request
|
||||||
|
|
||||||
|
关闭一个 Merge Request 而不合并。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
- `projectId`: GitLab 项目的 ID 或 URL 编码的路径
|
||||||
|
- `mrIid`: Merge Request 的内部 ID
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
要使用此 MCP 服务器,你需要在支持 Model Context Protocol 的客户端中运行它。服务器将通过标准输入/输出与客户端通信。
|
||||||
+399
@@ -0,0 +1,399 @@
|
|||||||
|
// 引入必要的模块
|
||||||
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// 创建 MCP 服务器实例
|
||||||
|
// 为其设置一个名字和版本
|
||||||
|
const server = new McpServer({
|
||||||
|
name: "gitlab-mcp-server",
|
||||||
|
version: "1.0.0",
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GitLab API 客户端配置
|
||||||
|
* 用于连接到GitLab实例并执行操作
|
||||||
|
*/
|
||||||
|
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",
|
||||||
|
// {
|
||||||
|
// description: "在指定的GitLab项目中创建一个新的Merge Request",
|
||||||
|
// inputSchema: z.object({
|
||||||
|
// 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 ({ projectId, sourceBranch, targetBranch, title, description = "", assigneeId, reviewerIds }) => {
|
||||||
|
// try {
|
||||||
|
// const result = await gitlabApiClient.createMergeRequest(
|
||||||
|
// projectId,
|
||||||
|
// sourceBranch,
|
||||||
|
// targetBranch,
|
||||||
|
// title,
|
||||||
|
// description,
|
||||||
|
// assigneeId,
|
||||||
|
// reviewerIds
|
||||||
|
// );
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// content: [
|
||||||
|
// {
|
||||||
|
// type: "text",
|
||||||
|
// text: `成功创建Merge Request: ${result.web_url}\n标题: ${result.title}\n状态: ${result.state}`,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
// } catch (error) {
|
||||||
|
// return {
|
||||||
|
// content: [
|
||||||
|
// {
|
||||||
|
// type: "text",
|
||||||
|
// text: `创建Merge Request失败: ${error.message}`,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// isError: true,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 合并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: `成功合并Merge Request #${mrIid}:\n状态: ${result.state}\n已合并时间: ${result.merged_at}`,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
// } catch (error) {
|
||||||
|
// return {
|
||||||
|
// content: [
|
||||||
|
// {
|
||||||
|
// type: "text",
|
||||||
|
// text: `合并Merge Request失败: ${error.message}`,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// 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: `Merge Request #${mrIid} 详情:\n标题: ${result.title}\n作者: ${result.author.name}\n状态: ${result.state}\n源分支: ${result.source_branch}\n目标分支: ${result.target_branch}\n创建时间: ${result.created_at}\n更新时间: ${result.updated_at}`,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
// } catch (error) {
|
||||||
|
// return {
|
||||||
|
// content: [
|
||||||
|
// {
|
||||||
|
// type: "text",
|
||||||
|
// text: `获取Merge Request详情失败: ${error.message}`,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// 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: `成功关闭Merge Request #${mrIid}:\n标题: ${result.title}\n当前状态: ${result.state}`,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// };
|
||||||
|
// } catch (error) {
|
||||||
|
// return {
|
||||||
|
// content: [
|
||||||
|
// {
|
||||||
|
// type: "text",
|
||||||
|
// text: `关闭Merge Request失败: ${error.message}`,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// isError: true,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'merge_to_test',
|
||||||
|
{
|
||||||
|
description: "把源分支名合并到测试分支,并部署到测试环境,默认用当前分支作为源分支",
|
||||||
|
inputSchema: z.object({
|
||||||
|
projectId: z.string().describe("GitLab项目的ID"),
|
||||||
|
sourceBranch: z.string().optional().describe("源分支名称,默认使用当前分支"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
|
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: `获取当前分支失败: ${error.message}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
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: `成功创建并合并Merge Request: ${mergeResult.web_url}\n标题: ${mergeResult.title}\n状态: ${mergeResult.state}\n合并时间: ${mergeResult.merged_at}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
} catch (mergeError) {
|
||||||
|
// 如果合并失败,返回MR创建成功的信息,但提示合并需要审核
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `成功创建Merge Request,但自动合并失败,请手动审核: ${result.web_url}\n标题: ${result.title}\n状态: ${result.state}\n错误信息: ${mergeError.message}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: `合并到test分支失败: ${error.message}`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
isError: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// 启动服务器,使用标准输入/输出(stdio)与客户端通信
|
||||||
|
async function main() {
|
||||||
|
const transport = new StdioServerTransport();
|
||||||
|
await server.connect(transport);
|
||||||
|
console.error("GitLab MCP 服务器已通过 stdio 启动"); // 日志输出到 stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行主函数,并捕获可能的错误
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error("服务器运行出现致命错误:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
export { server };
|
||||||
Generated
+2282
File diff suppressed because it is too large.
Load diff
+13
-4
@@ -4,14 +4,23 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"start": "node index.js",
|
||||||
|
"gitlab": "npx @modelcontextprotocol/inspector node gitlab-index.js",
|
||||||
|
"dev": "npx @modelcontextprotocol/inspector node index.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [
|
||||||
"author": "",
|
"mcp",
|
||||||
|
"gitlab",
|
||||||
|
"merge-requests"
|
||||||
|
],
|
||||||
|
"author": "Jasery",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "",
|
"description": "A Model Context Protocol server for GitLab operations, primarily for creating and merging merge requests",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.22.0",
|
"@modelcontextprotocol/sdk": "^1.22.0",
|
||||||
"zod": "^3.25.76"
|
"zod": "^3.25.76"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@modelcontextprotocol/inspector": "^0.17.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// 简单的测试文件,用于验证GitLab MCP服务器的基本功能
|
||||||
|
import { spawn } from 'child_process';
|
||||||
|
import { setTimeout } from 'timers/promises';
|
||||||
|
|
||||||
|
// 注意:这是一个概念验证测试,实际测试需要实际的GitLab项目和有效的API令牌
|
||||||
|
console.log('开始测试GitLab MCP服务器...');
|
||||||
|
|
||||||
|
// 测试GitLab MCP服务器是否能正常启动
|
||||||
|
async function testServerStartup() {
|
||||||
|
console.log('测试GitLab MCP服务器启动...');
|
||||||
|
|
||||||
|
// 启动服务器进程
|
||||||
|
const serverProcess = spawn('node', ['gitlab-index.js'], {
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe']
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听服务器输出
|
||||||
|
serverProcess.stderr.on('data', (data) => {
|
||||||
|
const output = data.toString();
|
||||||
|
console.log(`服务器输出: ${output}`);
|
||||||
|
|
||||||
|
if (output.includes('GitLab MCP 服务器已通过 stdio 启动')) {
|
||||||
|
console.log('✓ 服务器成功启动');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
serverProcess.on('error', (err) => {
|
||||||
|
console.error('✗ 服务器启动失败:', err.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 等待一段时间后终止进程
|
||||||
|
await setTimeout(2000); // 等待2秒
|
||||||
|
serverProcess.kill();
|
||||||
|
console.log('服务器测试完成');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务器启动测试
|
||||||
|
testServerStartup().then(() => {
|
||||||
|
console.log('所有测试完成');
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('测试过程中发生错误:', error);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user