关于 Redmine MCP Server
Redmine MCP Server 将企业 Redmine 实例连接到 AI Agent,用于处理项目、问题、Wiki、附件和工时。它结合了广泛的只读能力、版本分析,以及可选且可配置的写入工具。
主要功能
- 01浏览项目、问题和成员
- 02搜索问题、Wiki 与 Redmine 内容
- 03读取附件及支持的文档文本
- 04查看工时记录和参考数据
- 05分析工作量与版本风险
- 06启用或禁用写入及工具组
适用场景
- 01审查并分拣项目问题
- 02查找 Wiki 页面和附件中的信息
- 03检查用户工作量和逾期事项
- 04评估项目版本的风险
MCP 相关
它通过 stdio 将 Redmine 数据和操作作为 MCP 工具及提示词提供。默认提供七种写入工具,可通过 REDMINE_MCP_WRITE_ENABLED=false 禁用;也可单独禁用工具组。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm -e REDMINE_URL -e REDMINE_API_KEY -e REDMINE_MCP_WRITE_ENABLED ghcr.io/igorolv/redmine-mcp-server:0.1.1REDMINE_URLBase URL of the Redmine instance, e.g. https://redmine.example.comREDMINE_API_KEY密钥Redmine API key of the user the server acts as (My account -> API access key)REDMINE_MCP_WRITE_ENABLEDSet to true to also expose the issue, note, attachment, time-entry and wiki write tools本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/igorolv/redmine-mcp-server:0.1.1stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Redmine MCP 服务
用于访问企业 Redmine 实例的本地 MCP 服务。除了读取工具外,它还提供一组有限的写入操作,用于处理问题、工时记录和 Wiki;设置
REDMINE_MCP_WRITE_ENABLED=false 即可仅以只读模式运行。它允许 AI Agent(Claude Code、Cursor、
VS Code Copilot 等)处理问题、项目、成员、版本、Wiki、附件、工时记录和参考数据。
快速开始
本文档介绍如何安装和连接 Redmine MCP Server 本身。AI 客户端的安装和配置不在本文档的讨论范围内。
- 安装 JDK 25+。
- 从最新版本下载
redmine-mcp-server.jar, 或自行构建:./gradlew bootJar(参见构建)。此外也提供 Docker 镜像。 - 获取
REDMINE_URL和REDMINE_API_KEY(参见配置)。 - 确认 JAR 可以启动(参见冒烟测试)。
- 将 JAR 添加到客户端的 MCP 配置中(参见连接 AI 客户端)。
对于 Claude Code,只需运行一条命令:
claude mcp add --scope user -e REDMINE_URL=https://redmine.example.com -e REDMINE_API_KEY=your_key -- redmine java -jar /path/to/redmine-mcp-server.jar
架构
该服务仅支持 stdio 传输方式。
Stdio
┌─────────────┐ stdio ┌──────────────────┐ REST API ┌──────────┐
│ AI agent │ <------------> │ redmine-mcp- │ -------------> │ Redmine │
│ (Claude Code│ stdin/stdout │ server (Java) │ HTTP + API │ (corp.) │
│ Cursor...) │ │ │ Key │ │
└─────────────┘ └──────────────────┘ └──────────┘
AI 客户端会将服务作为子进程启动;通信通过 stdin/stdout 按照 MCP 协议进行。
工具
该服务默认提供 32 个只读 MCP 工具,外加 7 个写入工具。设置
REDMINE_MCP_WRITE_ENABLED=false 可移除写入工具,并以纯只读模式运行。
用户
| 工具 | 描述 |
|---|---|
getCurrentUser | 当前用户:ID、登录名、用户组、项目。适用于筛选“我的问题” |
项目
| 工具 | 描述 |
|---|---|
listProjects | 列出所有可访问的项目 |
getProject | 项目详情:跟踪器、模块、描述 |
listProjectMembers | 项目成员及其角色 |
listVersions | 项目版本(里程碑) |
问题
| 工具 | 描述 |
|---|---|
listIssues | 问题列表,支持按项目、状态、跟踪器、指派人、优先级、版本、已保存的查询、自定义字段(customFieldFilters 采用 cf_<id>=value 格式)和排序进行筛选 |
searchIssues | 全文搜索问题并返回详细结果 |
getIssue | 问题详情:描述、状态、指派人、日期、备注、关联关系、自定义字段、附件和关联修订(changesets)。参数:issueId、focus(default、implementation、timeline、full;可选) |
getIssueJournal | 获取一条完整的问题日志备注/事件,不压缩响应。参数:issueId、journalId |
getMyIssues | 当前用户的问题。参数:projectId、statusId、sort、limit、offset |
getIssueTree | 父级链和子任务。root 和 ancestors 保留 Issue JSON 结构,但只包含精简上下文;如需完整详情或关联关系,请调用 getIssue。参数:issueId、depth(默认值为 2,最大值为 5) |
getIssueHistory | 创建记录和每条日志事件,以及状态时间段。长文本会被缩短;journalId 用于标识可通过 getIssueJournal 获取的完整条目。使用可选的 offset 参数跟进 nextOffset,直到该字段缺失。 |
问题、时间条目和 Wiki 写入(可选)
这些工具默认会出现在 tools/list 中;设置 REDMINE_MCP_WRITE_ENABLED=false 可将其移除:
| 工具 | 描述 |
|---|---|
createIssue | 创建问题。支持 Redmine 核心字段和通过 customFieldsJson 指定的自定义字段 |
updateIssue | 部分更新现有问题的指定字段 |
addIssueNote | 向问题日志添加备注 |
attachFileToIssue | 从任意可读取的本地路径上传文件并将其附加到问题 |
createTimeEntry | 为 REDMINE_API_KEY 对应的用户创建时间条目;接受 issueId/projectId 中的且仅其中一个,以及工时、日期、活动、评论和自定义字段 |
createWikiPage | 创建 Wiki 页面;如果页面已存在,则拒绝请求 |
updateWikiPage | 完整替换 Wiki 页面文本;需要提供 getWikiPage 返回的乐观锁版本 |
Redmine 本身会强制执行 REDMINE_API_KEY 对应用户的权限、工作流和必填字段规则。MCP 服务不会额外定义“自己的”问题或备注。为便于识别由 AI 创建的问题,默认会在 createIssue 创建的问题描述前加上 AI_EDIT:(updateIssue 不会添加此前缀)。备注、时间条目评论、Wiki 修订评论和上传的文件名默认不会添加标记;每种标记都由单独的 REDMINE_MCP_WRITE_*_PREFIX 变量配置(参见配置),值为空时会关闭标记。问题描述和备注会在单独一行添加前缀,后接一个空行;单行字段(时间条目和 Wiki 修订评论)会添加前缀和一个空格;文件名会添加前缀,不加分隔符。Wiki 页面标记本身不会被更改。
当前未实现编辑和删除已有备注:目标版本 Redmine 4.0.4 未提供兼容的 REST API。编辑和删除时间条目、删除、重命名和保护 Wiki 页面,以及代表其他用户创建时间条目,均不属于当前操作范围。
搜索
| 工具 | 描述 |
|---|---|
searchAll | Redmine 全局搜索:问题、Wiki、新闻、文档、提交记录等。参数:searchQuery、projectId、types、limit、offset |
附件和 Wiki
| 工具 | 描述 |
|---|---|
getAttachment | 将原始附件文件下载到本地快照目录,返回 localPath/fileUri;如果格式受支持,则立即将文本上下文添加到 parts[]:txt/log/xml/json/csv、PDF、Word(.docx)、Excel(.xlsx)、PowerPoint(.pptx)、ZIP。ZIP 中的每个条目都可能生成单独的 part。.docx 会生成一个文本 part:有 pandoc 时为 pandoc markdown,否则为 POI 纯文本。参数:issueId、attachmentId、maxChars、partLimit(参见 处理限制) |
getWikiPage | 项目 Wiki 页面的内容 |
listWikiPages | 列出项目的所有 Wiki 页面 |
searchWikiPages | 在 Wiki 页面中进行全文搜索。参数:searchQuery、projectId、limit、offset |
时间条目
| 工具 | 描述 |
|---|---|
listTimeEntries | 按项目、问题、用户和时间段筛选已记录的时间 |
getMyTimeEntries | 当前用户已记录的时间。参数:projectId、issueId、from、to、limit、offset |
参考数据
| 工具 | 描述 |
|---|---|
listQueries | 已保存的查询(自定义筛选条件)——ID + 名称。将 ID 与 listIssues(queryId) 搭配使用以应用筛选条件,包括自定义字段筛选条件 |
listStatuses | 所有问题状态(ID + 名称)——用于筛选 listIssues |
listTrackers | 所有跟踪器(ID + 名称)——用于筛选 listIssues |
listPriorities | 所有优先级(ID + 名称)——用于筛选 listIssues |
listIssueCategories | 项目的问题类别(ID + 名称) |
listTimeEntryActivities | 用于解读现有时间条目的活动类型(ID + 名称) |
分析
| 工具 | 描述 |
|---|---|
getProjectSummary | 项目汇总数据:未关闭/已关闭问题总数;按状态、跟踪器、优先级和负责人分类分析未关闭问题;逾期问题;预估/已用工时。最多分析 500 个未关闭问题,并返回截断标记。参数:projectId、versionId(可选) |
getUserWorkload | 工作负载分析:按项目和优先级统计未关闭问题、逾期问题、重点问题。最多分析 500 个未关闭问题,并返回截断标记。参数:userId(可选,默认为当前用户)、projectId(可选) |
getVersionChangelog | 按跟踪器对版本问题分组,并统计未关闭/已关闭问题。最多分析 500 个问题,并返回截断标记。参数:projectId、versionId |
getBlockerChain | 递归遍历向上和向下的阻塞链(blocks/blocked_by),深度最多为 10,加载的问题最多为 30 个。参数:issueId |
getStaleIssues | 按更新时间从早到晚列出 N 天内未更新的未关闭问题。参数:projectId、daysSinceUpdate(默认值为 30)、limit |
getReleaseRisks | 发布风险评估:阻塞项、逾期事项、高优先级问题、未分配的问题。最多分析 500 个未关闭问题,并返回截断标记。参数:projectId、versionId |
compareVersions | 比较两个版本:独有问题、共同问题、关闭率。每个版本最多分析 500 个问题,并返回截断标记。参数:projectId、versionId1、versionId2 |
当 REDMINE_MCP_WRITE_ENABLED=false 时,所有工具均为只读,不会修改 Redmine 中的任何数据。
工具组(启用/禁用)
工具按领域分组,每组都可以通过环境变量禁用。
默认情况下所有组均已启用——开箱即用的工具清单保持不变。禁用
工具组会缩小 MCP tools/list 清单,客户端会在会话开始时将其加载到模型上下文中。
这有助于上下文较小的(本地)模型:禁用不需要的工具组,只保留模型实际需要的工具。
| 变量 | 组(工具) |
|---|---|
REDMINE_MCP_TOOLS_ISSUE | 问题(核心):listIssues、searchIssues、getIssue、getMyIssues、getIssueJournal |
REDMINE_MCP_TOOLS_ISSUE_STRUCTURE | 问题结构/历史:getIssueTree、getIssueHistory |
REDMINE_MCP_TOOLS_PROJECT | 项目:listProjects、getProject、listProjectMembers、listVersions |
REDMINE_MCP_TOOLS_SEARCH | 搜索:searchAll |
REDMINE_MCP_TOOLS_ATTACHMENT | 附件:getAttachment |
REDMINE_MCP_TOOLS_WIKI | Wiki:getWikiPage、listWikiPages、searchWikiPages |
REDMINE_MCP_TOOLS_TIME_ENTRY | 时间条目:listTimeEntries、getMyTimeEntries |
REDMINE_MCP_TOOLS_REFERENCE_DATA | 参考数据:listQueries、listStatuses、listTrackers、listPriorities、listIssueCategories、listTimeEntryActivities |
REDMINE_MCP_TOOLS_USER | 用户:getCurrentUser |
REDMINE_MCP_TOOLS_ISSUE_ANALYTICS | 问题分析:getBlockerChain、getStaleIssues |
REDMINE_MCP_TOOLS_RELEASE_ANALYTICS | 发布/项目分析:getProjectSummary、getUserWorkload、getVersionChangelog、getReleaseRisks、compareVersions |
每个变量都接受 true(默认值)或 false。示例:若只保留问题和项目相关工作,
请禁用其余工具组——REDMINE_MCP_TOOLS_RELEASE_ANALYTICS=false、
REDMINE_MCP_TOOLS_WIKI=false 等。MCP 提示词不受这些标志影响;如果某个工具已禁用,提示词会告诉模型跳过相应步骤。
MCP 提示词
该服务还提供用于典型问题处理场景的 MCP 提示词。每个提示词都需要一个参数,
issueId——Redmine 问题编号(12345 或 #12345)。
| 提示词 | 描述 |
|---|---|
incident-brief | 快速了解事件:通过 getIssue 获取问题,通过 getAttachment 下载所有附件并提供简短预览,然后生成简洁的 Markdown 报告 |
incident-implementation | 实施背景:使用 focus=implementation 获取问题,按名称、描述和日期挑选相关附件,仅在必要时恢复压缩的日志数据,然后生成需求、修订依据和验证清单 |
incident-timeline | 事件时间线:使用 getIssue(focus=timeline) 获取问题和变更集背景,然后读取 getIssueHistory 的每一页,以获取日志事件和状态区间 |
issue-remaining-work | 尚未完成的事项:结合 getIssue、getIssueTree(相关时包括同级问题)、getBlockerChain,并在需要时使用 getIssueHistory,报告未解决事项,而不单凭状态作判断 |
通过使用占位符参数请求提示词来构建命令模板的客户端(例如发送 $1 的
opencode 1.x)会获得保留占位符的模板,因此客户端自身的替换机制可以继续正常工作。
技术栈
- Java 25、Spring Boot 4.2.0-M2、Spring AI MCP 2.1.0-M1(stdio 传输)
- Apache PDFBox 3.0.5——从 PDF 中提取文本
- Apache POI 5.4.0——从 Word、Excel、PowerPoint 中提取文本
- Apache Tika 3.2.0(core + parsers-standard)——备用解析器和元数据提取
- Pandoc(可选,外部二进制程序)——如果
PATH中有该程序,可改进 DOCX 到文本/Markdown 的转换;否则服务会使用 POI - 使用版本目录(
gradle/libs.versions.toml)的 Gradle 9.3.1
构建
Linux/macOS:
# Point to JDK 25+ if it is not the default JDK:
export JAVA_HOME="$HOME/.jdks/jdk-25.0.2"
./gradlew build
Windows PowerShell:
# Point to JDK 25+ if it is not the default JDK:
$env:JAVA_HOME="C:\Program Files\Java\jdk-25"
.\gradlew.bat build
结果:build/libs/redmine-mcp-server.jar
配置
服务需要 REDMINE_URL 和 REDMINE_API_KEY;其余变量均为可选项:
| 变量 | 描述 |
|---|---|
REDMINE_URL | Redmine 实例的基础 URL(例如 https://redmine.example.com) |
REDMINE_API_KEY | Redmine 用户的 API 密钥 |
REDMINE_MCP_WRITE_ENABLED | 将 createIssue、updateIssue、addIssueNote、attachFileToIssue、createTimeEntry、createWikiPage、updateWikiPage 工具添加到 tools/list;设为 false 可移除这些工具并以只读模式运行;默认为 true |
REDMINE_MCP_WRITE_ISSUE_DESCRIPTION_PREFIX | 添加到 createIssue 创建的问题描述前的标记;默认为 AI_EDIT:。设为空值可禁用 |
REDMINE_MCP_WRITE_ISSUE_NOTE_PREFIX | 添加到 addIssueNote 添加的备注前的标记;默认为空(禁用) |
REDMINE_MCP_WRITE_TIME_ENTRY_COMMENT_PREFIX | 添加到 createTimeEntry 评论前的标记;计入 Redmine 的 1024 个字符限制;默认为空(禁用) |
REDMINE_MCP_WRITE_WIKI_COMMENT_PREFIX | 添加到 createWikiPage / updateWikiPage 的 wiki 修订评论前的标记;默认为空(禁用) |
REDMINE_MCP_WRITE_ATTACHMENT_FILENAME_PREFIX | 添加到 attachFileToIssue 上传文件名的前缀(例如 AI_EDIT__);不得包含 / \ ? % * : | " ' < >;默认为空(禁用) |
REDMINE_MCP_DATA_DIR | 服务的本地数据目录;默认为 ~/.redmine-mcp-server |
REDMINE_MCP_HTTP_CONNECT_TIMEOUT_SECONDS | 打开与 Redmine 的连接时的超时时间;默认为 10 秒 |
REDMINE_MCP_HTTP_READ_TIMEOUT_SECONDS | 等待 Redmine 响应单个请求(包括请求上传)的超时时间;默认为 30 秒。工具调用逐个处理,因此请求卡住会导致之后的所有调用延迟 |
REDMINE_MCP_HTTP_SLOW_REQUEST_WARN_MILLIS | 超过此时长的 Redmine 请求会记录为警告;默认为 5000 毫秒 |
REDMINE_MCP_ATTACHMENT_PER_PART_CHARS | 不带限制的 getAttachment 调用中,每个单独 part(例如 ZIP 中的一个文件)的文本上限;默认为 30000 个字符。工具的 partLimit 参数会覆盖此值;未提供 partLimit 时,maxChars 也会覆盖此值。 |
REDMINE_MCP_ATTACHMENT_PER_ATTACHMENT_CHARS | 不带 maxChars 的 getAttachment 调用中,每个附件提取文本的总上限;默认为 40000 个字符,可使默认响应低于 REDMINE_MCP_RESPONSE_MAX_CHARS。 |
REDMINE_MCP_ATTACHMENT_MAX_REQUEST_CHARS | 调用方传给 getAttachment 的 maxChars 和 partLimit 上限;默认为 200000 个字符。超过此上限的请求会被缩减至该值,响应中的 limits.note 会说明这一点。 |
REDMINE_MCP_RELATED_MAX_SIBLINGS | 读取问题时添加到 related 的同级问题数量上限;默认为 20 |
REDMINE_MCP_RELATED_MAX_CHILDREN | 读取问题时添加到 related 的子问题数量上限;默认为 20 |
REDMINE_MCP_RELATED_MAX_RELATED | 读取问题时,从关系中添加到 related 的相关问题数量上限;默认为 10 |
getIssue 通过一次轻量级读取父项,以及一次批量查询兄弟项、子项和关联目标,解析 related 上下文;不会对关联问题创建快照。timeline 和 changesets 聚焦模式会省略 related,并完全跳过这些请求。 | |
REDMINE_MCP_RESPONSE_MAX_CHARS | getIssue 和 getAttachment 逐步压缩前的目标响应大小上限;默认为 50000 个字符 |
REDMINE_MCP_RESPONSE_JOURNAL_TAIL_KEEP | 在进一步大幅精简前,getIssue 的预算压缩保留的最新日志条目数量;默认为 30 |
REDMINE_MCP_RESPONSE_ATTACHMENT_TEXT_PART_CHARS | 在未指定 maxChars/partLimit 的 getAttachment 调用中,响应压缩期间每个附件部分的文本上限;默认为 10000 个字符。对于明确指定限制的调用,截断后的长度不会低于这些限制。 |
REDMINE_MCP_RESPONSE_JOURNAL_NOTE_CHARS | getIssue 响应压缩期间每条日志备注的文本上限;默认为 5000 个字符 |
REDMINE_MCP_RESPONSE_IMAGE_PARTS_KEEP | getAttachment 响应压缩保留的图片部分数量;默认为 5 |
REDMINE_MCP_PAGINATION_DEFAULT_LIMIT | 列表/搜索工具的默认页大小;默认为 25 |
REDMINE_MCP_PAGINATION_DEFAULT_OFFSET | 列表/搜索工具的默认偏移量;默认为 0 |
REDMINE_MCP_PAGINATION_MEMBERS_DEFAULT_LIMIT | listProjectMembers 的默认页大小;默认为 100 |
REDMINE_MCP_TREE_DEFAULT_DEPTH | getIssueTree 的默认深度;默认为 2 |
REDMINE_MCP_TREE_MAX_DEPTH | getIssueTree 的最大深度;默认为 5 |
REDMINE_MCP_TREE_MAX_ISSUES | getIssueTree 加载的问题数量上限;默认为 50 |
REDMINE_MCP_ANALYSIS_MAX_PAGES | 分析工具读取的 Redmine 页面数量上限;默认为 5 |
REDMINE_MCP_ANALYSIS_PAGE_SIZE | 分析工具使用的 Redmine 页大小;默认为 100 |
REDMINE_MCP_ANALYSIS_TOP_ISSUES_LIMIT | 分析响应的热门列表中问题数量上限;默认为 10 |
REDMINE_MCP_ANALYSIS_MAX_BLOCKER_DEPTH | getBlockerChain 的最大遍历深度;默认为 10 |
REDMINE_MCP_ANALYSIS_MAX_BLOCKER_ISSUES | getBlockerChain 加载的问题数量上限;默认为 30 |
REDMINE_MCP_ANALYSIS_STALE_ISSUES_DEFAULT_DAYS_SINCE_UPDATE | getStaleIssues 的默认 daysSinceUpdate;默认为 30 |
REDMINE_MCP_ANALYSIS_STALE_ISSUES_DEFAULT_LIMIT | getStaleIssues 的默认结果数量上限;默认为 25 |
REDMINE_MCP_ANALYSIS_STALE_ISSUES_MAX_LIMIT | getStaleIssues 的最大结果数量上限;默认为 100 |
REDMINE_MCP_EXTRACTION_PANDOC_ENABLED | 当在 PATH 中找到 Pandoc 可执行文件时,启用对 DOCX 使用 Pandoc;默认为 true |
REDMINE_MCP_EXTRACTION_PANDOC_PROBE_TIMEOUT_SECONDS | 启动时检测 Pandoc 是否可用的超时时间;默认为 2 秒 |
REDMINE_MCP_EXTRACTION_PANDOC_CONVERSION_TIMEOUT_SECONDS | 通过 Pandoc 转换单个 DOCX 的超时时间;默认为 30 秒 |
REDMINE_MCP_EXTRACTION_LIMITS_MAX_DEPTH | 处理嵌套文档和归档文件时的最大递归深度;默认为 1 |
REDMINE_MCP_EXTRACTION_LIMITS_MAX_TOTAL_PARTS | 每次提取的文本/元数据部分数量上限;默认为 100 |
REDMINE_MCP_EXTRACTION_LIMITS_MAX_TOTAL_BYTES | 每次提取的读取总字节数上限;默认为 52428800 字节 |
REDMINE_MCP_EXTRACTION_LIMITS_MAX_ENTRY_BYTES | 归档文件中单个条目的大小上限;默认为 10485760 字节 |
REDMINE_MCP_EXTRACTION_ZIP_MAX_ENTRIES_PER_ARCHIVE | 每个 ZIP 归档文件的条目数量上限;默认为 100 |
REDMINE_MCP_EXTRACTION_TIKA_BODY_LIMIT_BYTES | 传递给 Tika 回退解析器的正文大小上限;默认为 5242880 字节 |
REDMINE_MCP_EXTRACTION_TIKA_METADATA_MAX_FIELDS | 响应中 Tika 元数据字段的数量上限;默认为 40 |
如何获取 REDMINE_URL
在浏览器中打开 Redmine,复制地址栏中的值,不包含路径——只保留协议和域名。
示例:
| 浏览器地址栏中的内容 | REDMINE_URL 的值 |
|---|---|
https://redmine.example.com/projects/myproject | https://redmine.example.com |
http://192.168.1.50:3000/issues/123 | http://192.168.1.50:3000 |
http://10.0.0.5/redmine/projects | http://10.0.0.5/redmine |
如果 Redmine 只能通过 IP 地址访问(没有域名),请直接使用 IP;如果端口不是标准端口(80/443),也要包含端口。如果 Redmine 部署在子路径下(例如
/redmine),也要将其包含在 URL 中。
如何获取 REDMINE_API_KEY
- 使用你的帐户登录 Redmine
- 点击 “My account”(右上角)
- 在右侧栏找到 “API access key” 区块
- 点击 “Show”,系统会显示你的个人 API 密钥
- 复制密钥,并将其用作
REDMINE_API_KEY的值
如果没有显示“API access key”区块,请联系 Redmine 管理员——设置中可能禁用了 REST API。
冒烟测试
连接 AI 客户端之前,建议先验证 JAR 能否使用之后将配置到客户端中的同一组环境变量正常启动。
Linux/macOS:
REDMINE_URL=https://redmine.example.com REDMINE_API_KEY=your_key \
java -jar build/libs/redmine-mcp-server.jar
Windows PowerShell:
$env:REDMINE_URL="https://redmine.example.com"
$env:REDMINE_API_KEY="your_key"
java -jar .\build\libs\redmine-mcp-server.jar
服务通过 stdio 运行,不会开放 HTTP 端口:成功启动后,它会静默等待通过 stdin/stdout 发送的 MCP 请求。日志中没有错误且进程没有立即退出,即表示启动成功。按 Ctrl+C 停止。
Docker
每次发布时都会将镜像推送到 GHCR:
docker run -i --rm -e REDMINE_URL=https://redmine.example.com -e REDMINE_API_KEY=your_key ghcr.io/igorolv/redmine-mcp-server:latest
MCP 客户端应启动的命令也是如此(-i 会让 stdin 在 stdio 传输期间保持打开)。将主机目录挂载到 /data,即可在多次运行之间保留日志和问题快照。
若要在本地构建镜像:docker build -t redmine-mcp-server .
日志
日志写入 ${REDMINE_MCP_DATA_DIR:-~/.redmine-mcp-server}/logs/redmine-mcp-server.log。
文件按日期和大小轮转:10MB,保留 30 天,总上限为 512MB。
问题快照
加载问题时,服务会将快照保存到磁盘上的
${REDMINE_MCP_DATA_DIR:-~/.redmine-mcp-server}/issues/<issue-id>/:issue.json、
包含快照元数据的 snapshot.json、attachments.json,以及用于存放派生文件的
extracted/<attachment-id>/ 目录。附件会保存到 attachments/ 目录下,文件名类似
<attachment-id>__<filename>;如果本地文件已存在且其大小与 Redmine 元数据相符,则可在不同快照之间重复使用。
连接到 AI 客户端
在客户端配置中添加:
{
"command": "java",
"args": ["-jar", "<absolute-path>/redmine-mcp-server.jar"],
"env": {
"REDMINE_URL": "https://redmine.example.com",
"REDMINE_API_KEY": "your_api_key"
}
}
具体配置位置:
| 客户端 | 连接方式 |
|---|---|
| Claude Code | claude mcp add --scope user -e REDMINE_URL=... -e REDMINE_API_KEY=... -- redmine java -jar /path/to/redmine-mcp-server.jar |
| Qwen Code | ~/.qwen/settings.json -> "mcpServers" -> "redmine" |
| VS Code | .vscode/mcp.json -> "servers" -> "redmine" |
| Cursor | .cursor/mcp.json -> "mcpServers" -> "redmine" |
| Claude Desktop | claude_desktop_config.json -> "mcpServers" -> "redmine" |
之后重启客户端。
运维与安全
此 MCP 服务旨在与 AI 客户端一同在本地运行。它不会开放 HTTP 端口,也不会接受任何传入的网络连接:客户端会将 JAR 作为子进程启动,并通过 stdin/stdout 与其通信。
访问模型
- 服务使用
REDMINE_API_KEY中设置了 API 密钥的 Redmine 用户权限。 - 默认情况下,七个明确列出的 issue、工时记录和 wiki 写入工具可用;
设置
REDMINE_MCP_WRITE_ENABLED=false即可完全以只读模式运行。服务既不会扩大 API 用户的权限,也不会绕过 Redmine 工作流。 - 用户在 Redmine 中的权限决定其可访问的项目、issue、附件和工时记录。如果用户在 Redmine 中无法查看某个对象,服务也不得访问该对象。
REDMINE_MCP_WRITE_*_PREFIX标记(例如创建 issue 时默认使用的AI_EDIT:)是 搜索标签,而非授权机制:修改的作者身份由拥有 API 密钥的 Redmine 账户可靠地确定。attachFileToIssue会有意接受任何可读取的本地路径,不会限制目录白名单。由于默认 启用了写入工具,请仅在可信 AI 客户端旁运行服务,并注意客户端可以将进程可读取的 任意文件内容传入 Redmine。- 将 API 密钥视为机密。请勿将其提交到项目仓库、shell 脚本、
.vscode/mcp.json、.cursor/mcp.json或项目的其他共享文件中。
开发和验证写入操作时,请使用单独的测试 Redmine 和单独的 API 密钥。由于默认启用写入,
将 integrationTest 或手动冒烟测试指向生产环境时,请设置
REDMINE_MCP_WRITE_ENABLED=false。
传递给 AI 客户端的数据
AI 客户端只会收到其通过 MCP 工具请求的数据:
- issue 卡片:主题、描述、状态、优先级、受派人、作者、日期、关联关系、子任务、 日志/评论、自定义字段;
- 项目、版本、成员、参考数据和工时记录的信息;
- wiki 页面;
- 附件元数据;
- 原始附件文件的本地路径,以及通过
getAttachment从 PDF、DOCX、XLSX、PPTX、 ZIP 和文本文件中提取的文本。
连接外部或云端 AI 客户端之前,请查看公司的内部政策:Redmine 数据可能包含商业机密、 个人数据、日志、密钥、错误转储和文档内容。
处理限制
代码中设有保护性限制,以防单个大型文档或相关 issue 网络使 MCP 客户端过载:
| 区域 | 限制 |
|---|---|
getAttachment.parts[] 中的每个文本部分 | 默认最多 30,000 个字符,超出部分将被截断 |
getAttachment 中的单个附件总量 | 默认最多 40,000 个字符 |
getAttachment 中显式指定的 maxChars / partLimit | 每项最多支持 200,000 个字符 |
| ZIP 深度 | 1 层 |
| ZIP 归档 | 最多 100 个条目 |
| 归档中的 ZIP 文件 | 最大 10 MB |
| ZIP 归档总量 | 最多 50 MB 的解压数据 |
getIssueTree | 深度最多为 5,issue 最多为 50 个 |
getIssueHistory | 每个 JSON 页面在字符数和 UTF-8 字节数上都不超过 REDMINE_MCP_RESPONSE_MAX_CHARS;所有事件仍可通过 nextOffset 获取 |
getAttachment 会在 limits 中报告其应用的限制(maxChars、partLimit,以及当请求值
被调低至上限时的 note);被截断的部分会携带其完整长度 totalChars。未显式指定限制时,
如果响应仍超过 REDMINE_MCP_RESPONSE_MAX_CHARS(例如,ZIP 条目很多),则会压缩响应:
先合并额外的图像部分,再将文本部分截断至
REDMINE_MCP_RESPONSE_ATTACHMENT_TEXT_PART_CHARS。显式指定限制时,只会合并图像部分。
原始文件仍保存在磁盘上的 localPath;对于 pandoc DOCX 提取,markdown 部分也会指向
其保存的文本文件。其他文档部分可能指向 PDF 或 Office 源文件,而非单独的提取文本文件。
getIssue 支持 focus 参数。default 会保留常规响应结构,仅在超出响应预算时进行压缩。implementation 面向该问题的实现工作:完整问题仍会保存到磁盘,而工具响应会保留描述、人类备注、附件元数据和所有变更集修订;省略详细历史记录和提交消息正文。timeline 面向“谁在何时做了什么”这类问题:保留日志和变更集,但省略附件、自定义字段和相关上下文。full 是显式选择完整形式,并启用保护性预算压缩。
getIssueTree 仍会完整加载并快照每个获取的问题,但只为 root 和 ancestors 返回精简的 Issue 上下文;此视图会省略它们的描述、日志、自定义字段、附件和变更集。其 subtree 仍是层级视图。这会改变这些现有 Issue 字段的内容:此前从树中读取完整详情的客户端应改为调用 getIssue(id)。getIssueTree 不包含直接关联。
getIssueHistory 包含创建事件和每个日志事件,包括没有备注或无法识别的字段变更的日志。必要时会缩短较长的备注以及字段的新旧值,在每次更新中保留 journalId,并通过 compressionNotes 说明内容缩减情况。要获取确切措辞或完整的新旧值,请调用 getIssueJournal(issueId, journalId)。先调用 getIssueHistory(issueId);如果存在 nextOffset,就在下一次调用中将该数字作为 offset 传入。持续调用,直到 nextOffset 不再存在。totalEvents 包含创建事件。statusDurations 中的状态区间属于当前页的事件;请跨页合并。比较各页的 sourceUpdatedOn,如果它发生变化,则重新开始查询。服务使用现有的 REDMINE_MCP_RESPONSE_MAX_CHARS 设置;无需新增环境变量。
如果单个事件在缩短文本后仍无法容纳,其所在页面会保留该事件和 journalId,但将事件缩减为元数据;compressionNotes 会标明省略的状态区间,并指向 getIssueJournal 以获取完整条目。
如果你需要的备注因预算压缩而从 getIssue 响应中删除,或备注被缩短,请调用 getIssueJournal(issueId, journalId):它会重新获取问题快照,并在不进行响应压缩的情况下返回所选日志条目。
一些常规列表工具(listIssues、listProjects、listTimeEntries、listQueries)直接接受 limit 和 offset。为确保运行可靠,请避免请求过大的页面;每次调用请求 25-100 条是较为实用的范围。
诊断
环境检查:
java -version
echo "$REDMINE_URL"
test -n "$REDMINE_API_KEY" && echo "REDMINE_API_KEY is set"
Redmine REST API 访问检查:
curl -H "X-Redmine-API-Key: <key>" <url>/users/current.json
构建检查:
./gradlew test
./gradlew build
针对运行中 Redmine 的集成测试:
REDMINE_URL=<url> REDMINE_API_KEY=<key> ./gradlew integrationTest
集成测试需要可访问的 Redmine 和真实测试数据。默认情况下,单元测试不包含标记为 integration 的测试。
已知运行限制
- HTTP 超时和重试策略目前无法分别配置。如果 Redmine 响应缓慢,MCP 调用的等待时间可能超出 AI 客户端用户的预期。
- Redmine 错误(
401、403、404、5xx)目前主要由 SpringRestClient层处理;AI 客户端用户看到的消息可能不如专门的 MCP 工具错误友好。 - 笔记或附件可能已成功写入,但如果 Redmine 或其他用户在确认读取之间修改了问题,其新的
journalId/attachmentId可能仍未定义。操作本身仍视为已完成。 - 搜索取决于 Redmine 设置。如果管理员禁用了
/search.json,searchAll、searchIssues和searchWikiPages可能无法返回预期结果。 - 只有带有文本层的 PDF 才能提取文本。没有 OCR 的扫描文档会被识别为无法提取文本的 PDF。
- 图片不会重新编码。
getAttachment会返回原始文件的路径;图片的文本parts[]仍为空。
项目结构
├── src/main/java/ru/it_spectrum/ai/redmine/mcp/
│ ├── RedmineMcpServerApplication.java — Spring Boot entry point
│ ├── api/ — stable MCP wire format: records returned by tools/services
│ │ ├── Issue.java
│ │ ├── AttachmentContent.java
│ │ ├── Project.java
│ │ ├── IssueMutationResult.java — stable result of issue write operations
│ │ ├── TimeEntryMutationResult.java — stable result of time-entry creation
│ │ ├── WikiMutationResult.java — stable result of wiki-page writes
│ │ └── ... — tool response DTOs and analytics DTOs
│ ├── client/
│ │ ├── RedmineClient.java — read-only wrapper over the Redmine REST API
│ │ ├── RedmineMutationClient.java — optional POST/PUT for issues, time entries, and wiki
│ │ └── model/ — raw Redmine REST API DTOs, never exported directly to MCP
│ │ ├── RedmineIssue.java
│ │ ├── RedmineAttachment.java
│ │ ├── RedmineProject.java
│ │ └── ...
│ ├── config/
│ │ ├── RedmineClientProperties.java — url + apiKey from env
│ │ ├── RedmineMcpProperties.java — all redmine-mcp.* runtime settings
│ │ ├── RedmineConfig.java — RestClient
│ │ ├── McpServerConfig.java — stdio MCP customizer with immediateExecution(true)
│ │ └── JsonConfig.java — ObjectMapper for MCP JSON
│ ├── extraction/
│ │ ├── ExtractionPipeline.java — document-to-text pipeline
│ │ ├── DocumentParser.java — parser interface
│ │ ├── FileTypeDetector.java — file type detection
│ │ ├── PandocAvailability.java — external pandoc probe at startup
│ │ └── parser/
│ │ ├── PlainTextParser.java — txt/log/csv/json/xml
│ │ ├── PdfTextParser.java — PDF via PDFBox
│ │ ├── DocxTextParser.java — DOCX via POI
│ │ ├── DocxPandocParser.java — DOCX via Pandoc when available
│ │ ├── XlsxTextParser.java — XLSX via POI
│ │ ├── PptxTextParser.java — PPTX via POI
│ │ ├── ZipParser.java — ZIP with bounded recursion
│ │ ├── ImagePassthroughParser.java
│ │ ├── TikaTextFallbackParser.java
│ │ ├── TikaMetadataParser.java
│ │ └── BinaryFallbackParser.java
│ ├── service/
│ │ ├── IssueService.java — issue business logic and mapping client.model -> api
│ │ ├── IssueMutationService.java — optional issue writing and AI marking
│ │ ├── TimeEntryMutationService.java — optional time-entry creation
│ │ ├── WikiMutationService.java — safe wiki writing with optimistic locking
│ │ ├── AttachmentService.java — attachment snapshot, download, and extraction
│ │ ├── IssueSnapshotService.java — local issue and attachment snapshots
│ │ ├── AnalysisService.java — analytics, risks, blocker chain
│ │ └── ... — services for projects, wiki, search, reference data, time entries
│ └── tools/
│ ├── AttachmentTools.java — 1 MCP tool for files and attachment context
│ ├── IncidentPrompts.java — 4 MCP prompts for issue investigation
│ ├── IssueAnalyticsTools.java — 2 issue-analytics MCP tools (blocker chain, stale)
│ ├── IssueStructureTools.java — 2 MCP tools: issue tree and change history
│ ├── IssueTools.java — 5 core issue MCP tools
│ ├── IssueWriteTools.java — 4 optional issue-write MCP tools
│ ├── ProjectTools.java — 4 project MCP tools
│ ├── ReferenceDataTools.java — 6 reference-data MCP tools
│ ├── ReleaseAnalyticsTools.java — 5 release/project analytics MCP tools
│ ├── SearchTools.java — 1 global-search MCP tool
│ ├── TimeEntryTools.java — 2 time-entry MCP tools
│ ├── TimeEntryWriteTools.java — 1 optional time-entry creation MCP tool
│ ├── UserTools.java — 1 current-user MCP tool
│ ├── WikiTools.java — 3 wiki MCP tools
│ └── WikiWriteTools.java — 2 optional wiki-write MCP tools
└── src/main/resources/
├── application.yml — MCP server configuration (stdio)
└── logback-spring.xml — logging configuration
故障排除
- “Gradle requires JVM 17 or later” — 将
JAVA_HOME指向 JDK 25+ - 连接被拒绝 / 401 — 检查
REDMINE_URL和REDMINE_API_KEY。测试:curl -H "X-Redmine-API-Key: <key>" <url>/users/current.json - 没有搜索结果 — 确认 Redmine 中的
/search.json可用(管理员可能已将其禁用)
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。