Skip to main content
Glama

ticket-writer MCP

MCP-сервер для MagOneAI. Репортёр оставляет запрос на фичу в свободной форме; рабочий процесс задаёт несколько вопросов, необходимых, чтобы перевести запрос в исполнимый вид, а затем создаёт тикет, в котором указаны проблема, что нужно сделать, критерии приёмки и все архитектурные решения.

Jira, GitHub, GitLab и Linear обслуживаются одним и тем же кодом.

Сервер никогда не обращается к трекеру. Он формирует запрос на создание тикета и возвращает его; отправку выполняет собственный HTTP-узел рабочего процесса.


Как работает рабочий процесс

free text  ──▶  check_request  ──"needs_clarification"──▶  ask the reporter ──┐
                     │                                                        │
                     │◀──────────────────── answers ─────────────────────────┘
                  "ready"
                     │
                     ▼
                render_ticket  ──"possible_duplicate"──▶  human confirms ──┐
                     │                                                     │
                     │◀────────── confirm_not_duplicate ───────────────────┘
              "ready_to_send"
                     │
                     ▼
          HTTP node: POST request.url  ◀── the only write in the workflow
                     │
                     ▼
              issue key + url back to the reporter

В WORKFLOW.md описан контракт по каждому узлу: точный входной и выходной JSON, а также все поля, которые попадают в тикет.


Related MCP server: refine-backlog-mcp

Почему он не подключается к Jira напрямую

В первом черновике был REST-клиент Jira. Эта версия требовала наличия учётных данных на этой машине, обзавелась сценарием отказа, когда тикет формировался нормально, а POST не проходил, и привязывала рабочий процесс к одному трекеру.

Формирование запроса вместо этого означает:

  • здесь нет учётных данных — заголовки содержат имена {{PLACEHOLDER}}, которые API-узел подставляет из хранилища секретов MagOne

  • любой трекер — новый трекер добавляется одной записью словаря в src/targets.py

  • переиспользование того, что у MagOne уже есть — если подключён трекер через MCP, используйте target="generic" и передайте поля в его инструмент создания

  • легко встраивается шаг согласования — ничего не записывается до вызова формирования

  • тестируется только pytest, и ничем больше — во всём репозитории нет сети

Он также не пишет текст тикета. Агент рабочего процесса — языковая модель, и он лучше, чем любое правило из этого репозитория, умеет превращать сбивчивое сообщение из Slack в формулировку проблемы. Этому серверу принадлежит та часть, которая должна вести себя одинаково при каждом запуске: чек-лист, формулировки вопросов, формат тела и защита от дубликатов.


Инструменты

Инструмент

Назначение

check_request

Достаточно ли этого для создания тикета? Если нет — что спросить.

render_ticket

Запрос на создание тикета, отформатированный для конкретного трекера.

list_targets

Трекеры, ключи конфигурации, какие учётные данные нужны API-узлу.

duplicate_search_query

Необязательный. Строит поисковый запрос для проверки дубликатов.

Все четыре инструмента доступны только для чтения. Каждый ответ содержит status, по которому переключается канвас:

status

Действие рабочего процесса

ready

перейти к render_ticket

needs_clarification

задать questions, повторить цикл

possible_duplicate

показать candidates, получить ответ человека

ready_to_send

передать request в API-узел

error

прочитать hint — обычно отсутствует ключ config


Что делает тикет полным

check_request требует четыре поля, запрашиваемые в таком порядке, не более трёх за один раунд:

  1. problem — что болит сегодня, кого это касается

  2. goal — что должно существовать после завершения, в виде поведения

  3. acceptance_criteria — как ревьюер принимает или отклоняет задачу

  4. architecture_notes — принятые решения, ограничения, которые нужно соблюдать ("none known" допустимо, если дизайн ещё не определён)

affected_users и out_of_scope собираются, когда пользователь их указывает, но никогда не блокируют. Ответ короче 25 символов или повторяющий формулировку вопроса не считается ответом.

Чтобы изменить чек-лист, отредактируйте SLOTS в src/ticket.py — вопросы, порядок и ограничение по количеству определяются этим списком.


Целевые системы

target

config

Учётные данные, которые предоставляет API-узел

jira

base_url, project_key

{{JIRA_BASIC_AUTH}} — base64 email:api_token

github

owner, repo

{{GITHUB_TOKEN}} — PAT с правами чтения/записи Issues

gitlab

project_id, host?

{{GITLAB_TOKEN}} — токен с областью api

linear

team_id, project_id?

{{LINEAR_API_KEY}}

generic

—

нет — передайте fields в собственный MCP этого трекера

Различия, которые рендерер берёт на себя, чтобы агенту не приходилось:

  • Jira в description ожидает ADF, а не markdown. Передача markdown-строки — самый частый способ, которым падает самодельный узел Jira.

  • GitLab ожидает метки в виде строки через запятую; GitHub и Jira — в виде списка.

  • В GitHub нет поля приоритета, поэтому приоритет становится меткой priority-*.

  • Linear — это GraphQL: одна конечная точка, мутация в теле запроса.

Добавление трекера: одна запись в TARGETS с build(), возвращающим (url, headers, body). Вот и всё изменение.


Защита от дубликатов

Этот сервер не умеет искать, поэтому рабочий процесс передаёт ему данные: duplicate_search_query строит запрос, поисковый узел выполняет его, результаты возвращаются в render_ticket как existing_issues=[{key, summary, url}]. Краткие описания, у которых пересечение токенов ≥ 0.6, возвращаются как possible_duplicate.

Нет поискового узла — нет проверки — тикет всё равно формируется. Такова цена за то, что у нас нет собственного поискового клиента для каждого трекера.


Запуск

python -m venv .venv && .venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest -q          # 47 tests, no network, no account

Локальный MCP через stdio для Claude Desktop:

MCP_TRANSPORT=stdio .venv/bin/python -m src.server

Деплой:

docker build -t ticket-writer .
docker run -p 8000:8000 -e TICKET_WRITER_TOKEN=$(openssl rand -hex 32) ticket-writer
curl localhost:8000/health

Зарегистрируйте https://your-host/mcp в MagOneAI с заголовком Authorization: Bearer $TICKET_WRITER_TOKEN, точно так же, как настроен Outlook MCP. TICKET_WRITER_TOKEN — единственная переменная окружения, которая требуется серверу; у MCP_TRANSPORT, HOST, PORT и LOG_LEVEL есть рабочие значения по умолчанию. Установите max_iterations примерно в 12.


Создайте настоящий тикет для проверки

scripts/send.py делает ровно то, что делает API-узел: формирует запрос, подставляет плейсхолдер из одноимённой переменной окружения и отправляет POST:

.venv/bin/python scripts/send.py --target jira \
  --config base_url=https://you.atlassian.net project_key=KAN --dry   # payload only

export JIRA_BASIC_AUTH=$(printf '%s' 'you@mail.com:API_TOKEN' | base64)
.venv/bin/python scripts/send.py --target jira \
  --config base_url=https://you.atlassian.net project_key=KAN         # 201 + issue key

TESTING.md описывает, что доказывает каждый набор тестов, живой прогон против Jira Cloud и таблицу ошибок.


Безопасность

  • Bearer-токен берётся из переменной окружения и никогда не передаётся через узел рабочего процесса — токен, проходящий через канвас, попадает в логи запуска.

  • Никакие учётные данные трекера никогда не попадают на этот сервер. Заголовки — это плейсхолдеры; config принимает расположения, а не секреты. Это проверяется тестом.

  • /health не требует аутентификации для проверок платформы; всё остальное — требует. Сервер отказывается запускаться по HTTP без заданного токена.

  • Пользователь контейнера — не root.

  • Текст репортёра — это данные, а не инструкции. Он сохраняется дословно в блоке цитаты и никогда не интерпретируется. Об этом говорят докстринги инструментов, потому что именно их читает агент.

  • Защита от дубликатов и проверка полноты — это то, что стоит между болтливым Slack-каналом и сотней мусорных тикетов. Не добавляйте флаг, который пропускает обе проверки.


Структура

src/server.py    MCP surface: tool defs, transport, auth
src/ticket.py    pure: rubric, questions, body in markdown + ADF, dup scoring
src/targets.py   pure: what each tracker's API wants — one entry per tracker
tests/           47 tests: the rules, the tool surface, HTTP and auth
scripts/send.py  stands in for the API node, for end-to-end checks
WORKFLOW.md      node-by-node input/output contract
TESTING.md       what is covered, what is not

ticket.py ничего не знает ни об одном трекере; targets.py ничего не знает о том, что делает тикет хорошим. Если добавление обязательного поля означает правку targets.py, разделение нарушено.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers