mcpField
T
Ticket AI

Ticket AI

本地服务
github.com生产力07 周前更新

关于 Ticket AI

Ticket AI 是 MCP 服务和命令行工具,可从团队的跟踪系统学习工单写作习惯,并据此衡量草稿。分析通过计数完成,不需要模型;使用模型撰写工单则为可选功能。

主要功能

  1. 01从已关闭工单学习写作惯例
  2. 02衡量章节、长度、标签和语言
  3. 03查找相关工单及其变更文件
  4. 04审查草稿和已有工单
  5. 05按优先级查看未关闭工单
  6. 06可选用模型撰写工单

适用场景

  1. 01按团队工单惯例检查草稿
  2. 02撰写工单前查找相关历史工作
  3. 03按优先级审查未关闭事项
  4. 04在 CI 中设置工单审查门槛

MCP 相关

提供八个只读 MCP 工具,用于学习惯例、获取模板和上下文,以及审查草稿、工单和未关闭事项。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"syrian963-ticket-ai": {
"command": "uvx",
"args": [
"ticket-ai-mcp"
],
"env": {
"TICKET_AI_TRACKER": "<TICKET_AI_TRACKER>",
"TICKET_AI_PROJECT": "<TICKET_AI_PROJECT>",
"TICKET_AI_GITLAB_URL": "<TICKET_AI_GITLAB_URL>",
"TICKET_AI_GITLAB_TOKEN": "<TICKET_AI_GITLAB_TOKEN>",
"TICKET_AI_JIRA_URL": "<TICKET_AI_JIRA_URL>",
"TICKET_AI_JIRA_EMAIL": "<TICKET_AI_JIRA_EMAIL>",
"TICKET_AI_JIRA_TOKEN": "<TICKET_AI_JIRA_TOKEN>",
"TICKET_AI_GITHUB_TOKEN": "<TICKET_AI_GITHUB_TOKEN>",
"TICKET_AI_REPO": "<TICKET_AI_REPO>",
"TICKET_AI_TICKET_LANGUAGE": "<TICKET_AI_TICKET_LANGUAGE>",
"TICKET_AI_CACHE_DIR": "<TICKET_AI_CACHE_DIR>"
}
}
}
}
启动命令uvx ticket-ai-mcp
需要配置的环境变量
TICKET_AI_TRACKERWhich tracker to read: gitlab, jira or github.
TICKET_AI_PROJECTThe project to measure. A GitLab path like acme/shop or its numeric id, a Jira project key like PROJ, or a GitHub owner/repo.
TICKET_AI_GITLAB_URLBase URL of the GitLab instance, for example https://gitlab.example.com. Required when the tracker is gitlab.
TICKET_AI_GITLAB_TOKEN密钥GitLab personal access token. The read_api scope is enough; nothing here writes.
TICKET_AI_JIRA_URLBase URL of the Jira instance. Cloud or self-hosted Server and Data Center; which one is detected from the instance.
TICKET_AI_JIRA_EMAILAccount email, for Jira Cloud's basic auth. Leave unset for a public board or for a Server personal access token.
TICKET_AI_JIRA_TOKEN密钥Jira API token from id.atlassian.com, or a Server personal access token on its own. Leave unset to read a public instance anonymously.
TICKET_AI_GITHUB_TOKEN密钥GitHub token with read access to the repository's issues.
TICKET_AI_REPOCheckout to search when gathering context for a subject. Defaults to the working directory, which on a wrong guess produces confident nonsense - the report names the directory it searched.
TICKET_AI_TICKET_LANGUAGEForce the language tickets should be written in, overriding what was measured in the corpus. Leave unset unless the board is mid-switch: a measurement beats a setting.
TICKET_AI_CACHE_DIRWhere the learned profile is kept between runs. Defaults to .ticket-ai/ under the working directory.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypiticket-ai-mcp0.1.1

工具

8

收录的工具列表 · 更新于 2 周前

参数类型必填说明
keepinteger否
sampleinteger否
projectany否
trackerany否
from_ticketsany否

详细介绍

ticket-ai

release MCP tools CLI commands trackers

tests coverage python license

一个 MCP 服务和 CLI,用于衡量团队实际如何编写工单,并据此要求新工单遵循规范。 不只是说这张工单写得不好,而是指出这里已完成的 40 张工单中,有 31 张包含验收标准部分,而这张没有。

没有“Akzeptanzkriterien”(验收标准)部分。 40 张示例工单中有 31 张(78%)包含该部分。

大多数工单建议都是免费的,因此也没人理会。“添加验收标准”“写上复现步骤”——人人都听过,却没有人因此改变。统计你们项目中实际发生过的情况,更难被一笔带过;而且这不是对工单的主观看法。

整个过程中都不会调用大模型。 学习团队风格、查找相关工单和衡量草稿都是计数工作,可以在不配置密钥、也不与工单跟踪系统以外的网络通信的情况下运行。编写工单需要大模型,因此这部分是可选的,使用哪个模型由你决定。 在真实看板上运行后的发现 →

安装

uv tool install ticket-ai-mcp

配置仅使用环境变量——通过 --token 传入的令牌会留在 shell 历史记录和进程列表中。

export TICKET_AI_TRACKER=gitlab
export TICKET_AI_PROJECT=acme/shop        # or the numeric id
export TICKET_AI_GITLAB_URL=https://gitlab.example.com
export TICKET_AI_GITLAB_TOKEN=...         # read_api scope is enough

公开项目可以不提供令牌。设置 TICKET_AI_GITLAB_URL=https://gitlab.com 且不提供令牌,即可读取任何公开看板;这是在连接自己的实例之前,最快了解它能做什么的方式。

Jira 和 GitHub
# Jira — Cloud or self-hosted Server / Data Center. Which one you are on is
# detected from the instance; you do not have to say.
export TICKET_AI_TRACKER=jira
export TICKET_AI_PROJECT=PROJ
export TICKET_AI_JIRA_URL=https://acme.atlassian.net

# Leave the credentials unset for a public board — plenty answer without any.
# Cloud: the token comes from id.atlassian.com, and is not the password.
export [email protected]
export TICKET_AI_JIRA_TOKEN=...
# Self-hosted: a personal access token on its own, sent as a Bearer.
export TICKET_AI_JIRA_TOKEN=...
# Only if detection gets it wrong: cloud | server | auto (the default)
export TICKET_AI_JIRA_API=server

# GitHub
export TICKET_AI_TRACKER=github
export TICKET_AI_PROJECT=acme/shop
export TICKET_AI_GITHUB_TOKEN=...

mcp-name: io.github.syrian963/ticket-ai-mcp

一条命令

ticket-ai learn      # mine the tracker, cache the profile
ticket-ai style      # what it learned

learn 需要一两分钟:排名需要获取每张工单的评论和关联的合并请求,因此每张工单都要额外发出一两次请求。如果设置了 TICKET_AI_CACHE_DIR,缓存会写入该目录;否则会写入 .ticket-ai/,因此只需运行一次,而不是每次评审都运行。

接下来:

ticket-ai learn --from '#412,#98'                  # or name the good ones yourself
ticket-ai context 'export is broken on mobile'     # what already exists
ticket-ai gaps                                     # declared template vs what arrives
ticket-ai draft --title '...' --file draft.md      # check one before creating it
ticket-ai review '#42'                             # measure one that exists
ticket-ai open                                     # every open ticket, worst first

draft 是最值得养成使用习惯的命令。创建工单后再检查,就已经错过了最佳时机:看板已经发出通知,此后的每次修正都只能作为编辑留下历史记录。

$ ticket-ai draft --title 'Filter kaputt' --file draft.md
Alignment with house style: 33% over 3 checks

### MEDIUM - The description is 116 characters.
The shortest quarter of tickets that shipped here start at 639; the median is 1079.

### MEDIUM - The ticket has no labels.
62% of the exemplars are labelled.

--from 会原样采用:不进行筛选,也不会根据你的选择评分。用三个词描述一张工单,就等于说明了团队如何编写工单,生成的风格档案也会如实反映这一点。

作为 MCP 服务

{
  "mcpServers": {
    "ticket-ai": {
      "command": "uvx",
      "args": ["ticket-ai-mcp"],
      "env": {
        "TICKET_AI_TRACKER": "gitlab",
        "TICKET_AI_PROJECT": "acme/shop",
        "TICKET_AI_GITLAB_URL": "https://gitlab.example.com",
        "TICKET_AI_GITLAB_TOKEN": "..."
      }
    }
  }
}

共 8 个工具,全部为只读:learn_conventions、house_style、ticket_template、ticket_context、template_gaps、review_draft、review_ticket、review_open_tickets。

如果要搜索的代码库不是助手的工作目录,请将 TICKET_AI_REPO 添加到 env 块中。

以这种方式撰写工单

如果你已经在使用 Claude Code,可以选择这条路径:无需密钥,无需 compose,也无需调用第二次模型。要求撰写工单后,助手会做五件事,其中三件在这里完成:

  1. ticket_template — 工单的框架:包含哪些章节、篇幅多长、使用什么语言、添加哪些标签。
  2. ticket_context — 现有信息:相关工单、其合并请求修改过的文件,以及检出目录中提到该主题的文件。
  3. 它会读取这些文件。 ticket_context 执行的是文本搜索,而非分析;它会指出该查看哪里,但不会代你查看。
  4. 它会撰写工单。
  5. review_draft — 衡量写出的内容,并在展示给你之前修正发现的问题。

“AI”在哪里

计数无法生成一段德语文字,因此撰写工单需要模型。其他所有事情都不需要。

撰写需要
MCP,在 Claude Code 中是无需任何东西 — 助手本身已经是模型
--writer ollama是你机器上的模型。无需密钥、无需账户,任何内容都不会离开笔记本电脑
--writer openai是基础 URL 和密钥。OpenRouter、Azure AI Foundry、vLLM,或任何服务提供商
不指定 writer(默认)否无需任何东西。负责衡量和收集;由你撰写
export TICKET_AI_WRITER=ollama       # or openai, with a base url and key
ticket-ai models                     # what that endpoint can reach
ticket-ai compose --title 'Etikettendruck bricht bei mehr als zehn Positionen ab'
ticket-ai models --workflow          # an Actions workflow that drafts new issues

compose 会撰写正文、衡量正文、将发现的问题交还给模型一次,并把审查结果输出到 stderr,以便单独重定向正文。--fail-under 会拒绝输出不符合团队风格的草稿。

无论使用哪个模型撰写,草稿都要经过与其他工单相同的衡量流程。这种循环让小型本地模型在这里也能胜任:它会按照通过计数得出的框架撰写,之后再依据你们团队自己的工单进行评估。

改用页面

ticket-ai ui --lang de     # or en

一个位于 127.0.0.1:8760 的本地页面,包含三个标签页:团队风格、用于粘贴草稿的文本框,以及按问题严重程度从高到低排列的未结积压工单。仅限环回访问,因为此进程持有跟踪系统令牌,而且没有可更改此设置的标志。

无需构建步骤,也无需 CDN — 所有 CSS 和 JavaScript 都内联在一个 HTML 文件中。

两种语言,彼此独立
export TICKET_AI_UI_LANGUAGE=de       # buttons and headings
export TICKET_AI_TICKET_LANGUAGE=de   # what it says to write tickets in

这两者很容易混为一谈,但值得区分。德语团队可能希望工具自身的按钮使用英语;加入德语看板的人仍然需要用德语撰写工单。

TICKET_AI_TICKET_LANGUAGE 会覆盖根据语料衡量出的语言,且无需重新学习即可生效。除非看板正在切换语言,否则不要设置它 — 衡量结果胜过手动设置,而强制使用看板并未采用的语言,会导致所有现有工单都无法通过语言检查。

发现的问题、其下方的注意事项,以及工单已经做对的事项列表,都会在展示时以阅读者的语言呈现。其中的章节名称不会翻译:它们是团队自己的标题,翻译后就会变成团队并不存在的章节。

在 CI 中
ticket-ai review "$CI_ISSUE" --fail-under 0.5

衡量内容

项目风格。 章节、篇幅、标签、标题标记、语言——基于你提供或工具自行找到的语料进行分析。每项发现都会标明语料中的数量。

失败的工单,而不只是成功的工单。 比例可以用来比较:不是“78% 的工单有验收标准”,而是 “已交付工单中有 78% 有验收标准,而停滞的工单中只有 30%”。后者是证据;前者只会让人耸耸肩。

划分只依据结果——变更已合并、工单重新打开,或经过一轮澄清提问——绝不依据工单内容,因为先按内容划分,再比较内容,会形成循环论证。因过期由机器人关闭的工单会从两组中都排除:它们反映的是关注度,而不是写作方式。如果某个看板上没有任何因素能区分两组,工具会如实说明;在要求任何人改变写法之前,了解这一点很有价值。docs/shipped-against-stalled.md 说明了限制条件,以及促使工具排除机器人关闭工单的真实看板。

只会一起出现的章节。 某个章节出现在 41% 的工单中,低于任何值得采用的阈值——但如果有 Ziel 章节的工单中有 76% 也包含该章节,而没有 Ziel 章节的工单中只有 20% 包含它,那团队就有一个模板,并将它应用于某一类工作。看板整体比例会完全掩盖这一点。

你声明的表单,对照你收到的工单。 如果检出内容中有 .github/ISSUE_TEMPLATE 或 .gitlab/issue_templates,gaps 会逐项对照表单字段与工单中实际包含这些字段的频率——差距可能出现在两个方向。某个必填字段只出现在 10% 的工单中,说明表单要求填写人们难以提供的内容。大多数工单都包含某个表单未提及的章节,说明项目逐渐形成了一种约定,却从未将它写下来。

编写工单时需要什么。 提供一个主题,它会返回相关的历史工单、这些工单对应的合并请求实际更改的文件,以及检出内容中提及该主题的文件。第二项信息只存在于跟踪器的历史记录中——无论读多少代码都无法找到,而且它通常是了解工作最终会落在哪些文件中的最快方式。

挖掘方式

只抽样已关闭的工单:一张未关闭的工单可能写得非常好,但目前还没有任何迹象表明有人能据此采取行动。每张工单的评分依据如下:

信号权重原因
关联了已合并的 MR0.30最有力的证据,表明有人能按工单所写的内容完成开发
描述内容充实0.20占位内容无法为模板提供参考
包含章节0.15我们要学习的正是模板
没有澄清问题0.15十一条“你是什么意思?”的评论意味着内容不清楚
从未重新打开0.10重新打开意味着工单尚未被理解就关闭了
带有标签0.10

在一小时内创建并关闭的工单,权重减半——它们通常是重复项或拼写修正,其形态并不能代表真实工作。机器人作者的工单会直接剔除;从 Renovate 学习项目风格是一个真实的失效模式。任何一位作者提供的语料不得超过 40%,否则分析得出的项目风格描述的就只是最高产的工单编写者,而非整个团队。

每个分数都会附上产生该分数的原因。无法据理质疑的语料,也不会让人信服。

它无法告诉你的事

你的工单写得好不好。 这里的所有操作都不看含义,只做计数。它无法判断你的验收标准是否合理,但可以告诉你,这个项目中已交付的 40 个工单都有验收标准,而这个工单没有。

一致不代表质量。 评分反映的是与此处历史上实际完成的工单之间的差距。一个了解自己意图的人写的一行工单,可能得分很低,但完全没有问题。工具也这样评价自己;当你向同事引用它时,也应该这么说。

不会更改跟踪工具中的任何内容。 所有操作都是读取。

排名信号也只是间接依据。一个写得很好的工单,因为超出范围而关闭且没有 MR,得分会很低;这是一种可以接受的误差:目标是找出 30 个有代表性的工单,而不是 30 个最好的工单。如果样本太少,报告会明确指出这一点,基于它进行的每次审核也都会如此。

Jira 没有用于获取关联分支和合并请求的公开 API,因此其中的“已交付”信号会退而使用远程链接;如果看板不发布这些链接,就完全无法将工单区分为已交付和停滞。此时工具会说明这一点,而不是把每个工单都报告为停滞。这是 API 的限制,不是语料库的限制。

衡量模型撰写的部分

此仓库中的每项测试都会模拟撰写者,因此没有一项能说明实际生成的文本质量。evals/ 正是为此而设:其中包含一组冻结的公开看板工单标题,每个标题都与收集该案例时的看板配置文件一同保存。

ticket-ai eval --baseline evals/baseline.json          # run it and mark the result
ticket-ai eval --runs evals/results/latest.jsonl       # re-score records, no model
ticket-ai eval --repeats 5 --markdown                  # spread, as a table for an MR

在构建配置文件之前,案例已从中留出。 如果配置文件拟合过某个案例,模型的表现就会因此显得更好,但这与模型本身毫无关系。

运行器会记录结果,但不会评分。 在本地模型上运行一个案例需要 30 到 70 秒,因此将评分单独进行,就能免费尝试更改指标。--runs 就是用于此路径的参数。

其中两个指标有意不纳入 review_draft。它会检查看板使用的章节是否齐全,但不会问草稿还编造了什么内容,因为人们很少添加看板从未用过的标题,而模型却经常这么做。另一个指标是离散度:对于非确定性撰写者,每个案例只给出一个数字会让结果看起来像精确值,因此提供了 --repeats,报告会在均值旁列出标准差。

评判器,以及为何它的分数从不单独出现

计数无法判断正文是否围绕标题展开,而这正是值得发现的问题:一份草稿可以包含所有章节、达到长度要求、使用正确的标签,却描述着完全不同的内容。review_draft 会给它满分。

--judge 只问模型这个问题,不问其他问题。不是“这是不是一张好的工单”——这个工具的存在,就是为了用计数取代这个问题。

ticket-ai eval --judge --labels evals/labels.jsonl
python tools/label_drafts.py evals/results/latest.jsonl   # collect the labels

单看原始一致率几乎毫无用处。 如果十份草稿中有九份切题,那么一个每次都回答“切题”的评判器就能达到百分之九十的一致率,却什么也没学到。Cohen 的 kappa 会扣除两个人按照这些比例猜测时也能达到的一致率,因此这个评判器的得分为零。

标签来自人工标注,这一点无法回避。如果校准时的人工部分是生成出来的,那么比较的就只是一个模型和另一个模型。没有 --labels 时,评判器一栏每次都会打印 uncalibrated,而不是只记录一次这条注意事项,然后让分数在脱离注意事项的情况下传播。

数据集包含来自十个公开看板的 134 个案例。其中 30 个是德语案例,来自 gitlab.opencode.de 上的 kern-ux/pattern-library 和 fitko/fim/portal;这是一个无需令牌即可读取的公开德语 GitLab。三个看板使用 Jira(issues.apache.org 上的 KAFKA 和 CASSANDRA,以及 hibernate.atlassian.net 上的 HHH),另有四个看板只写纯文本,完全没有重复出现的章节。

这并不是为了多样而多样。compose 会针对每一种情况走不同分支:德语提示词、跟踪系统抽象,以及一个骨架区块——它要么列出章节,要么告诉模型完全不要写标题。CI 会断言这三种情况都保留下来,因为如果数据集丢掉其中一种,它就会在平均值不变的情况下不再衡量该情况,而人们关注的那个数字却看不出任何异常。

kern-ux 正是 jsonl_lines 存在的原因:其中一张工单包含字面上的 U+2028,str.splitlines 会将其视为换行符,而 json.dumps 不会对它进行转义,因此有效文件会被读取为截断记录,错误却被归咎于文件本身。

evals/README.md 说明了哪些内容可以成为案例。仅限公开看板。

运行位置

轻量部分会在每个涉及测试框架的拉取请求上运行:加载每个看板,确保没有案例同时充当示例,并重新评分已提交的结果文件。不调用模型,因此只需几秒钟。

测试套件通过 workflow_dispatch 触发。组合 134 个案例需要几分钟,也要花钱;在一个只改 README 错别字的分支上为此付费毫无意义。

terraform/ 配置了报告的托管位置:一个位于 CloudFront 之后的 S3 存储桶,以及一个 GitHub Actions 使用短期 OIDC 令牌承担的 IAM 角色。仓库密钥中没有可泄露或需要轮换的 AWS 密钥。信任策略仅限于一个仓库;repo:owner/* 则会把角色交给该所有者今后创建的每一个仓库。即使没有这些配置,所有功能仍可正常工作;未设置角色变量时,发布步骤会跳过自身。

文档

thirty-boards.md在 43 个真实看板、一个 MCP 客户端、一个浏览器和全新安装环境中运行后发现了什么。当时几乎所有内容都具备完整的行覆盖率
what-it-produces.md它编写的五张工单,未经编辑,并附有每张工单获得的分数
shipped-against-stalled.md两组如何划分,以及哪些防护措施能确保比较公平
evaluating-compose.md在冻结数据集上运行的测试框架:哪些数据被留出以及原因、review_draft 缺少的两项指标、为什么门槛容许噪声,以及仍有哪些不足
local-models.md在 2 GB 本地模型上运行写作器:实测耗时,以及它需要牺牲什么
验证对象

已对 home-assistant/core、pydantic/pydantic、astral-sh/uv、fastapi/fastapi(GitHub)、inkscape/inkscape 和 gitlab-org/gitlab-runner(GitLab)、hibernate.atlassian.net HHH(Jira Cloud)、issues.apache.org/jira KAFKA(Jira Data Center),以及一个私有 GitLab 看板进行端到端只读验证。

Home Assistant 没有生成任何条件规则,这正是正确结果:他们的议题表单是必填的,因此每个部分都已达到看板级别的阈值,条件规则无从补充。

开发

uv sync
uv run pytest
uv run ruff check .
bash install_check.sh          # build the wheel and drive it from a clean venv
uv run python tests/fleet.py   # the whole tool across 43 public boards

许可证

MIT。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。