jira-mcp
Provides tools for interacting with a self-hosted Jira Server, enabling searching issues, creating and updating issues, managing transitions and comments, listing projects and issue types, and validating authentication.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
jira-mcp —— 自托管 Jira 的轻量 MCP Server
一个面向 自托管 Jira Server 7.3.6(私有化部署,如 https://jira.gacrnd.com:8443)
的最小 MCP 服务器。官方 Atlassian Rovo MCP 只支持 Cloud(*.atlassian.net),
这个项目填补自托管场景的空缺,直连 Jira REST API v2,通过 MCP 协议把 Jira 能力
暴露给 Claude Desktop / Cursor / Hermes / VS Code Copilot 等 AI 客户端。
本文档既包含快速上手,也包含完整的设计说明,供维护 / 扩展 / 接入使用。
目录
Related MCP server: jira-mcp-bridge
特性
stdio 传输,任何 MCP 客户端都能挂(Claude Desktop / Cursor / Hermes / VS Code Copilot)
Basic Auth 认证(7.3.6 无 PAT),支持自签证书(默认不校验 SSL)
14 个工具:搜索 / 查询 / 建单 / 改单 / 流转 / 评论 / 项目与类型枚举 / 身份校验 / 保存筛选器 / 问题表格 / 缺陷克隆(clone++)
服务端做 payload 精简,抗 LLM 工具结果截断
快速上手
环境要求
Python 3.10+ ,已装
mcp、requests
安装
cd /media/tommy/win_documents/code/mcp/jira-mcp
pip install -r requirements.txt配置
复制 .env.example 内容到你的客户端配置或 export 环境变量:
export JIRA_URL=https://jira.gacrnd.com:8443
export JIRA_USERNAME=<你的账号>
export JIRA_PASSWORD=<密码>认证说明:Jira 7.3.6 没有 Personal Access Token(8.14 才引入),只能用 Basic Auth(账号 + 密码)。建议用一个专用服务账号,别用个人主账号。 若账号走 SSO/LDAP,确认该账号能用密码做 REST 认证。
挂载示例
Claude Desktop(claude_desktop_config.json)
{
"mcpServers": {
"jira-gac": {
"command": "python3",
"args": ["/media/tommy/win_documents/code/mcp/jira-mcp/server.py"],
"env": {
"JIRA_URL": "https://jira.gacrnd.com:8443",
"JIRA_USERNAME": "你的账号",
"JIRA_PASSWORD": "你的密码",
"JIRA_VERIFY_SSL": "false"
}
}
}
}Hermes Agent
见 hermes mcp 相关命令或 hermes-mcp-setup skill,把上面的 command/args/env
加到 MCP 服务器列表即可(stdio 类型)。
快速自测
# 仅测工具注册(不连 Jira)
python3 - <<'PY'
import server
print("tools:", [t for t in dir(server) if t.startswith("jira_")])
PY工具清单
工具 | 说明 |
jira_get_myself | 校验账号可用(排查认证) |
jira_search | JQL 搜索 issue |
jira_list_filters | 列出当前账号的保存筛选器 |
jira_search_by_filter | 按筛选器(id/名称)搜索 |
jira_issue_table | 问题导航器表格(页面同款列配置) |
jira_get_issue | 按 key 查单个 issue 全字段 |
jira_list_projects | 列出可见项目 |
jira_get_issue_types | 列项目可用 issue 类型(可能是中文) |
jira_create_issue | 建单 |
jira_update_issue | 改单(summary/description/assignee/priority) |
jira_list_transitions | 列可用的状态流转 |
jira_transition_issue | 按名称流转状态 |
jira_add_comment | 加评论 |
jira_clone_issue | 缺陷克隆 clone++(拷贝到手机APP联调项目 + Cloners 关联 + 拷贝评论/附件) |
背景与动机
官方 Atlassian Rovo MCP Server(github.com/atlassian/atlassian-mcp-server)仅面向
Cloud(*.atlassian.net),其入口 mcp.atlassian.com 无法代理自托管(Server / Data Center)
实例。本项目直连 自托管 Jira Server 7.3.6 的 REST API v2,填补自托管场景的空缺。
关键约束(决定了多项设计选择)
约束 | 影响 |
Jira 7.3.6 < 8.14,无 Personal Access Token(PAT 8.14 才引入) | 认证只能走 Basic Auth(账号 + 密码) |
自托管实例使用自签证书(8443 端口) | 默认关闭 SSL 校验,并抑制 |
社区 | 不依赖它,自己实现最小 server |
账号可能走 SSO/LDAP | Basic Auth 仍按用户目录校验;建议用专用服务账号 |
AI 客户端工具结果有大小上限(沙箱 ~ 数百 KB) | 搜索结果必须精简摘要,丢弃巨型字段 |
设计目标
最小依赖、单文件:
server.py一个文件即可运行,仅依赖mcp+requests。stdio 传输:任何 MCP 客户端都能挂,无需网络端口 / 进程常驻管理。
AI 友好:工具返回值统一
{"ok": bool, ...},永不抛异常,错误信息可读, 让 LLM 客户端能读取错误并继续,而不是被异常打断。抗截断:对搜索 / 表格等大 payload 做结构化精简,只保留对 LLM 有用的字段。
可诊断:提供身份校验工具、端到端验证脚本,便于排查认证 / 连通性问题。
整体架构
┌─────────────────────────────────────────────────────────────┐
│ MCP 客户端 (Claude Desktop / Cursor / Hermes / Copilot) │
└───────────────────────────────┬─────────────────────────────┘
│ stdio (JSON-RPC 2.0)
│ initialize / tools/list / tools/call
┌───────────────────────────────▼─────────────────────────────┐
│ FastMCP("jira-gac") ← MCP SDK 层 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 14 个 @mcp.tool() 处理器 │ │
│ │ jira_get_myself / jira_search / jira_list_filters / │ │
│ │ jira_search_by_filter / jira_issue_table / │ │
│ │ jira_get_issue / jira_create_issue / jira_update_issue │ │
│ │ jira_list_transitions / jira_transition_issue / │ │
│ │ jira_add_comment / jira_list_projects / │ │
│ │ jira_get_issue_types / jira_clone_issue │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ JiraClient ← HTTP 客户端层(requests.Session)│ │
│ │ · Basic Auth / Bearer / SSL 开关 │ │
│ │ · request() 统一请求 + 错误规整 │ │
│ │ · post_issue_table() 内部接口专用(CSRF 头) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ JiraError ← 错误规整层 │ │
│ │ · errorMessages / errors → 可读字符串 │ │
│ └─────────────────────────────────────────────────────────┘ │
└───────────────────────────────┬─────────────────────────────┘
│ HTTPS (verify 可关)
┌───────────────────────────────▼─────────────────────────────┐
│ 自托管 Jira Server 7.3.6 │
│ · REST API v2 : /rest/api/2/... │
│ · 内部接口 : /rest/issueNav/1/issueTable │
└─────────────────────────────────────────────────────────────┘分层职责:
工具层(
@mcp.tool())只做「入参校验 → 调 client → 精简 → 包_ok/_err」,不含 HTTP 细节。客户端层(
JiraClient)只做「拼 URL → 发请求 → 解析 JSON → 抛JiraError」。规整层(
JiraError/_name/_summarize_*)负责「原始 Jira 数据 → LLM 友好输出」。
配置设计
全部通过环境变量注入,避免硬编码凭据,便于客户端配置与 CI 隔离。
环境变量 | 默认值 | 说明 |
|
| Jira 根地址,末尾 |
| 空 | Basic Auth 用户名(7.3.6 主认证方式) |
| 空 | Basic Auth 密码 |
| 空 | 可选;Jira 8.14+ 的 PAT,7.3.6 忽略 |
|
|
|
| 空 | 预留:缺省 project key(当前工具未消费,留作扩展) |
认证优先级(见 JiraClient.__init__):
若设
JIRA_PERSONAL_TOKEN→Authorization: Bearer <token>(8.14+);否则若设
JIRA_USERNAME→requests的session.auth = (username, password)(HTTP Basic);两者皆无 → 匿名请求(工具调用会收到 401,规整为
ok=false错误,不崩溃)。
SSL 处理:
JIRA_VERIFY_SSL = os.environ.get("JIRA_VERIFY_SSL", "false").lower() in ("1", "true", "yes")
if not JIRA_VERIFY_SSL:
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)session.verify = JIRA_VERIFY_SSL决定 TLS 证书校验;关闭校验时抑制
InsecureRequestWarning,避免 stdio 上刷屏。
安全建议:优先用专用服务账号而非个人主账号;
JIRA_DEFAULT_PROJECT为预留字段, 当前未在工具逻辑中消费,后续可用来简化建单参数。
HTTP 客户端层设计
JiraError — 错误规整
把 Jira 返回的错误体规整成一条可读字符串,供 LLM 直接展示:
class JiraError(Exception):
def __init__(self, status, data):
self.status = status
self.data = data
msgs = []
if isinstance(data, dict):
msgs.extend(data.get("errorMessages") or []) # 顶层错误消息数组
msgs.extend(f"{k}: {v}" for k, v in (data.get("errors") or {}).items()) # 字段级错误
super().__init__("; ".join(msgs) or f"HTTP {status}")兼容 Jira 两种错误体:全局
errorMessages(字符串数组)与字段级errors(dict)。两者皆无时退化为
HTTP <status>,保证总有可读信息。
JiraClient — 请求封装
class JiraClient:
def __init__(self):
self.session = requests.Session() # 连接复用(连接池 / Keep-Alive)
self.session.verify = JIRA_VERIFY_SSL
self.session.headers["Accept"] = "application/json"
# ... 认证设置见「配置设计」使用
requests.Session复用底层连接,避免每次工具调用重新握手(自签 8443 握手开销不小)。_url(path):JIRA_URL + "/rest/api/2/" + path.lstrip("/"),统一 API v2 前缀。request(method, path, **kwargs):核心统一方法——默认
timeout=30;204 No Content→ 返回None;尝试
resp.json(),解析失败则退化为{"raw": resp.text[:2000]}(截断防超大文本);status >= 400→ 抛JiraError。
get/post/put为便捷方法。post_issue_table(...):内部接口专用(见「内部接口专项设计」),直接 POST 而非走request(),因为目标/rest/issueNav/1/issueTable不在/rest/api/2/下,且需要form-urlencodedbody + CSRF 免检头 + XHR 头。
工具 API 设计
统一响应规约
每个工具只返回 JSON,不抛异常,两种形态:
def _ok(data): return {"ok": True, "data": data}
def _err(e): return {"ok": False, "error": str(e)}成功:
{"ok": true, "data": <精简后的结构>}失败:
{"ok": false, "error": "<可读错误>"}
设计意图:AI 客户端通过
ok判断成败,失败时读error继续推理或重试, 不会被 Python 异常中断整条工具调用链路。
工具清单与数据流
工具 | 方法 | Jira 端点 | 写副作用 | 输出精简策略 |
| GET |
| 无 | 原始(诊断用) |
| GET |
| 无 |
|
| GET |
| 无 |
|
| GET |
| 无 |
|
| POST |
| 无 |
|
| GET |
| 无 | 原始(单条) |
| GET |
| 无 |
|
| GET |
| 无 |
|
| POST |
| 有 |
|
| PUT |
| 有 |
|
| GET |
| 无 |
|
| POST |
| 有 |
|
| POST |
| 有 |
|
| GET+POST |
| 有 |
|
| GET+POST |
| 有 |
|
| — | 进程内任务表 | 无 |
|
各工具设计要点
jira_get_myself — 身份诊断入口。返回 client.get("myself") 原始结果,用于排查
认证是否可用。匿名/密码错误时返回 ok=false 且含可读错误,不崩溃。
jira_search(jql, max_results=20, fields=DEFAULT_FIELDS, raw=False)
DEFAULT_FIELDS = "summary,status,priority,issuetype,assignee,reporter,created,updated", 显式指定字段列表,避免拉取全字段(默认会带 avatarUrls/expand 等冗余)。raw=False走精简;raw=True返回 Jira 原始完整结果(调用方明确需要 description/attachments 等大字段时才用)。
jira_list_filters / jira_search_by_filter — 保存筛选器支持
Jira 保存的筛选器在
GET /filter/favourite(收藏)与/filter/my(我的), 每项含id/name/jql。_resolve_filter_jql(filter_ref)解析规则:filter_ref是纯数字 → 当作 id 直接GET /filter/{id}读 jql;否则在
favourite/my两个端点里按 id 或名称匹配;都未命中 → 返回
ok=false,提示可用jira_list_filters查看。
结果额外附
filter(名称)与jql(解析出的 JQL),便于调用方继续扩展 JQL。
jira_issue_table — 问题导航器内部接口(详见「内部接口专项设计」)
jira_get_issue(key) — 按 key 查单条,返回原始字段(单条 payload 小,无需精简)。
jira_get_issue_types(project_key) — 建单前的类型枚举
调
GET /issue/createmeta?projectKeys=<k>&expand=projects.issuetypes;返回
[{id, name}]。name 可能本地化(中文「任务/缺陷」),建单前必须用它确认。
jira_create_issue(project_key, summary, description="", issue_type="Task", assignee="", priority="")
issue_type/priority/assignee均用名称(Server 版语义,非 Cloud 的 accountId):issuetype: {"name": ...}、priority: {"name": ...}、assignee: {"name": ...};空值字段不写入
fields,避免误提交;
成功返回
{key, id, url},url直接拼成/browse/{key}供用户点击。
jira_update_issue(key, summary/description/assignee/priority)
只把非空字段放入
fields(增量更新);全空 → 返回
ok=false「没有传入任何要更新的字段」,避免无意义 PUT。
jira_list_transitions / jira_transition_issue
list返回[{id, name}];transition支持按名称或 id定位目标流转(名称本地化,所以支持 id 兜底);未命中 → 返回
ok=false并列出所有可用名称,方便客户端纠正。
jira_clone_issue — 缺陷克隆 clone++(PRD「测试账号缺陷分配」流程第 3 步)
把源缺陷克隆到「手机APP联调项目」(默认 T9MLBUG,覆盖所有车型、按【影响车型】确定车型来源),
用于把手机 App 类缺陷集中到联调项目里做前端/后端分析与分配。签名:
jira_clone_issue(source_key, target_project="T9MLBUG", vehicle_model="",
app_system="", reproducibility="", severity="", app="传祺",
component="", assignee="", copy_comments=True, copy_attachments=True,
attachments_async=False, max_single_attach_mb=100)流程:
读源:只拉
CLONE_SOURCE_FIELDS(summary/description/priority/labels/components/ comment/attachment/project/issuetype),避免 175 KB 全量 payload。定【影响车型】:
vehicle_model入参优先;否则_guess_vehicle_model从源项目名推断 (去「项目质量问题/质量问题/项目」后缀后精确/唯一前缀匹配目标项目可选值)。推断不出 → 返回ok=false并列出全部可选值,由调用方(LLM)补传,绝不带错值建单。补必填字段:目标项目
BUG类型的必填自定义字段(app系统/再现率/严重程度/APP)按入参 或默认值填充;严重程度缺省时按优先级 → 严重程度映射(High→A_Critical、Medium→B_Major…)。建单:
POST /issue,复制 summary/description/priority/labels/组件。关联:
POST /issueLink,Cloners类型,outwardIssue=源、inwardIssue=克隆(与 Jira 原生 clone 同向)。clone++:按需拷贝评论(加「克隆自 <源key>」前缀)与附件,best-effort,单条失败跳过不影响整体。 附件拷贝规则(不再因总量大而整体跳过):
场景
行为
单条 issue 附件总量 ≤
attachments_sync_limit_mb(默认 15MB)同步拷贝,结果含逐文件状态
总量 > 15MB
自动转后台线程异步拷贝(
attachments_async=True),主流程先返回job_id,不阻塞建单单个附件 >
max_single_attach_mb(默认 100MB)跳过该附件,并在结果里标注文件名与大小
下载/上传失败
自动重试
ATTACH_RETRY(默认 2 次),仍失败则记录error原因实现要点:附件流式落临时盘再 multipart 重传(避免几十 MB 日志一次性进内存), 每轮结束删除临时文件(含重试场景),上传走
X-Atlassian-Token: no-check绕过 XSRF。 漏传附件可用jira_copy_attachments(source_key, target_key)补拷, 异步任务用jira_attachment_job_status(job_id)查进度。
字段 id 映射(实例专属常量,见 server.py 顶部):customfield_12009=影响车型、
customfield_15405=app系统、customfield_11616=再现率、customfield_11600=严重程度、
customfield_16000=APP。若字段被重建,用 GET /rest/api/2/field 重新确认。
抗截断设计
MCP 工具结果过大(如一次 raw 搜索 50 条 issue 约 180 KB)会撑爆 LLM 的工具结果沙箱。 本项目在服务端做精简,把体积压到 ~1/10:
_name(obj) — 字段规整
Jira 字段值可能是 dict(如 {"name": ..., "displayName": ..., "key": ...})、str 或
None。统一规整成可读字符串:
def _name(obj):
if isinstance(obj, dict):
return obj.get("name") or obj.get("displayName") or obj.get("key") or ""
if isinstance(obj, str):
return obj
return ""_summarize_issue — 单条 issue 精简
return {
"key", "summary", "status", "priority", "type",
"assignee", "reporter", "created", "updated",
}丢弃
avatarUrls/iconUrl/expand/ 嵌套对象等冗余字段;状态/优先级/类型/经办人/报告人全部 flatten 成字符串。
_summarize_search — 搜索结果精简
保留分页元信息 total / startAt / maxResults + 精简后的 issues 列表。
_summarize_issue_table — 表格结果精简(最激进)
issueTable 接口返回的 table 字段是渲染好的 HTML(几百 KB)。只保留:
total, displayed, page, pageSize, startIndex, sortBy, columnConfig, columns, issueKeys丢弃 HTML table 字段本身。
内部接口专项设计
jira_issue_table 是对 Jira 问题导航器内部接口的封装,与标准 REST v2 不同:
端点:
POST /rest/issueNav/1/issueTable(不在/rest/api/2/前缀下);请求体:
application/x-www-form-urlencoded;参数:
startIndex/filterId/jql/layoutKey;必要请求头:
X-Atlassian-Token: no-check— 跳过 Jira 的 XSRF 校验;X-Requested-With: XMLHttpRequest— 标记为 AJAX 请求。
与 jira_search 的区别:本工具返回 Jira 页面同款的列配置与排序
(columnConfig / sortBy),更适合还原「我在 Jira 页面上看到的那个列表」。
默认 JQL:assignee = currentUser() AND resolution = Unresolved order by updated DESC,
即「当前账号未解决的问题」,配合 jira-unresolved-issues skill 使用。
错误处理设计
场景 | 行为 |
HTTP 4xx/5xx |
|
JSON 解析失败 | 退化为 |
204 无内容 | 返回 |
筛选器未命中 |
|
状态流转未命中 |
|
更新无字段 |
|
无凭据 | 请求返回 401 → 规整为 |
设计原则:所有错误都走 {"ok": false, "error": <可读文本>},错误文本尽量带上
「下一步建议」(如「可用 jira_list_filters 查看」)。
关键设计决策与权衡
决策 | 理由 | 权衡 |
单文件 | 部署极简,stdio 挂载无路径依赖 | 扩展性靠新增函数,非模块化 |
服务端做 payload 精简 | 抗截断,省 LLM token |
|
认证走 Basic Auth | 7.3.6 无 PAT 的硬约束 | 密码明文走 HTTPS,需专用账号 |
默认关 SSL 校验 | 自签证书环境 | 有中间人风险,仅内网可接受 |
工具永不抛异常 | LLM 能读错误继续推理 | 客户端需自己判断 |
| Server 版语义 | Cloud 用 |
名称 + id 双通道定位(流转/筛选器) | 名称本地化易错,id 稳定 | 略增解析逻辑 |
测试与验证设计
项目内置三类验证脚本,分层覆盖:
脚本 | 层 | 说明 |
| 传输 + 注册 | 以真实 MCP 客户端身份拉起 server,验证握手 + 工具注册 + 一次调用(无凭据 → 应得 |
| 端到端 | 从 |
| 只读工具 | 直接 import server,覆盖 |
| 实连 | 加载 |
验证原则:不要只 import 模块做 Python 内省 —— 必须走 MCP stdio 传输做
initialize + list_tools + call_tool,才能发现传输层 / 注册层的 bug。
已知坑:stdio server 会把 INFO 日志(Processing request of type ...)打到 stdout,
握手时无害(客户端读 JSON-RPC 帧而非裸 stdout),但 shell 一行式调用时应 2>/dev/null 抑制。
已知限制与注意点
建单前先确认类型名:
issue_type/priority名称可能本地化(中文环境「任务/缺陷」), 先用jira_get_issue_types确认精确名称。description是纯字符串:7.x 上 wiki/plain 渲染按字段配置,纯字符串两者兼容。代理会破坏自托管 Jira 的 TLS:
jira.gacrnd.com解析到内网 IP,但 shell 的https_proxy(如 Clash127.0.0.1:7897)仍会命中,因为no_proxy的 CIDR 条目 (172.16.0.0/12)匹配的是主机名而非解析后的 IP。CONNECT 隧道导致 TLS 握手 失败报SSL: UNEXPECTED_EOF_WHILE_READING(非证书错误)。修复:把主机名加入no_proxy(如.gacrnd.com),或对 session 设trust_env=False/ 显式空代理。编辑 server.py 后需重启会话:Hermes 在会话启动时发现 MCP 工具;改动后需
/reset(CLI)//restart(gateway)才生效。JIRA_DEFAULT_PROJECT尚未消费:预留字段,后续可用于建单默认项目。工具永不抛异常:每个工具返回
{"ok": true, "data": ...}或{"ok": false, "error": ...},失败不会抛异常,便于 AI 客户端读取错误继续处理。
扩展方向
分页遍历:
jira_search当前单页返回,可加startAt遍历;消费
JIRA_DEFAULT_PROJECT:建单时缺省 project key;附件 / 字段配置读取:接入
raw=True已有数据,封装成专用工具;8.14+ PAT 兼容:
JIRA_PERSONAL_TOKEN已预留,升级实例后自动走 Bearer;权限白名单:为写操作(建单/改单/流转/评论)加项目级 / 账号级开关,控制 AI 写权限。
附录:文件清单
文件 | 作用 |
| 主程序,全部工具 + 客户端实现 |
| 依赖: |
| 本文档:快速上手 + 详细设计 |
| 环境变量模板 |
| 端到端握手 + 新工具探测 |
| stdio 握手 + 注册自测 |
| 只读工具覆盖测试 |
| 实连冒烟测试 |
| 缺陷克隆 clone++ 实连测试(真实建单,测试后请删除) |
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Official Fibery MCP server: query and update your workspace from any AI client via OAuth.
MCP server that lets AI assistants use all OneSchema features exposed via the public API.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceMCP server that wraps the jira-cli command-line tool to enable AI assistants to interact with Jira.8 npm9MIT
- AlicenseNot gradedqualityDmaintenanceA local-only MCP server providing safe, typed Jira tools for AI agents via Atlassian ACLI, enabling search, get issue, add comment, and transition issues with policy guardrails.MIT
- AlicenseNot gradedqualityDmaintenanceA server that exposes Jira Cloud operations as MCP tools, enabling programmatic management of Epics, Stories, Tasks, and Sprints directly from an AI chat or agentic workflow.1MIT
- AlicenseNot gradedqualityDmaintenanceA lightweight MCP server that exposes Jira issue operations (get, search, create) as tools for AI clients like Claude.226 npmISC