diff --git a/servers/oeGitExt/README.md b/servers/oeGitExt/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0c9bbb9166b000f487857a2f99c18bfeb40b73f2 --- /dev/null +++ b/servers/oeGitExt/README.md @@ -0,0 +1,73 @@ +# oeGitExt MCP Server 使用说明 + +oeGitExt 提供了管理 openEuler 社区 issue、repos、PR 和 project 的 MCP 服务 + +## 1. 环境准备 + +安装 python 依赖。为了更加直观,当前示例使用 `pip` 安装到系统的 python 目录,实际上更加推荐 `uv` 安装到虚拟环境。 + +```bash +pip install pydantic mcp requests gitpython --trusted-host mirrors.huaweicloud.com -i https://mirrors.huaweicloud.com/repository/pypi/simple +``` + +确保已安装 oegitext 命令行工具并配置好 openEuler 社区账号 + +## 2. MCP 配置 + +在插件 Roo Code 中配置 MCP 服务器,编辑 MCP 配置文件 `mcp_settings.json`,在 `mcpServers` 中新增如下内容: + +```json +{ + "mcpServers": { + "oeGitExt": { + "command": "uv", + "args": [ + "--directory", + "/your_path/mcp-servers/servers/oeGitExt/src", + "run", + "oegitext_mcp.py" + ], + "disabled": false, + "alwaysAllow": [ + "get_my_openeuler_project" + ] + } + } +} +``` + +配置完成后,可以在 MCP 列表上看到 `oeGitExt` 服务,且状态正常。 + +> 如果出现报错,请根据提示信息检查 python 组件依赖是否满足。 + +## 3. 功能说明 + +oeGitExt 提供以下工具函数: + +1. `get_my_openeuler_issue()` - 统计我在 openEuler 社区所负责的 issue +2. `get_my_openeuler_project()` - 查找我在 openEuler 社区的项目 +3. `get_my_openeuler_pr(repo_type, repo_name)` - 查找我在 openEuler 对应仓库下的 PR +4. `create_openeuler_pr(repo_type, repo_name, title, namespace, source_branch, base, body)` - 在 openEuler 社区仓库创建 PR + +## 4. 使用示例 + +### 查询我的 openEuler issue + +``` +查询我在openEuler社区负责的issue +``` + +### 创建 PR + +``` +在openEuler社区创建PR: +- 仓库类型:src-openeuler +- 仓库名:kernel +- 标题:修复内核模块加载问题 +- 命名空间:your_namespace +- 源分支:new-feature +- 目标分支:master +- 描述:版本新特性.. (可选) +``` + +MCP 会自动处理 PR 创建流程,包括检查源仓库是否存在、获取当前分支信息等。 \ No newline at end of file diff --git a/servers/oeGitExt/mcp_config.json b/servers/oeGitExt/mcp_config.json new file mode 100644 index 0000000000000000000000000000000000000000..ce664e9d79ba82fd39ed302e39e62fa780fc4d32 --- /dev/null +++ b/servers/oeGitExt/mcp_config.json @@ -0,0 +1,18 @@ +{ + "mcpServers": { + "管理openEuler社区的issue,repos,pr,以及我的project": { + "command": "uv", + "args": [ + "--directory", + "/home/dev/mcp-servers/servers/oeGitExt/src", + "run", + "oegitext_mcp.py" + ], + "disabled": false, + "autoApprove": [], + "alwaysAllow": [ + "get_my_openeuler_project" + ] + } + } + } \ No newline at end of file diff --git a/servers/oeGitExt/readme.md b/servers/oeGitExt/readme.md deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/servers/oeGitExt/src/oegitext_mcp.py b/servers/oeGitExt/src/oegitext_mcp.py new file mode 100644 index 0000000000000000000000000000000000000000..bf1663ef6ce13fb30b7206e4cf49ce251fe31f6a --- /dev/null +++ b/servers/oeGitExt/src/oegitext_mcp.py @@ -0,0 +1,105 @@ +import subprocess +import json +from pydantic import Field +from mcp.server.fastmcp import FastMCP +from git import Repo, Remote + +mcp = FastMCP("管理openEuler社区的issue,repos,pr,以及我的project") + +@mcp.tool() +def get_my_openeuler_issue() -> dict: + """统计我在openEuler社区所负责的issue""" + try: + # 执行oegitext命令并解析JSON结果 + result = subprocess.check_output(['oegitext', 'show', 'issue', '-p'], + text=True, + stderr=subprocess.STDOUT) + + return result + except subprocess.CalledProcessError as e: + return e + except Exception as e: + return e + +@mcp.tool() +def get_my_openeuler_project() -> dict: + """查找我在openEuler社区的项目""" + try: + # 执行oegitext命令并解析JSON结果 + result = subprocess.check_output(['oegitext', 'show', 'proj', '-p'], + text=True, + stderr=subprocess.STDOUT) + + return result + except subprocess.CalledProcessError as e: + return e + except Exception as e: + return e + +@mcp.tool() +def get_my_openeuler_pr(repo_type:str = Field(description="仓库属性,有两种:制品仓:src-openeuler,源码仓:openeuler"), + repo_name:str = Field(description="仓库名")) -> dict: + """ + 查找我在openEuler对应仓库下的pr,如果用户没有指定repo_type,可以执行这个函数两次,都查询一遍 + """ + try: + # 执行oegitext命令并解析JSON结果 + name = f"{repo_type}/{repo_name}" + result = subprocess.check_output(['oegitext', 'show', 'pr' , '-name', name, '-p'], + text=True, + stderr=subprocess.STDOUT) + + return result + except subprocess.CalledProcessError as e: + return e + except Exception as e: + return e + + +@mcp.tool() +def create_openeuler_pr( + repo_type: str = Field(description="仓库属性,有两种:制品仓:src-openeuler,源码仓:openeuler"), + repo_name: str = Field(description="仓库名。如果未指定,则默认和本地仓库名相同"), + title: str = Field(description="PR标题"), + namespace: str = Field(description="Pull Request提交使用的源命名空间(一般是git用户名)。如果未指定,则使用当前本地配置的git用户名"), + source_branch: str = Field(description="Pull Request提交的源分支。如果未指定,则使用当前分支"), + base: str = Field(description="Pull Request提交目标分支的名称。如果未指定,则默认为主仓的master分支"), + body: str = Field(default="", description="PR描述(可选)") +) -> dict: + """在openEuler社区仓库创建PR,默认源为本地当前仓库的当前分支,目标为主仓的master分支""" + try: + head = f"{namespace}:{source_branch}" + + # 构建命令 + cmd = [ + 'oegitext', 'pull', '-cmd', 'create', + '-user', repo_type, + '-repo', repo_name, + '-title', title, + '-head', head, + '-base', base + ] + + if body: + cmd.extend(['-body', body]) + + # 执行命令 + result = subprocess.check_output(cmd, text=True, stderr=subprocess.STDOUT) + return { + "result": result, + "details": { + "source_repo": f"{namespace}/{repo_name}", + "target_repo": f"{repo_type}/{repo_name}", + "source_branch": source_branch, + "target_branch": base + } + } + except subprocess.CalledProcessError as e: + return {"error": e.output} + except Exception as e: + return {"error": str(e)} + + +if __name__ == "__main__": + # Initialize and run the server + mcp.run()