Skip to main content
Glama

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)

  • 13 个工具:搜索 / 查询 / 建单 / 改单 / 流转 / 评论 / 项目与类型枚举 / 身份校验 / 保存筛选器 / 问题表格

  • 服务端做 payload 精简,抗 LLM 工具结果截断

快速上手

环境要求

  • Python 3.10+ ,已装 mcprequests

安装

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

加评论


背景与动机

官方 Atlassian Rovo MCP Servergithub.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 校验,并抑制 InsecureRequestWarning

社区 sooperset/mcp-atlassian 要求 8.14+

不依赖它,自己实现最小 server

账号可能走 SSO/LDAP

Basic Auth 仍按用户目录校验;建议用专用服务账号

AI 客户端工具结果有大小上限(沙箱 ~ 数百 KB)

搜索结果必须精简摘要,丢弃巨型字段


设计目标

  1. 最小依赖、单文件server.py 一个文件即可运行,仅依赖 mcp + requests

  2. stdio 传输:任何 MCP 客户端都能挂,无需网络端口 / 进程常驻管理。

  3. AI 友好:工具返回值统一 {"ok": bool, ...}永不抛异常,错误信息可读, 让 LLM 客户端能读取错误并继续,而不是被异常打断。

  4. 抗截断:对搜索 / 表格等大 payload 做结构化精简,只保留对 LLM 有用的字段。

  5. 可诊断:提供身份校验工具、端到端验证脚本,便于排查认证 / 连通性问题。


整体架构

┌─────────────────────────────────────────────────────────────┐
│  MCP 客户端 (Claude Desktop / Cursor / Hermes / Copilot)     │
└───────────────────────────────┬─────────────────────────────┘
                                │ stdio (JSON-RPC 2.0)
                                │ initialize / tools/list / tools/call
┌───────────────────────────────▼─────────────────────────────┐
│  FastMCP("jira-gac")                ← MCP SDK 层             │
│  ┌─────────────────────────────────────────────────────────┐ │
│  │  13 个 @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                                   │ │
│  └─────────────────────────────────────────────────────────┘ │
│  ┌─────────────────────────────────────────────────────────┐ │
│  │  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_URL

https://jira.gacrnd.com:8443

Jira 根地址,末尾 / 会被 rstrip 去掉

JIRA_USERNAME

Basic Auth 用户名(7.3.6 主认证方式)

JIRA_PASSWORD

Basic Auth 密码

JIRA_PERSONAL_TOKEN

可选;Jira 8.14+ 的 PAT,7.3.6 忽略

JIRA_VERIFY_SSL

false

true/false;自签证书设 false

JIRA_DEFAULT_PROJECT

预留:缺省 project key(当前工具未消费,留作扩展)

认证优先级(见 JiraClient.__init__):

  1. 若设 JIRA_PERSONAL_TOKENAuthorization: Bearer <token>(8.14+);

  2. 否则若设 JIRA_USERNAMErequestssession.auth = (username, password)(HTTP Basic);

  3. 两者皆无 → 匿名请求(工具调用会收到 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-urlencoded body + 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 端点

写副作用

输出精简策略

jira_get_myself

GET

/myself

原始(诊断用)

jira_search

GET

/search

_summarize_search

jira_list_filters

GET

/filter/favourite/filter/my

[{id,name,jql}]

jira_search_by_filter

GET

/filter/{id} + /search

_summarize_search + filter/jql

jira_issue_table

POST

/rest/issueNav/1/issueTable

_summarize_issue_table

jira_get_issue

GET

/issue/{key}

原始(单条)

jira_list_projects

GET

/project

[{key,name,id,type}]

jira_get_issue_types

GET

/issue/createmeta

[{id,name}]

jira_create_issue

POST

/issue

{key,id,url}

jira_update_issue

PUT

/issue/{key}

{key,updated:[...]}

jira_list_transitions

GET

/issue/{key}/transitions

[{id,name}]

jira_transition_issue

POST

/issue/{key}/transitions

{key,transitioned_to}

jira_add_comment

POST

/issue/{key}/comment

{key,comment_id}

各工具设计要点

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) 解析规则:

    1. filter_ref 是纯数字 → 当作 id 直接 GET /filter/{id} 读 jql;

    2. 否则在 favourite / my 两个端点里按 id 或名称匹配;

    3. 都未命中 → 返回 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 并列出所有可用名称,方便客户端纠正。


抗截断设计

MCP 工具结果过大(如一次 raw 搜索 50 条 issue 约 180 KB)会撑爆 LLM 的工具结果沙箱。 本项目在服务端做精简,把体积压到 ~1/10:

_name(obj) — 字段规整

Jira 字段值可能是 dict(如 {"name": ..., "displayName": ..., "key": ...})、strNone。统一规整成可读字符串:

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 成字符串。

保留分页元信息 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 页面上看到的那个列表」。

默认 JQLassignee = currentUser() AND resolution = Unresolved order by updated DESC, 即「当前账号未解决的问题」,配合 jira-unresolved-issues skill 使用。


错误处理设计

场景

行为

HTTP 4xx/5xx

request()JiraError,工具捕获后 _err(e)

JSON 解析失败

退化为 {"raw": text[:2000]},避免异常

204 无内容

返回 None(如 PUT 成功无 body)

筛选器未命中

_err("找不到筛选器 ...") 附排查提示

状态流转未命中

_err(...) 附可用名称列表

更新无字段

_err("没有传入任何要更新的字段")

无凭据

请求返回 401 → 规整为 ok=false,不崩溃

设计原则:所有错误都走 {"ok": false, "error": <可读文本>},错误文本尽量带上 「下一步建议」(如「可用 jira_list_filters 查看」)。


关键设计决策与权衡

决策

理由

权衡

单文件 server.py

部署极简,stdio 挂载无路径依赖

扩展性靠新增函数,非模块化

服务端做 payload 精简

抗截断,省 LLM token

raw=True 需显式 opt-in 才能拿全量

认证走 Basic Auth

7.3.6 无 PAT 的硬约束

密码明文走 HTTPS,需专用账号

默认关 SSL 校验

自签证书环境

有中间人风险,仅内网可接受

工具永不抛异常

LLM 能读错误继续推理

客户端需自己判断 ok

assignee{"name": ...}

Server 版语义

Cloud 用 accountId,不可照抄 Cloud 示例

名称 + id 双通道定位(流转/筛选器)

名称本地化易错,id 稳定

略增解析逻辑


测试与验证设计

项目内置三类验证脚本,分层覆盖:

脚本

说明

test_handshake.py

传输 + 注册

以真实 MCP 客户端身份拉起 server,验证握手 + 工具注册 + 一次调用(无凭据 → 应得 ok=false 而非异常)

verify_server.py

端到端

~/.hermes/config.yaml 读凭据(不落盘密码),握手 + 断言 jira_list_filters/jira_search_by_filter 已注册 + 探测两工具

test_readonly.py

只读工具

直接 import server,覆盖 get_issue_types / get_issue / list_transitions(无写副作用)+ 错误分支

test_live.py

实连

加载 .env,真实调 get_myself / list_projects / search

验证原则:不要只 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 抑制。


已知限制与注意点

  1. 建单前先确认类型名issue_type / priority 名称可能本地化(中文环境「任务/缺陷」), 先用 jira_get_issue_types 确认精确名称。

  2. description 是纯字符串:7.x 上 wiki/plain 渲染按字段配置,纯字符串两者兼容。

  3. 代理会破坏自托管 Jira 的 TLSjira.gacrnd.com 解析到内网 IP,但 shell 的 https_proxy(如 Clash 127.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 / 显式空代理。

  4. 编辑 server.py 后需重启会话:Hermes 在会话启动时发现 MCP 工具;改动后需 /reset(CLI)/ /restart(gateway)才生效。

  5. JIRA_DEFAULT_PROJECT 尚未消费:预留字段,后续可用于建单默认项目。

  6. 工具永不抛异常:每个工具返回 {"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 写权限。


附录:文件清单

文件

作用

server.py

主程序,全部工具 + 客户端实现

requirements.txt

依赖:mcp>=1.20.0requests>=2.31.0

README.md

本文档:快速上手 + 详细设计

.env.example

环境变量模板

verify_server.py

端到端握手 + 新工具探测

test_handshake.py

stdio 握手 + 注册自测

test_readonly.py

只读工具覆盖测试

test_live.py

实连冒烟测试

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…

  • MCP server exposing the Backtest360 engine API as tools for AI agents.

  • MCP server for AI access to SmartBear tools, including BugSnag, Reflect, Swagger, PactFlow, QTM4J.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Pandacli/jira-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server