Jira AI Tool Server
by JustWang1110
README.md
# Jira AI Tool Server
一个面向后续插件化的大模型 Jira 能力内核。
## 当前状态
当前版本提供 17 个只读工具,外加 7 个受控写入 / 本地配置工具:
- `jira.search_issues`
- `jira.get_issue`
- `jira.get_comments`
- `jira.get_transitions`
- `jira.get_permissions`
- `jira.get_project_snapshot`
- `jira.find_stale_issues`
- `jira.get_assignee_workload`
- `jira.get_status_distribution`
- `jira.find_high_priority_open_issues`
- `jira.analyze_project_risks`
- `jira.build_weekly_summary`
- `jira.list_projects`
- `jira.refresh_project_index`
- `jira.resolve_project`
- `jira.search_named_filter`
- `jira.search_issues_by_filters`
- `jira.upsert_name_mapping`
- `jira.add_comment`
- `jira.add_review_summary_comment`
- `jira.transition_issue`
- `jira.update_issue_fields`
- `jira.sync_review_outcome`
- `jira.ide_sync_review_outcome`
并提供统一调度层:
- `ToolDispatcher.dispatch(tool_name, payload)`
- `ToolDispatcher.list_tools()`
- `ToolDispatcher.describe(tool_name)`
以及一个最小 HTTP 服务:
- `GET /health`
- `GET /tools`
- `GET /tools/{tool_name}`
- `POST /invoke/{tool_name}`
运行时还会依据 `schemas/` 中的输入契约执行校验;
这意味着模型或外部调用方即使构造了错误参数,也会先被拦在 dispatcher 层。
同样地,工具输出也会按 response schema 反向校验;
如果某个实现悄悄返回了不符合契约的结构,dispatcher 会把它收敛成 `INVALID_TOOL_OUTPUT`。
调用链路也已支持审计记录;HTTP 服务启动时可把事件追加写入 JSONL 文件,
为未来写操作、审批和排障保留事实依据。
## 目录
```text
docs/ 协议与说明
schemas/ JSON Schema
tools/ 工具注册表
src/ 最小可运行内核
tests/ 基础测试
```
## 环境变量
```text
JIRA_BASE_URL
JIRA_USERNAME
JIRA_PASSWORD
JIRA_TIMEOUT_SECONDS
HTTP_HOST
HTTP_PORT
AUDIT_FILE_PATH
```
## 本地配置文件
复制示例文件:
```bash
copy config\local.example.json config\local.json
```
第一次使用时,可在 `config/local.json` 里填写明文密码:
```json
{
"jira": {
"base_url": "https://jira.example.com",
"username": "your-username",
"password": "首次启动前填写明文"
}
}
```
项目启动后会自动移除明文字段,并改写为平台对应的安全引用:
- Windows:使用 DPAPI,把密文写回配置文件
- macOS:把密码写入系统 Keychain,配置文件只保存引用
因此:
- Windows 密文通常只能由当前用户、当前机器解开
- macOS 密码由 Keychain 代管,配置文件不再保存密码本体
`config/local.json` 已被加入 `.gitignore`,不要提交到仓库。
### 项目别名与缓存
为了让“昆山人民项目”这类口语化说法稳定映射到 Jira 项目,
配置文件支持维护人工别名:
```json
{
"projects": {
"index_file_path": "data/project-index.json",
"ttl_seconds": 86400,
"aliases": {
"昆山人民": "IHGS2512222C"
}
}
}
```
运行时默认优先读取本地索引;只有索引缺失、过期,或显式要求刷新时,
才会重新向 Jira 拉取项目列表。
项目分析工具同时支持:
```json
{"project_key": "IHGS2512222C"}
```
或:
```json
{"project_name": "昆山人民"}
```
当传入 `project_name` 时,工具内部会自动走本地索引与别名解析。
### 项目优先分单与路由规则(最小改造)
如果希望“某些项目优先分给固定人员”,可在 `projects.directory` 的项目条目中增加:
```json
{
"key": "IHZB2404585A",
"name": "上海曙光医院安徽医院",
"product_category": "RS6",
"province": "安徽省",
"preferred_assignee": "w_tang"
}
```
- `preferred_assignee`:项目级优先处理人(Jira 账号)
- 命中该字段后,可直接优先分配给该账号
- 若未配置该字段,再走通用自动命中规则
配套的“最小改造版”路由配置可放在 `local.json` 根节点(与现有 `projects`、`members` 并列):
```json
{
"routing": {
"enabled": true,
"project_preferred_assignee_field": "preferred_assignee",
"match_order": [
"project_preferred_assignee",
"province_product_category_module",
"province_product_category",
"product_category_module",
"module_only"
],
"min_hit": 5,
"effort_estimation": {
"method": "mean",
"source_field": "折算工时(小时)",
"min_sample": 5,
"fallback_order": [
"province_product_category_module",
"province_product_category",
"product_category_module",
"module_only",
"product_category_global"
],
"precision": 1
},
"outlier_filter": {
"enabled": true,
"lower_percentile": 5,
"upper_percentile": 95
},
"scoring_weights": {
"hit": 0.6,
"efficiency": 0.25,
"load": 0.15
}
}
}
```
带中文备注的可读版(示例):
```json
{
"_comment": "分单命中规则配置(含工时预估)",
"routing": {
"enabled": true,
"_comment_enabled": "总开关,false 时不启用自动分单",
"project_preferred_assignee_field": "preferred_assignee",
"_comment_project_preferred_assignee_field": "项目条目中的优先分配字段名",
"match_order": [
"project_preferred_assignee",
"province_product_category_module",
"province_product_category",
"product_category_module",
"module_only"
],
"_comment_match_order": "命中顺序:从上到下依次尝试,命中即停止",
"min_hit": 5,
"_comment_min_hit": "候选人最小样本量,低于该值不参与排序",
"effort_estimation": {
"method": "mean",
"_comment_method": "工时预估方式:平均折算工时",
"source_field": "折算工时(小时)",
"min_sample": 5,
"_comment_min_sample": "样本不足时按 fallback_order 逐级回退",
"precision": 1
}
}
}
```
### 命中流程示例(输入 Jira 单 -> 输出命中人 + 预估工时)
以下示例用于验证“项目优先分单 + 自动规则回退 + 平均工时预估”是否按预期工作。
输入(示例 Jira 单):
```json
{
"issue_key": "L1NJ2007002C-855",
"project_key": "L1NJ2007002C",
"project_name": "华东-仪征市人民医院",
"summary": "术语维护中的限制提醒,住院能正常弹窗,门诊未弹窗",
"module": "HIS"
}
```
处理流程:
1. 读取 `projects.directory`,按 `project_key/project_name` 定位项目元数据
2. 检查是否存在 `preferred_assignee`
- 若存在:直接命中该账号
- 若不存在:进入自动规则
3. 自动规则按 `match_order` 依次尝试命中:
- `province + product_category + module`
- `province + product_category`
- `product_category + module`
- `module_only`
4. 在命中层级内按历史样本排序候选人(命中量、效率、负载)
5. 预估工时按当前命中层级样本计算**平均折算工时**
- 样本量小于 `min_sample` 时按 `fallback_order` 逐级回退
输出(示例):
```json
{
"matched_by": "province_product_category_module",
"assignee": "yu-ran",
"assignee_source": "auto_rule",
"estimated_hours": 3.1,
"estimation_method": "mean(折算工时(小时))",
"sample_size": 134,
"candidates_top3": [
{"assignee": "yu-ran", "score": 126.72},
{"assignee": "cheng.l.neu", "score": 119.86},
{"assignee": "ji-t", "score": 79.42}
]
}
```
若项目配置了优先人(例如 `preferred_assignee = w_tang`),输出示例:
```json
{
"matched_by": "project_preferred_assignee",
"assignee": "w_tang",
"assignee_source": "project_config",
"estimated_hours": 2.4,
"estimation_method": "mean(同项目/同类样本)",
"sample_size": 39
}
```
### 命名筛选器
对于“本地化问题”这类团队内部已有共识的概念,可直接在配置中固化:
```json
{
"named_filters": {
"本地化问题": "resolution = Unresolved AND 问题分类 = 本地化"
}
}
```
之后工具可用:
```json
{
"filter_name": "本地化问题",
"project_name": "昆山人民",
"module_name": "医生站",
"extra_jql": "assignee is EMPTY"
}
```
也支持按项目属性做跨项目命名筛选统计:
```json
{
"filter_name": "本地化问题",
"project_filters": {
"product": "RS6"
},
"module_name": "医生站"
}
```
其中:
- `filter_name` 表示问题性质,例如“本地化问题”
- `module_name` 表示模块维度,底层会翻译成 Jira JQL 中的 `component`
- `project_filters` 与 `search_issues_by_filters` 一致,支持 `key/name/team/product/product_category/province/region/industry/owner`
### 按 Jira 界面维度查询
如果你希望精确匹配界面筛选项,可直接使用:
```json
{
"project_name": "昆山人民",
"statuses": ["待处理", "处理中"],
"modules": ["医生站"],
"issue_categories": ["本地化"],
"assignees": ["晓刚"]
}
```
它会生成:
```jql
project = IHGS2512222C
AND status in (待处理, 处理中)
AND component in (医生站)
AND 问题分类 in (本地化)
AND assignee in (kuangxg)
ORDER BY priority DESC, updated DESC
```
如果你希望按项目属性做跨项目统计(例如统计 `RS6` 产品的问题总数),可在 `~/.jira-ai/local.json` 的 `projects.directory` 维护项目元数据,然后在工具中传:
```json
{
"project_filters": {
"product": "RS6"
},
"statuses": ["待处理", "处理中"],
"max_results": 100
}
```
`project_filters` 支持字段:
- `key`
- `name`
- `team`
- `product`
- `product_category`
- `province`
- `region`
- `industry`
- `owner`
每个字段支持传单个字符串或字符串数组;系统会先把匹配到的项目集合翻译成 `project in (...)`,再拼接其他 Jira 维度条件。
同样的 `project_filters` 也可用于以下工具,实现按属性维度的跨项目统计与分析:
- `jira.get_project_snapshot`
- `jira.find_stale_issues`
- `jira.get_assignee_workload`
- `jira.get_status_distribution`
- `jira.find_high_priority_open_issues`
- `jira.analyze_project_risks`
- `jira.build_weekly_summary`
### 导出加班工作簿
可直接用本地 Jira 数据刷新固定模板:
```bash
python -m jira_ai.overtime_export --label 0622加班
```
默认会写回 `C:\Users\MengWang\.jira-ai\第一交付服务中心加班工作任务一览表.xlsx`,也可以通过 `--workbook` 和 `--config` 覆盖路径。
### 中文成员名映射
如果团队成员日常使用中文名沟通,建议在配置中维护成员映射:
```json
{
"members": {
"aliases": {
"晓刚": "kuangxg",
"张三": "zhangsan"
},
"directory": [
{
"jira_username": "kuangxg",
"display_name": "匡晓刚",
"aliases": ["匡工"],
"project_keys": ["IHGS2512222C"]
}
]
}
}
```
这样后续就可以直接说中文名。若模型没有匹配到成员,它会返回候选人,适合继续追问确认。
如需把新的中文别名写回本地配置,可调用:
```json
{
"mapping_type": "member",
"alias": "晓刚",
"target": "kuangxg",
"display_name": "匡晓刚",
"project_keys": ["IHGS2512222C"]
}
```
## 启动前自检
```bash
python -m jira_ai.doctor_cli
```
当前会检查:
- 配置是否成功加载
- Jira REST 服务是否可访问
## 首轮真实联调
当 `doctor` 通过后,可以直接执行:
```bash
python -m jira_ai.smoke_cli --issue-key PROJ-123 --project-key PROJ
```
它会依次验证:
1. 权限检查
2. 单任务读取
3. 评论读取
4. 当前可流转状态读取
5. 项目内 JQL 搜索
## 本地验证
```bash
python -m unittest discover -s tests
```
## 插件构建与发布
构建最新 Codex 插件自包含包:
```powershell
.\scripts\build-plugin.ps1
```
发布指定版本并生成 zip:
```powershell
.\scripts\release-plugin.ps1 -Version 0.1.2
```
发布产物会输出到 `dist/`,其中包括:
- `jira-ai-codex-<version>.zip`
- `jira-ai-codex-<version>-RELEASE.md`
每次发布前,脚本会先同步 `plugins/jira-ai-codex/bundle/`,确保插件离开源码仓库后仍可独立运行。
完整发布流程见:
- [plugin-release-workflow.md](/E:/personal/AI/tool-server/docs/plugin-release-workflow.md)
- [codex-plugin-release-checklist.md](/E:/personal/AI/tool-server/docs/codex-plugin-release-checklist.md)
## 调用示例
```python
from jira_ai.service import ToolDispatcher
dispatcher = ToolDispatcher.from_env()
result = dispatcher.dispatch(
"jira.search_issues",
{"jql": "project = PROJ ORDER BY updated DESC"}
)
```
也可以通过 CLI 直接调用:
```bash
python -m jira_ai.cli jira.search_issues "{\"jql\":\"project = PROJ\"}"
```
## 启动 HTTP 服务
```bash
python -m jira_ai.http_cli --host 127.0.0.1 --port 8765
```
如需显式指定审计日志文件:
```bash
python -m jira_ai.http_cli --audit-file logs/audit.jsonl
```
所有审计事件在落盘前都会对 `password`、`token`、`authorization` 等常见敏感字段做递归脱敏。
示例:
```bash
curl http://127.0.0.1:8765/tools
curl -X POST http://127.0.0.1:8765/invoke/jira.search_issues ^
-H "Content-Type: application/json" ^
-d "{\"jql\":\"project = PROJ\"}"
```
## 受控写入
当前第一条写操作是:
- `jira.add_comment`
- `jira.add_review_summary_comment`
- `jira.transition_issue`
它要求显式审批:
```bash
python -m jira_ai.cli jira.add_comment "{\"issue_key\":\"PROJ-123\",\"body\":\"已完成评审\"}" --approved
```
HTTP 调用时也必须显式带上:
```text
X-Approved: true
```
否则 dispatcher 会返回 `APPROVAL_REQUIRED`,不会真正写入 Jira。
`jira.transition_issue` 还会先读取当前任务可执行的 transition;
只有当你请求的流转名称真实存在于当前工作流中时,才会继续执行。
`jira.update_issue_fields` 则只允许更新配置白名单中的字段。默认白名单:
```json
{
"write_policy": {
"allowed_issue_fields": ["labels", "components"]
}
}
```
如果请求里包含未授权字段,会返回 `FIELD_NOT_ALLOWED`,不会向 Jira 发起修改。
如果你希望把“评审摘要 + 可选流转”一次完成,可以使用:
```json
{
"issue_key": "PROJ-123",
"summary": "修复了患者检索逻辑",
"verdict": "approved",
"transition_name": "提交测试"
}
```
对应工具:
- `jira.sync_review_outcome`
### IDE 助手对接
如果评审和提交代码发生在 IDE 助手中,推荐直接调用:
- `jira.ide_sync_review_outcome`
输入示例:
```json
{
"issue_key": "PROJ-123",
"review_summary": "修复患者检索逻辑,并补充空值保护。",
"verdict": "approved",
"findings": ["未发现阻塞性问题"],
"tests": ["单元测试通过"],
"commit_sha": "abc123",
"commit_message": "fix: 修复患者检索逻辑",
"branch_name": "feature/patient-search",
"review_url": "https://example/reviews/42",
"transition_name": "提交测试"
}
```
该工具会先把 IDE 侧信息归一化,再复用内部的评审同步流程。
例如,代码评审摘要可以这样写回:
```json
{
"issue_key": "PROJ-123",
"summary": "修复了患者检索逻辑,并补充了空值保护。",
"verdict": "approved",
"findings": ["未发现阻塞性问题"],
"tests": ["已通过单元测试"],
"links": ["PR #42"]
}
```
## 设计原则
- 核心能力与插件外壳分离
- Jira 原始 JSON 与内部领域模型分离
- 写操作能力以后按显式权限和审批边界扩展
## 多宿主适配
项目不把能力写死在单一模型产品上,而是采用:
```text
统一能力内核 + 宿主无关工具目录 + 薄适配层
```
相关文档:
- `docs/plugin-architecture.md`
- `docs/integrations/codex.md`
- `docs/integrations/mcp.md`
- `docs/integrations/cursor.md`
- `docs/integrations/github-copilot.md`
## MCP server
当前已提供可运行的 stdio MCP server:
```bash
python -m jira_ai.mcp_stdio
```
它支持:
- `initialize`
- `ping`
- `tools/list`
- `tools/call`
## Codex 插件包
当前仓库已经具备 repo-local Codex 插件包结构:
- `plugins/jira-ai-codex/.codex-plugin/plugin.json`
- `plugins/jira-ai-codex/.mcp.json`
- `.agents/plugins/marketplace.json`
安装与发布说明:
- `plugins/jira-ai-codex/INSTALL.md`
- `docs/codex-plugin-release-checklist.md`
## Cursor 插件包
当前仓库已经具备 repo-local Cursor 插件包结构:
- `plugins/jira-ai-cursor/.cursor-plugin/plugin.json`
- `plugins/jira-ai-cursor/mcp.json`
- `.cursor-plugin/marketplace.json`
构建与安装:
```powershell
.\scripts\build-plugin.ps1 -PluginName jira-ai-cursor
.\plugins\jira-ai-cursor\scripts\doctor.ps1
```
然后执行本地安装脚本并重启 Cursor:
```powershell
.\plugins\jira-ai-cursor\scripts\install-local.ps1
```
插件会注册到 `%USERPROFILE%\.cursor\plugins\local\`,不会出现在 Plugins 市场列表;请在 **Settings → MCP** 与 **Rules** 中确认已加载。
安装与发布说明:
- `plugins/jira-ai-cursor/INSTALL.md`
- `docs/integrations/cursor.md`
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues