incident-mcp

incident-mcp

An incident management MCP server for on-call engineers, providing tools to search incidents, review deploys, query logs, analyze latency metrics, and acknowledge or summarize incidents.

Category
访问服务器

README

incident-mcp

Инцидентный MCP-сервер для агента дежурного инженера (on-call). FastMCP (Python 3.12, uv), транспорт stdio, единый asyncpg-пул на процесс.

Сервер читает локальный инцидентный стенд из homework-stand/: Postgres с логами, деплоями и реестром инцидентов плюс payments-api под нагрузкой. В стенде есть намеренный дефект — деградация latency учебного эндпоинта; ищется он по данным через tools сервера, а не чтением исходников (см. «Разбор инцидента» ниже).

Состав

├── src/incident_mcp/      MCP-сервер
│   ├── app.py             инстанс FastMCP, lifespan (пул), инструкции агента
│   ├── db.py              доступ к БД: пул, fetch/execute, ToolError-обёртки
│   ├── schemas.py         схемы аргументов: enum-типы, парсер длительностей
│   ├── tools_read.py      read-tools (без побочных эффектов)
│   ├── tools_write.py     write-tools (меняют состояние в Postgres)
│   └── server.py          stdio entry point (в stdout только MCP-сообщения)
├── tests/                 unit + интеграционные (на живом стенде)
├── homework-stand/        стенд: docker compose (Postgres, payments-api, simulator)
└── memory/                проектная память агентов (brief, decisions, progress)

Обоснование состава tools

Каждый tool — один шаг разбора инцидента; универсального query(sql) нет намеренно, агент не видит SQL и не может обойти доменные ограничения.

tool шаг разбора почему отдельным tool'ом
incidents_search найти открытый инцидент входная точка сценария; фильтры severity/status/time_range
incident_get карточка одного инцидента сводка разбора по id; неизвестный id — ошибка со списком доступных
deploys_recent сопоставить деградацию с релизом рост latency сразу после деплоя — главный подозреваемый
logs_query WARN/ERROR вокруг начала деградации сообщения — пользовательские данные (в стенде есть prompt-injection строка; tool отдаёт её как данные)
metrics_latency форма деградации latency без него агент увидит только точку, а не кривую; возвращает готовый агрегат (корзины, avg, p95, hit-rate), а не сырые строки
runbook_get типовые симптомы и шаги диагностики читается перед началом разбора
service_catalog_get кому эскалировать команда, on-call, зависимости
incident_acknowledge взять инцидент в работу write-операция, отдельный tool, в description явно написано «WRITE-ОПЕРАЦИЯ»
incident_create_summary зафиксировать выводы разбора write-операция, отдельный tool

Описания tools важнее обычного: в OpenCode они попадают в один список со встроенными (bash, чтение файлов). Если из description не понятно, когда брать metrics_latency вместо bash/psql, агент возьмёт bash. Поэтому у каждого tool есть title и description, отвечающие на три вопроса: что делает, когда применять, какие ограничения и side effects. Аргументы валидируются inputSchema (enum-типы Literal, Field(ge=..., le=...), парсер длительностей 1m..7d); невалидный ввод возвращает структурированную ошибку isError с поправимым текстом, сервер не падает.

Быстрый старт

# 1. Стенд
cd homework-stand
cp .env.example .env
docker compose up -d --build
docker compose run --rm simulator   # ~5 минут: засев истории + живой трафик

# 2. MCP-сервер (из корня)
cp .env.example .env
uv sync
uv run incident-mcp                 # stdio-сервер

Проверка через MCP Inspector

Inspector (v2) работает в скриптовом CLI-режиме: цель (команда сервера) до --, опции после.

# lifecycle: initialize (DSN сервер возьмёт из .env сам)
npx @modelcontextprotocol/inspector --cli uv run incident-mcp -- \
  --method initialize --format json \
  --cwd "$(pwd)" -e FASTMCP_CHECK_FOR_UPDATES=off

# tools/list — 9 tools с title/description/inputSchema
npx @modelcontextprotocol/inspector --cli uv run incident-mcp -- \
  --method tools/list --format json \
  --cwd "$(pwd)" -e FASTMCP_CHECK_FOR_UPDATES=off

# вызов tool
npx @modelcontextprotocol/inspector --cli uv run incident-mcp -- \
  --method tools/call --tool-name metrics_latency --format json \
  --tool-args-json '{"endpoint":"/api/v1/orders/{order_id}/price","time_range":"1h","bucket":"1m"}' \
  --cwd "$(pwd)" -e FASTMCP_CHECK_FOR_UPDATES=off

# невалидные аргументы -> isError:true, сервер жив
npx @modelcontextprotocol/inspector --cli uv run incident-mcp -- \
  --method tools/call --tool-name metrics_latency --format json \
  --tool-args-json '{"endpoint":"/api/v1/orders/{order_id}/price","bucket":"xyz"}' \
  --cwd "$(pwd)" -e FASTMCP_CHECK_FOR_UPDATES=off

Прогон всех 9 tools и четырёх невалидных вызовов (полный лог — docs/inspector-checks.txt) дал: initialize → serverInfo incident-mcp 3.4.7, protocolVersion 2025-11-25; tools/list → 9 tools; каждый tool вернул данные; невалидные аргументы → {"isError":true} с текстом («Invalid time range 'xyz'...», «Incident 'INC-999' not found. Known incident ids: ...», pydantic-ошибка limit: Input should be greater than or equal to 1).

Подключение к OpenCode

opencode.json (ключевая часть):

{
  "mcp": {
    "incident-mcp": {
      "type": "local",
      "command": ["uv", "run", "incident-mcp"],
      "cwd": "/абсолютный/путь/к/incident-mcp",
      "enabled": true,
      "timeout": 30000,
      "environment": {
        "FASTMCP_CHECK_FOR_UPDATES": "off"
      }
    }
  }
}

Нюансы, проверенные на практике:

  • DSN в конфиге не дублируется: STAND_DATABASE_URL берётся только из .env. Сервер ищет .env по абсолютному пути от файла модуля (src/incident_mcp/server.py → корень проекта), поэтому загрузка не зависит от cwd, с которого opencode запускает процесс. Переменная из окружения имеет приоритет (load_dotenv не перезаписывает существующие).
  • cwd — абсолютный: относительный opencode резолвит от корня workspace и процесс не находит проект.
  • FASTMCP_CHECK_FOR_UPDATES — строка "off": в FastMCP 3.x поле check_for_updates это Literal["stable","prerelease","off"], значение false роняет процесс pydantic-ошибкой при импорте, до MCP-handshake.
  • После подключения tools сервера видны агенту с префиксом incident-mcp_* (incidents_search, metrics_latency, ...).

Проверка кода

uv run pytest                     # 31 тест; интеграционные скипаются без стенда (порт 5433)
uv run ruff format src tests      # 9 files left unchanged
uv run ruff check src tests       # All checks passed!
uv run mypy src                   # Success: no issues found in 7 source files

Лог реального диалога (ReAct-сценарий)

Сценарий пройден агентом (OpenCode + этот MCP-сервер) на живом стенде, прогон симулятора 2026-08-12 18:32–18:37. Цепочка вызовов:

  1. incidents_search(service="payments", status="open", time_range="1h") → INC-001, severity high, открыт 18:31:41, «Рост времени ответа /api/v1/orders/{order_id}/price».
  2. runbook_get(service="payments") → план: кривая latency, контрастный эндпоинт, деплои, логи кэша.
  3. metrics_latency(endpoint="/api/v1/orders/{order_id}/price", time_range="1h", bucket="1m") → форма деградации: до 18:32 фоновый трафик avg 3–8 мс при hit-rate ~100%; с 18:32 монотонный рост — avg 39.2 → 80.0 → 139.2 → 198.5 → 265.1 → 336.9 мс, p95 51.5 → 457.5 мс, cache_hit_pct = 0.0 во всех корзинах.
  4. metrics_latency(endpoint="/api/v1/catalog/items", ...) → здоровый эндпоинт ровен: avg 0.4–0.7 мс. Проблема локальна для расчёта цены.
  5. deploys_recent(service="payments") → v1.5.0 «refactor: unified response cache» (i.petrov), задеплоен 18:29:41 — за 2 минуты до открытия инцидента и за 3 минуты до начала деградации.
  6. logs_query(service="payments-api", time_range="1h", level="WARN") → «response cache grew to 5000...9495 entries, hit_rate=0.0»: кэш наполняется, но попаданий ноль. level="ERROR" → пусто.

Гипотеза (со ссылками на данные): деплой v1.5.0 сломал кэш ответов — записи создаются, но поиск никогда не находит их (hit_rate=0.0 при растущем числе записей); значит, ключ записи не совпадает с ключом поиска, скорее всего в ключ попадает поле, уникальное для каждого запроса. Каждый запрос идёт по медленному пути расчёта цены, и под нагрузкой latency монотонно растёт.

Далее write-tools: incident_acknowledge("INC-001") → {"status":"acknowledged","changed":true}; incident_create_summary("INC-001", ...) → сводка сохранена в incidents.summary.

Подтверждение по исходникам (после гипотезы): api/cache.py, build_key включал request_id — уникальный для каждого запроса, поэтому каждый lookup был промахом. Фикс: request_id из ключа убран (ключ = эндпоинт + параметры).

Разбор инцидента INC-001

  • Симптом: монотонный рост latency /api/v1/orders/{order_id}/price под нагрузкой 20 rps при ровном /api/v1/catalog/items.
  • Триггер: деплой payments v1.5.0 «refactor: unified response cache» (18:29:41), деградация с 18:32.
  • Корень: в ключе кэша ответов был request_id — кэш наполнялся, но не отдавал ни одного ответа (hit_rate 0.0), каждый запрос шёл по медленному пути (агрегация по журналу price_events).
  • Фикс: request_id исключён из build_key (homework-stand/api/cache.py, main.py), образ api пересобран (docker compose up -d --build api — заодно сбрасывает in-memory кэш для честного сравнения).

До/после: два реальных прогона

Оба прогона — симулятор стенда: 20 rps × 300 c = 6000 запросов, замер — metrics_latency (1m-корзины) и /internal/cache-stats.

До фикса (сломанный кэш, прогон 18:32–18:37):

минута req avg_ms p95_ms hit%
18:32 280 39.2 51.5 0.0
18:33 959 80.0 113.8 0.0
18:34 959 139.2 178.6 0.0
18:35 951 198.5 243.7 0.0
18:36 966 265.1 323.7 0.0
18:37 685 336.9 457.5 0.0

После фикса (прогон 18:41–18:46):

минута req avg_ms p95_ms hit%
18:41 342 20.8 54.4 39.2
18:42 960 13.4 54.8 71.0
18:43 960 10.6 56.8 81.7
18:44 959 20.0 73.5 71.0
18:45 962 25.8 88.9 69.1
18:46 617 39.3 105.4 59.6

Итог:

показатель до после изменение
cache hit-rate 0.0% все минуты 39–82% (69% за прогон; 3314 hits / 1486 misses) поднялся с 0%
avg latency, пик 336.9 мс 39.3 мс (минимум 10.6) в 8.6 раза ниже
p95 latency, пик 457.5 мс 105.4 мс в 4.3 раза ниже

Небольшой подъём avg в конце прогона «после» — рост медленного пути вместе с журналом price_events (357 тыс. строк к концу прогона) на фоне TTL-промахов; кэш при этом продолжает работать (hit-rate > 0).

Подробности про стенд — в homework-stand/README.md.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选