MCP-сервер для AI-агентов
mcp-server/ — это Model Context Protocol сервер, оборачивающий CLI gitflic в типизированные тулзы для AI-агентов (Claude Code, Cursor и т.п.).
Что внутри
mcp-server/
├── server.mjs # MCP stdio сервер
├── tools.mjs # описания тулзов (имя, описание, JSON-схема)
├── runner.mjs # child_process-обёртка: node <gitflic CLI> <args>
├── project-resolver.mjs # авто-резолв project из git remote
└── __tests__/ # vitest77 инструментов — каждый маппится 1-в-1 на подкоманду CLI.
Установка и запуск
Сервер опубликован в npm как gitflic-cli-mcp и запускается через npx — ставить ничего не нужно:
GITFLIC_TOKEN="xxxx" npx -y gitflic-cli-mcpnpx сам подтянет gitflic-cli-mcp и его зависимости (@modelcontextprotocol/sdk, zod) и сам CLI gitflic-cli. Из исходников — то же самое вручную:
cd mcp-server && npm install
GITFLIC_TOKEN="xxxx" node server.mjsНастройка токена
MCP-сервер наследует GITFLIC_TOKEN от родительского процесса — так же, как CLI:
export GITFLIC_TOKEN="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"Если токен лежит в OS keychain (через gitflic auth login), MCP-сервер тоже его увидит — он запускает gitflic CLI как subprocess, а тот умеет резолвить из keychain.
Конфигурация в Claude Code / Cursor
Claude Code
Одной командой:
claude mcp add gitflic --env GITFLIC_TOKEN=xxxxxxxx -- npx -y gitflic-cli-mcpИли вручную в ~/.claude.json:
{
"mcpServers": {
"gitflic": {
"command": "npx",
"args": ["-y", "gitflic-cli-mcp"],
"env": {
"GITFLIC_TOKEN": "xxxxxxxx"
}
}
}
}Cursor
В ~/.cursor/mcp.json:
{
"mcpServers": {
"gitflic": {
"command": "npx",
"args": ["-y", "gitflic-cli-mcp"],
"env": {
"GITFLIC_TOKEN": "xxxxxxxx"
}
}
}
}Из исходников вместо
npx -y gitflic-cli-mcpукажи"command": "node", "args": ["/path/to/gitflic-cli/mcp-server/server.mjs"].
Примеры использования (в Claude Code)
После подключения агент получает доступ к 77 тулам. Несколько типичных сценариев:
> Создай MR из текущей ветки в main с описанием из последнего коммита
> Найди все открытые MR в проекте FerdBur/wave, которые ждут моего approve
> Закрой все issue старше 60 дней, которые в статусе OPEN и без assigneeАгент сам составляет правильные аргументы, валидирует результат, итерирует при ошибках.
Авто-резолв проекта
mcp-server/project-resolver.mjs автоматически определяет owner/alias из git remote get-url origin — агенту не нужно каждый раз передавать --project:
cd ~/code/FerdBur/wave
# Агент теперь работает с FerdBur/wave по умолчанию для всех команд.Безопасность
MCP-сервер — тонкая прослойка: каждая тулза маппится 1-в-1 на CLI. Всё, что CLI может сделать, может и MCP-хост. Это и фича, и риск — следите за:
- Кто имеет доступ к MCP-хосту (любой промпт там может вызвать тулзы).
- Scope токена (минимизируйте:
PROJECT_READдля read-only агентов). - Не давайте токен с
ADMIN_*scope без крайней нужды.
Что в планах
- ❌ OAuth browser flow для токена (вместо env-var).
- ❌ Read-only mode (отдельный MCP-сервер с фильтром tools = только read-*).
- ❌ Audit log (кто какую тулзу вызвал).
Если нужно — issue/PR.
Annotations: деструктивные операции требуют approval
Все 77 тулов помечены MCP-аннотациями (MCP 2025-06-18 spec):
{
"name": "gitflic_mr_close",
"annotations": {
"title": "MR Close",
"readOnlyHint": false,
"destructiveHint": true, // ← клиент запросит подтверждение
"idempotentHint": true, // повторный вызов = то же состояние
"openWorldHint": true // ходит в GitFlic API
}
}| Hint | Когда true | Когда false |
|---|---|---|
readOnlyHint | list / get / view / search / me / status | create / edit / delete / merge |
destructiveHint | (почти всегда — дефолт) | только чисто read-only тулзы |
idempotentHint | close / cancel / delete / bind / set (повтор → то же состояние) | approve (toggle), pipeline start (новый run) |
openWorldHint | любой тул, ходящий в GitFlic API | только локальные (auth login/logout, alias set, cache clear) |
Что это даёт
Хосты вроде Claude Code и Cursor при получении тулза с destructiveHint: true показывают пользователю prompt:
gitflic_mr_close wants to close merge request #42 on project FerdBur/wave.
Approve? [y/N]Это защита от runaway-агентов, которые могут снести прод случайным вызовом mr_close или issue_delete.
Как это работает внутри
Аннотации живут в mcp-server/tools.mjs рядом с каждым тулзом:
{
name: "gitflic_mr_close",
description: "...",
inputSchema: { ... },
annotations: {
title: "MR Close",
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: true,
},
handler: (a) => ["mr", "close", a.localId, "--project", a.project],
}server.mjs пробрасывает annotations в server.tool(name, desc, schema, annotations, handler) — MCP SDK v1.x их нативно поддерживает и шлёт хосту в tools/list ответе.
Re-classification after adding new tools
Если добавляешь новый тул в tools.mjs, прогони классификатор:
node scripts/annotate-mcp-tools.mjsСкрипт идёт по TOOLS-массиву, классифицирует каждый по суффиксу/префиксу имени, и вставляет annotations: {...} если его ещё нет. Полностью идемпотентен — повторный запуск ничего не дублирует.
Логика классификации — в scripts/annotate-mcp-tools.mjs (~150 LOC, без зависимостей).
Caveats
destructiveHintне означает "обязательно удалит" — это hint, что может. Хосты используют его для UI prompt, но не для блокировки.- Если хост не поддерживает annotations (старые версии), всё работает как раньше — annotations просто игнорируются.
idempotentHintиспользуется MCP-хостами для авто-retry, но в нашей реализации retry сам делает CLI (черезGITFLIC_HTTP_RETRIES). Так что это скорее hint для пользователя.