vetmanager-mcp

vetmanager-mcp

Enables managing veterinary clinic operations via natural language by integrating Vetmanager REST API with MCP clients.

Category
访问服务器

README

vetmanager-mcp

Tests License: MIT

English speakers: This project is documented in Russian. The API and MCP protocol are language-agnostic and work with any MCP-compatible client. Google Translate handles technical documentation well. See Quick Start to get running in 3 commands.

MCP-сервер для интеграции Vetmanager REST API с любым клиентом, поддерживающим Model Context Protocol. Позволяет управлять операциями ветеринарной клиники через диалоговых AI-агентов на естественном языке.

Сервер берёт на себя bearer-аутентификацию сервиса, хранение Vetmanager credentials на уровне service account, динамическое определение URL API (через billing-api.vetmanager.cloud) и форматирование данных. Runtime-контур теперь bearer-only: MCP-клиент передаёт только Authorization: Bearer <service_token>, а активное Vetmanager-подключение определяется через account context.

Privacy / auth boundary:

  • сервис не сохраняет бизнес-данные Vetmanager для постоянного хранения;
  • сервис хранит только технические данные integration и metadata сервисных bearer-токенов;
  • для режима login/password -> user token логин и пароль используются только для token exchange и не сохраняются в storage;
  • при смене пароля в Vetmanager сохранённый user token может стать невалидным, и тогда в кабинете потребуется повторная авторизация.

Требования

  • Docker (с плагином Compose)
  • ssh-copy-id настроен для деплоя на удалённый сервер
  • Python на хосте не требуется — всё работает через Docker

Быстрый старт (локально)

cp .env.example .env          # задайте UID/GID и LOG_LEVEL
docker compose build
docker compose up -d          # запустить MCP-сервер

После запуска:

  • публичный лендинг доступен на http://localhost:8000/
  • MCP endpoint остаётся на http://localhost:8000/mcp

Полезные runtime-переменные:

  • DATABASE_URL — строка подключения к БД. По умолчанию используется локальный sqlite+aiosqlite:///./data/vetmanager.db.
  • STORAGE_ENCRYPTION_KEY — ключ шифрования для сохранённых Vetmanager secrets. В production должен быть задан явно.
  • WEB_SESSION_SECRET — секрет подписи web session cookie для /register, /login, /account. В production должен быть задан явно.
  • WEB_SESSION_MAX_AGE_SECONDS — срок жизни web session cookie. По умолчанию 86 400 (24 часа).
  • WEB_TRUSTED_PROXY_IPS — список доверенных reverse proxy IP/host через запятую; только для них сервис учитывает X-Forwarded-For.
  • SITE_BASE_URL — базовый URL self-hosted инсталляции (используется в canonical/og:url лендинга и в mcp.json snippet на странице аккаунта). По умолчанию https://vetmanager-mcp.vromanichev.ru (prod). Для self-hosted — задайте свой домен (без trailing slash).
  • ERROR_TRACKING_DSN / SENTRY_DSN — opt-in DSN для отправки unhandled errors в Sentry.
  • ERROR_TRACKING_ENVIRONMENT, ERROR_TRACKING_RELEASE, ERROR_TRACKING_TRACES_SAMPLE_RATE — knobs для error tracking bootstrap.
  • PORT, MCP_PATH, LOG_LEVEL — стандартные настройки MCP HTTP runtime.

Operational helpers:

  • scripts/post_deploy_smoke_checks.sh — post-deploy checks для /healthz, /readyz, /metrics и /mcp. Поддерживает retry/grace knobs через env: SMOKE_MAX_ATTEMPTS, SMOKE_SLEEP_SECONDS, SMOKE_CONNECT_TIMEOUT_SECONDS, SMOKE_CURL_MAX_TIME_SECONDS. При падении deploy script дополнительно печатает docker compose ps и tail container logs для fast triage. Deploy path также принудительно выравнивает UID/GID для docker compose и заранее создаёт локальный data/, чтобы SQLite storage мог стартовать на bind-mounted репозитории без permission drift.

Запуск тестов:

# default contour: unit + mock + live browser happy-path tests
# Chromium уже предустановлен в test image, доп. setup не нужен
docker compose --profile test run --rm test

# fast contour: без browser и real contour tests
docker compose --profile test run --rm test sh -c "python scripts/run_fast_test_suite.py"

# security contour: только security regressions этапа 44
docker compose --profile test run --rm test sh -c "python -m pytest -m security -q"

# opt-in real contour: real API и real browser tests
docker compose run --rm \
  -e TEST_DOMAIN=<домен> \
  -e TEST_API_KEY=<ключ> \
  test sh -c "python scripts/run_opt_in_real_test_suite.py"

# opt-in real contour с direct real smoke для уже выданного user-token
docker compose run --rm \
  -e TEST_DOMAIN=<домен> \
  -e TEST_USER_TOKEN=<user_token> \
  test sh -c "python scripts/run_opt_in_real_test_suite.py"

# opt-in real contour с login/password -> user token exchange
docker compose run --rm \
  -e TEST_DOMAIN=<домен> \
  -e TEST_USER_TOKEN_BASE_URL=<https://clinic.vetmanager2.ru> \
  -e TEST_USER_LOGIN=<login> \
  -e TEST_USER_PASSWORD=<password> \
  test sh -c "python scripts/run_opt_in_real_test_suite.py"

Тестовые контуры:

  • fast: быстрый inner-loop без Playwright/browser и без real API/browser tests.
  • default: unit + mock/e2e + live localhost browser tests, без реального Vetmanager API.
  • opt_in_real: real API e2e + real browser tests; real browser flow дополнительно требует RUN_REAL_BROWSER_TESTS=1.

Safe workflow для real/browser verification:

  • использовать только env-driven TEST_* и RUN_REAL_BROWSER_TESTS=1, не записывать clinic credentials в репозиторий;
  • для login/password -> user token real flow сервис повторяет production контракт Vetmanager: POST /token_auth.php c app_name=vetmanager-mcp, затем X-USER-TOKEN + X-APP-NAME на последующих API-запросах;
  • для production browser checks использовать временный account и удалять его после ручной верификации, чтобы не копить тестовые сущности в production.

Что входит в default docker compose --profile test run --rm test:

  • unit tests;
  • mock/e2e tests;
  • live localhost browser tests через Playwright;
  • browser happy-path tests для обоих web auth flows;
  • cleanup regression для browser-created account data;
  • zero-warning quality gate через warning-as-error launcher.

Observability

HTTP probes и scrape endpoints:

  • GET /healthz — process liveness, не ходит во внешние зависимости.
  • GET /readyz — readiness probe; сейчас проверяет storage через SELECT 1.
  • GET /metrics — Prometheus-compatible text exposition для process-local service metrics.

Что собирается из коробки:

  • structured logs c request_id, correlation_id, event_category, event_name;
  • runtime / audit / security logger taxonomy;
  • service metrics:
    • vetmanager_http_requests_total + vetmanager_http_request_latency_seconds_{count,sum,max} — inbound HTTP web routes;
    • vetmanager_auth_failures_total{source,reason} — bearer/web auth failures grouped by reason;
    • vetmanager_upstream_failures_total{target,reason} — failures to VM/billing upstream (timeout / network_error / http_5xx / circuit_open);
    • vetmanager_upstream_requests_total{target,status} + vetmanager_upstream_request_latency_seconds_{count,sum,max} — all VM API requests with their outcome (stage 88);
    • vetmanager_tool_calls_total{endpoint,method,outcome} + vetmanager_tool_call_latency_seconds_{count,sum,max} — per-tool (endpoint+method) latency and success/error rate via crud_helpers instrumentation (stage 88);
    • vetmanager_cache_{hits,misses,invalidations,evictions}_total + vetmanager_cache_entries;
    • vetmanager_business_events_total{event=...} — lifecycle business events (account_registered, web_login_succeeded, bearer_token_issued, bearer_token_revoked) (stage 110);
    • vetmanager_token_preset_issued_total{preset} — issuance counter by access preset;
    • vetmanager_account_last_request_age_hours{account_id} — hours since the last successful bearer runtime request for active accounts with active connection and live token; never-used tokens use the earliest live token creation time as the age anchor;
    • vetmanager_rate_limit_backend_degraded_total{reason} — Redis rate-limit backend fallback/strict failure counter;
    • vetmanager_sanitizer_failures_total — depersonalized response sanitizer failures;
    • /metrics endpoint gated by optional METRICS_AUTH_TOKEN env (stage 111.1): when set, requires Authorization: Bearer <token> or returns 403;
    • activation telemetry scan on /metrics runs only when METRICS_AUTH_TOKEN is configured and the request passed bearer auth;
    • invalid /metrics bearer attempts increment vetmanager_auth_failures_total{source="metrics",reason="invalid_token"} and emit a security log event metrics_auth_failed;
  • opt-in Sentry bootstrap для unhandled exceptions.

Exceptions raised by VM tools

Все клиенты MCP tools ловят исключения наследники VetmanagerError:

  • AuthError — 401/403 от Vetmanager (неверный токен/доступ запрещён);
  • NotFoundError — 404 (ресурс не существует);
  • VetmanagerTimeoutError — истёк timeout upstream запроса;
  • VetmanagerUpstreamUnavailable (stage 91) — circuit breaker OPEN для domain, fast-fail без вызова upstream. Наследует VetmanagerError, поэтому existing except VetmanagerError: ловят его без изменений;
  • RateLimitError — срабатывание локального rate limiter'а;
  • HostResolutionError — сбой резолва VM-хоста через billing API;
  • VetmanagerError — база для upstream 5xx и протокольных ошибок.

Error tracking:

  • без ERROR_TRACKING_DSN/SENTRY_DSN integration не активируется;
  • pattern-based sanitizer (stage 89) редактирует заголовки/cookies/query/body по substring'ам token|key|secret|auth|api|cookie|bearer|password|credential|session|csrf|signature|jwt|hmac|otp|passphrase, с whitelist для observability headers (x-request-id, x-correlation-id, x-api-version и т.д.).

Отдельный runbook по эксплуатации observability-контура:

  • artifacts/observability-runbook-vetmanager-mcp-ru.md

Bearer-only runtime

Рабочий MCP runtime больше не принимает X-VM-Domain и X-VM-Api-Key в HTTP-заголовках. Все tools и prompts работают только через Authorization: Bearer <service_token>.

Bearer-токен привязан к account сервиса:

  • service_bearer_token идентифицирует account;
  • account хранит ровно одно активное vetmanager_connection;
  • активное connection поддерживает auth mode domain + rest_api_key и login/password -> user token;
  • домен и Vetmanager API key хранятся в storage-слое и не передаются в MCP tool arguments.

Текущий статус provisioning

Пользовательский web-контур уже начал работать:

  • лендинг перепозиционирован под ветврачей, администраторов и руководителей клиник;
  • регистрация вынесена в главный CTA главной страницы;
  • доступны лендинг, регистрация account и login/logout;
  • доступна страница /account;
  • доступна настройка активной Vetmanager integration через wizard: сначала выбор способа авторизации, затем только релевантные поля;
  • доступна настройка domain + rest_api_key;
  • доступна настройка user-token integration через domain + login/password -> user token;
  • логин и пароль Vetmanager не сохраняются и не отображаются повторно после submit;
  • для token exchange используется POST /token_auth.php с multipart/form-data и фиксированным app_name=vetmanager-mcp, без X-REST-API-KEY;
  • для нового account кабинет показывает onboarding state с явным следующим шагом;
  • state-changing web forms защищены signed CSRF token layer;
  • /register, /login и bearer runtime защищены shared rate limiting: in-memory по умолчанию, Redis-backed при REDIS_URL;
  • HTML responses отдают baseline security headers: CSP, X-Frame-Options, Referrer-Policy, X-Content-Type-Options;
  • кабинет показывает health активной integration и статус reauth_required, если сохранённый user token больше не проходит валидацию;
  • доступен выпуск Bearer-токенов с именем, сроком действия, preset'ом доступа (full_access, read_only, frontdesk, doctor, finance, inventory, report_ai) и опциональным режимом деперсонализации ответов; report_ai отображается в UI как Analytics и даёт full read-only доступ плюс права сохранения Report AI отчётов;
  • web-выпуск безопасен по умолчанию: blank expiry становится 30 days, default preset — read_only, а full_access и *.*.*.* IP mask требуют явного подтверждения в форме;
  • после выпуска raw bearer token показывается в отдельной success-card в верхней части страницы и может быть скопирован кнопкой;
  • доступен список токенов со статусом, сроком действия, last_used_at, request_count и revoke action.

На текущем этапе в репозитории уже есть:

  • storage foundation и миграции для accounts, vetmanager_connections, service_bearer_tokens;
  • шифрование Vetmanager credentials;
  • hash-only хранение bearer-токенов;
  • сервис сохранения Vetmanager connection domain + rest_api_key;
  • web exchange login/password -> user token c сохранением только полученного user token;
  • web auth для account через email/password и signed cookie session;
  • account onboarding wizard с выбором API key или login/password;
  • signed CSRF layer для /register, /login, /logout и /account/*;
  • rate limiting для /register, /login и bearer runtime через общий backend;
  • baseline security headers для HTML-ответов web UI;
  • web-экран сохранения active Vetmanager integration;
  • web-выпуск Bearer-токенов с preset-based scopes и one-time показом raw значения;
  • централизованная деперсонализация ответов для токенов с включённым флагом: structured PII поля маскируются, free-text scrub ограничен whitelist clinical fields, а при ошибке sanitizer'а raw payload не возвращается;
  • success-card для нового raw bearer token с copy action;
  • список Bearer-токенов с usage metadata;
  • runtime usage accounting (last_used_at, request_count);
  • безопасный audit log для create/revoke Bearer-токенов.

То есть runtime-контракт уже bearer-only, а account provisioning, Vetmanager integration, token management, security baseline web-контура и продуктовый landing больше не internal-only.

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

Шаг 1 — запустить сервер

cp .env.example .env          # задайте UID/GID и LOG_LEVEL при необходимости
docker compose up -d mcp

Шаг 1.1 — создать web account

  • http://localhost:8000/register — регистрация account
  • http://localhost:8000/login — вход в account
  • http://localhost:8000/account — кабинет после входа, включая Vetmanager integration, выпуск Bearer-токенов и их список

Шаг 2 — настроить ~/.cursor/mcp.json

{
  "mcpServers": {
    "vetmanager": {
      "url": "http://localhost:8000/mcp",
      "headers": {
        "Authorization": "Bearer vm_st_your_service_token"
      }
    }
  }
}
  • Authorization должен содержать service bearer token, выданный для account сервиса.
  • domain и api_key больше не указываются в mcp.json и не передаются в tool arguments.

Политика credentials

Контекст Где берутся credentials
Cursor / Claude ~/.cursor/mcp.jsonAuthorization: Bearer <service_token>
Vetmanager domain / api_key активное vetmanager_connection выбранного account
Явный аргумент инструмента не поддерживается
e2e real tests (api_key) TEST_DOMAIN / TEST_API_KEY в .env или CI secrets
e2e real tests (user_token) TEST_USER_TOKEN или TEST_USER_TOKEN_BASE_URL + TEST_USER_LOGIN + TEST_USER_PASSWORD
Проектный .env (runtime) используется только для infra-конфига (DATABASE_URL, STORAGE_ENCRYPTION_KEY, transport settings)

Production notes

  • Rate limiting по умолчанию in-memory и process-local. Для multi-worker / multi-instance production задайте REDIS_URL, чтобы web и bearer limit state использовали общий backend.
  • Redis backend задаёт bounded connect/socket/operation timeouts. При временной недоступности Redis default policy fail-open деградирует в process-local fallback и увеличивает vetmanager_rate_limit_backend_degraded_total{reason}; RATE_LIMIT_REQUIRE_REDIS=1 делает init/runtime failures fail-closed.
  • Текущий CSRF/session hardening рассчитан на single-instance deployment с общим WEB_SESSION_SECRET; при горизонтальном масштабировании нужен единый secret и согласованный deployment policy.
  • Для production рекомендуется:
    • явный WEB_SESSION_SECRET;
    • явный STORAGE_ENCRYPTION_KEY;
    • раздельное хранение этих двух секретов;
    • WEB_TRUSTED_PROXY_IPS=<ip1,ip2> только если сервис реально стоит за доверенным reverse proxy;
    • WEB_ENABLE_HSTS=1 за HTTPS reverse proxy;
    • внешний rate limit на /register и /login.
  • Для error tracking в production:
    • задавать только production DSN;
    • держать ERROR_TRACKING_TRACES_SAMPLE_RATE=0 или низкое значение, если нужен только exception tracking;
    • проверять, что reverse proxy не добавляет в headers чувствительные данные, которые не нужны приложению.
  • Billing-resolved Vetmanager host теперь принимается только как bare HTTPS origin: без userinfo, custom port и path/query/fragment.
  • Отдельные deployment notes по security baseline этапа 44: artifacts/security-deployment-notes-vetmanager-mcp-ru.md.
  • Observability runbook этапа 45: artifacts/observability-runbook-vetmanager-mcp-ru.md.
  • Operations readiness baseline этапа 47: artifacts/operations-readiness-vetmanager-mcp-ru.md.
  • Release checklist этапа 47: artifacts/release-checklist-vetmanager-mcp-ru.md.

Тест подключения

После запуска сервера и настройки mcp.json в чате Cursor попросите:

«Покажи список клиентов клиники» — инструмент get_clients вызовется с bearer-derived account context.

Деплой на сервер

Предусловие: ssh-copy-id user@host выполнен.

Прод-хост проекта: <your-domain> (например, mcp.example.com).

# Первичная настройка (один раз)
./scripts/init_server.sh user@host

# Обновление кода и перезапуск
export FEEDBACK_FINGERPRINT_PEPPER="<stored-production-pepper>"
./scripts/deploy_server.sh user@host

По умолчанию код размещается в /opt/vetmanager-mcp. Альтернативный путь:

./scripts/init_server.sh user@host /srv/vetmanager-mcp
export FEEDBACK_FINGERPRINT_PEPPER="<stored-production-pepper>"
./scripts/deploy_server.sh user@host /srv/vetmanager-mcp

Используйте тот же сохранённый FEEDBACK_FINGERPRINT_PEPPER, что и для автоматического deploy; не генерируйте новое значение для каждого запуска.

Режим для приватного репозитория: rsync + deploy

Если сервер не может делать git clone/pull (приватный repo), используйте синхронизацию кода по SSH:

export FEEDBACK_FINGERPRINT_PEPPER="$(openssl rand -hex 32)"  # один раз для production, хранить как secret
./scripts/sync_and_deploy_server.sh root@<your-server-ip> /opt/vetmanager-mcp

FEEDBACK_FINGERPRINT_PEPPER нужно сгенерировать один раз, сохранить постоянно на уровне STORAGE_ENCRYPTION_KEY и переиспользовать во всех последующих deploy. Не регенерируйте его перед каждым запуском: смена значения ломает сопоставление уже сохранённых feedback fingerprints без отдельной миграции.

Скрипт:

  • синхронизирует проект через rsync (без .git, .env, служебных директорий);
  • запускает deploy_server.sh с SKIP_GIT_PULL=1;
  • выполняет те же smoke-check и TLS-check, что обычный deploy.

Post-deploy smoke

Локально или на сервере можно прогнать:

./scripts/post_deploy_smoke_checks.sh
./scripts/post_deploy_smoke_checks.sh http://127.0.0.1:8000 <your-domain>

Если /metrics защищён METRICS_AUTH_TOKEN, smoke script автоматически передаёт Authorization: Bearer $METRICS_AUTH_TOKEN.

Полностью автоматический деплой после push в main

Добавлен workflow: .github/workflows/deploy-prod.yml.

Он срабатывает после успешного workflow Tests для ветки main и делает:

  • rsync кода на прод-сервер;
  • запуск deploy_server.sh в режиме SKIP_GIT_PULL=1.

Нужные GitHub Secrets:

  • PROD_SSH_TARGET (пример: root@<your-server-ip>)
  • PROD_SSH_PRIVATE_KEY (приватный ключ для SSH)
  • FEEDBACK_FINGERPRINT_PEPPER (обязателен для production/PostgreSQL feedback fingerprints)
  • PROD_REMOTE_DIR (опционально, по умолчанию /opt/vetmanager-mcp)
  • PROD_SSL_DOMAIN (опционально, ваш домен)
  • PROD_CERTBOT_EMAIL (опционально, email для certbot)

FEEDBACK_FINGERPRINT_PEPPER храните как долгоживущий production secret: он должен оставаться тем же между deploy и не ротироваться без плана миграции исторических feedback fingerprints.

TLS (Let's Encrypt) и автообновление

init_server.sh настраивает nginx reverse proxy, а deploy_server.sh автоматически вызывает проверку сертификата:

  • если сертификата нет — будет первичный выпуск;
  • если до истечения осталось меньше 30 дней — будет выполнено продление;
  • после обновления сертификата nginx перезагружается автоматически.

При необходимости можно переопределить домен и email для certbot:

SSL_DOMAIN=mcp.example.com CERTBOT_EMAIL=ops@example.com \
./scripts/init_server.sh root@<your-server-ip>

SSL_DOMAIN=mcp.example.com CERTBOT_EMAIL=ops@example.com \
./scripts/deploy_server.sh root@<your-server-ip>

Обязательные внешние условия:

  • DNS A-record вашего домена должен указывать на IP сервера;
  • на сервере/в облачном firewall должны быть открыты порты 80/tcp и 443/tcp.

Прод-конфиг Cursor MCP (локально, не в репозиторий)

Добавьте отдельный сервер в локальный ~/.cursor/mcp.json.

Только прод:

{
  "mcpServers": {
    "vetmanager-prod": {
      "url": "https://<your-domain>/mcp",
      "headers": {
        "Authorization": "Bearer vm_st_prod_service_token"
      }
    }
  }
}

Оба сервера (локальный + прод) одновременно:

{
  "mcpServers": {
    "vetmanager-local": {
      "url": "http://localhost:8000/mcp",
      "headers": {
        "Authorization": "Bearer vm_st_local_service_token"
      }
    },
    "vetmanager-prod": {
      "url": "https://<your-domain>/mcp",
      "headers": {
        "Authorization": "Bearer vm_st_prod_service_token"
      }
    }
  }
}

Можно подключить разные account/token пары к разным серверам или использовать разные bearer-токены для разных записей в mcpServers.

Bearer-токен должен храниться только в локальном mcp.json пользователя или в секретах CI, но не в репозитории. Vetmanager domain и api_key должны храниться только внутри storage-слоя сервиса в зашифрованном виде.

MCP-инструменты

Инструменты работают по bearer-only контракту: runtime credentials берутся только из Authorization: Bearer <service_token>, а Vetmanager credentials резолвятся через account context. Параметры limit (1–100) и offset (0–10 000) защищены от случайных массовых выборок.

Контракт tools/list

MCP-клиенты могут использовать tools/list как источник истины по возможностям сервера. Для каждого зарегистрированного инструмента сервер публикует:

  • name
  • description
  • inputSchema

description формируется из актуальных docstrings инструментов и не должен содержать runtime credentials. Начиная с этапа 18 descriptions также включают доменные синонимы из справочника сущностей Vetmanager, чтобы LLM лучше сопоставлял пользовательские формулировки вроде хозяин, запись на приём, приходная накладная, остаток на складе с правильными MCP-инструментами. inputSchema отражает реальные типы аргументов и ограничения вроде limit: 1..100.

Agent feedback

Инструмент report_problem позволяет LLM-агенту сообщить о подозрительной ошибке, нехватке инструмента/параметра, плохом description или несовпадении документации с реальным поведением. Он доступен для активных bearer-токенов без отдельного business-scope, но не принимает runtime credentials и не должен получать raw Vetmanager payload, bearer token, API key, пароли или персональные данные.

Агент должен вызывать report_problem не только при явной ошибке, но и even when the tool call succeeded, если результат не позволяет качественно ответить пользователю: empty result but relevant records were expected, response is missing fields needed to answer, tool description/docs promised or implied a capability that the result does not provide, missing tool, parameter, filter, sort, pagination, or date semantics blocks a reasonable request, workaround was necessary because no direct tool or parameter exists, или successful response is suspicious, inconsistent, or not enough to answer. Do not call report_problem for legitimately empty results. Do not paste raw tool response bodies, raw record IDs, user's verbatim message, or full error payloads.

Feedback хранится в БД в agent_feedback_reports после redaction/truncation. Агентам нужно описывать форму проблемы, а не данные: вместо ФИО клиента, клички пациента, телефона и адреса использовать <client>, <patient>, <phone>, <address>. После этапа 150 отчёты получают possible_pii flag: старые ручные/model reports помечаются консервативно, новые reports получают флаг при privacy-like redaction или placeholders; auto-events остаются possible_pii=false, потому что не сохраняют raw error text. Проверенные workaround-и хранятся отдельно в known_issues; только они могут вернуться агенту как deterministic playbook. Runtime не вызывает LLM и не делает автоисправления кода. Offline triage выполняется через scripts/triage_agent_feedback.py.

Known-issue bootstrap and write-path diagnostic:

# Run after DB migrations are applied; the script expects known_issues and
# known_issue_match_events tables to exist.
python scripts/seed_known_issues.py --dry-run
python scripts/seed_known_issues.py --apply

# Production-safe diagnostic: pass real non-secret DB ids from an active
# account/token. Do not pass bearer token strings or Vetmanager credentials.
python scripts/seed_known_issues.py diagnostic-auto-event --apply \
  --account-id <account_id> --bearer-token-id <bearer_token_id>

Diagnostic --apply requires FEEDBACK_FINGERPRINT_PEPPER; without it the script fails closed. diagnostic-auto-event requires an explicit --apply or --dry-run; a dry-run without identity reports status=skipped, not a successful write-path validation. --apply prevalidates that supplied DB ids exist and are active before it writes synthetic diagnostic rows. Before/after production runs, check runtime logs for feedback_auto_event_failed, known_issue_lookup_failed and known_issue_match_event_write_failed.

Each diagnostic --apply intentionally writes one synthetic auto-event/report with related_tool='__stage157_diagnostic__' to prove the write path. Clean it up after production verification if those rows should not remain in analytics:

BEGIN;
DELETE FROM known_issue_match_events
WHERE related_tool = '__stage157_diagnostic__';
DELETE FROM agent_feedback_reports
WHERE source = 'auto' AND related_tool = '__stage157_diagnostic__';
DELETE FROM known_issues
WHERE related_tool = '__stage157_diagnostic__'
  AND title LIKE '[seed:stage157-diagnostic] %';
COMMIT;

Для production/PostgreSQL обязателен FEEDBACK_FINGERPRINT_PEPPER: он нужен, чтобы fingerprints ошибок хранились как non-reversible HMAC. Операторы должны запускать python scripts/triage_agent_feedback.py retention-cleanup --days 180 не реже одного раза в месяц после triage; активные new/grouped reports сохраняются до ручного разбора.

Универсальные sort/filter для list GET

Во всех list get_* инструментах поддерживаются дополнительные параметры:

  • sort: массив объектов {"property":"<field>","direction":"ASC|DESC"}
  • filter: массив объектов {"property":"<field>","value":<value>,"operator":"<op>"}

Поддерживаемые операторы фильтра:

  • =, !=, <>
  • <, <=, >, >=
  • in, not in (value должен быть массивом)
  • like

Пример:

{
  "limit": 20,
  "offset": 0,
  "sort": [{"property": "id", "direction": "DESC"}],
  "filter": [{"property": "id", "value": 10, "operator": ">="}]
}

Глобальные уведомления messages/*

Добавлены операционные инструменты для внутренних уведомлений Vetmanager:

  • send_message_to_all(message, campaign)
  • send_message_to_users(message, campaign, user_ids)
  • send_message_to_roles(message, campaign, roles)
  • get_message_reports(limit, offset, campaign="", sort=None, filter=None)

get_message_reports поддерживает обычный list-контракт limit/offset/sort/filter и дополнительный query-параметр campaign, потому что этот сценарий нужен для получения статуса конкретной рассылки.

Кеширование GET-запросов

  • Все успешные GET-запросы к Vetmanager API кешируются in-memory на 15 минут.
  • Ключ кеша: METHOD + canonical_full_url_with_sorted_query + api_key_hash + account_id.
  • api_key_hash — отпечаток (sha256) API-ключа, изолирует кеш между разными ключами.
  • account_id — добавлен с этапа 54.2.3 для строгой изоляции между аккаунтами даже при случайном совпадении api_key. Fallback на none для legacy caller'ов без account-контекста.
  • Кеш-tag: domain:entity, где entity берётся из пути /rest/api/<entity>/....
  • После успешного POST/PUT/DELETE кеш для соответствующего тега domain:entity инвалидируется.
  • Ограничение подхода: кеш живёт только в памяти процесса и полностью сбрасывается при рестарте сервера.

114 инструментов по 14 группам сущностей:

Группа Инструменты Кол-во
Client get_clients, get_debtors, get_client_by_id, get_personal_account_link_by_phone, create_client, update_client, delete_client, get_client_profile, get_inactive_clients 9
Pet get_pets, get_pet_by_id, create_pet, update_pet, delete_pet, get_pet_profile, get_inactive_pets 7
Admission get_admissions, get_admission_by_id, create_admission, update_admission, get_client_upcoming_visits, get_daily_schedule 6
MedicalCard get_medical_cards, get_medical_cards_by_client_id, get_medical_card_by_id, create_medical_card, update_medical_card, get_vaccinations 6
Invoice get_invoices, get_average_invoice, get_invoice_by_id, update_invoice, delete_invoice 5
Finance get_payments, get_payment_by_id, get_invoice_documents, get_invoice_document_by_id, delete_invoice_document, get_closing_of_invoices, get_closing_of_invoice_by_id, get_cassas, get_cassa_by_id, get_cassa_closes, get_cassa_close_by_id 11
Good get_goods, get_good_by_id, search_invoice_goods, get_good_combination, calculate_good_combination_price, create_good, update_good 7
User get_users, get_user_by_id, update_user 3
Warehouse get_good_groups, get_good_group_by_id, get_good_sale_params, get_good_sale_param_by_id, get_party_accounts, get_party_account_by_id, get_party_account_docs, get_party_account_doc_by_id, get_store_documents, get_store_document_by_id, get_suppliers, get_supplier_by_id, create_supplier, update_supplier, get_good_stock_balance 15
Clinical get_hospitalizations, get_hospitalization_by_id, create_hospitalization, update_hospitalization, get_hospital_blocks, get_hospital_block_by_id, get_diagnoses 7
Reference get_breeds, get_breed_by_id, get_pet_types, get_pet_type_by_id, get_cities, get_city_by_id, get_city_types, get_streets, get_street_by_id, get_units, get_unit_by_id, get_roles, get_role_by_id, get_user_positions, get_user_position_by_id, get_combo_manual_names, get_combo_manual_name_by_id, get_combo_manual_items, get_combo_manual_item_by_id 19
Operations get_clinics, get_clinic_by_id, get_timesheets, get_timesheet_by_id, create_timesheet, get_properties, get_anonymous_clients, send_message_to_all, send_message_to_users, send_message_to_roles, get_message_reports 11
Schedule get_doctor_free_slots 1
Report AI get_report_ai_prompt_helper, create_report_ai_job, get_report_ai_job, confirm_report_ai_job_candidate, get_report_ai_job_data, start_report_export, get_report_export_file, get_report_ai_job_export, save_report_ai_job_as_report 9

Payment REST API доступен только на чтение: Vetmanager Payment entity разрешает restList/restView, поэтому MCP не публикует create_payment.

Ограничения CRUD по API

Некоторые операции запрещены контроллерами Vetmanager REST API:

Сущность Запрещено Причина
Admission DELETE явно запрещён в filterRestAccessRules
MedicalCards DELETE явно запрещён в filterRestAccessRules
Payment CREATE, UPDATE, DELETE только restList + restView
Hospital CREATE, DELETE явно запрещены
User CREATE, DELETE явно запрещены
Suppliers DELETE не в whitelist
Cassa, PartyAccount, StoreDocument, Properties, HospitalBlock CUD только чтение

Полная матрица: artifacts/api_crud_permissions-ru.md.

MCP Prompts

20 готовых шаблонов для типовых сценариев — LLM использует их для составления цепочек вызовов инструментов:

Prompts работают по тому же bearer-only контракту, что и tools: они принимают только бизнес-параметры сценария. Runtime credentials не передаются в prompt arguments и не должны прокидываться в tool calls.

Категория Промпты
Администратор daily_schedule, find_client, client_balance, book_appointment, doctor_workload, unconfirmed_appointments
Врач pet_history, last_vaccinations, add_medical_note, current_inpatients, pet_invoices, pet_full_profile
Финансы daily_revenue, unpaid_invoices, popular_services
Склад и клиентская база search_good, low_stock, new_clients, client_no_visit
Аналитика report_ai_prompt_helper

Report AI для ad-hoc отчётов

Когда пользователю нужен отчёт, выборка, группировка, тренд или бизнес-условие, которое проще получить через конструктор отчётов Vetmanager, агент должен сначала вызвать tool get_report_ai_prompt_helper или prompt report_ai_prompt_helper, затем сформировать русский intent_text и создать async job через create_report_ai_job. Если пользователь уже дал готовый русский intent_text, helper не является обязательным preflight.

Базовый flow:

  1. get_report_ai_prompt_helper или report_ai_prompt_helper — получить правила формулировки intent и ограничения.
  2. create_report_ai_job(intent_text) — создать Report AI job.
  3. get_report_ai_job(job_id) — poll с bounded retry; статусы queued/processing означают ожидание, failed возвращается пользователю как ошибка источника.
  4. Если статус needs_confirmation, выбрать только report_id из job.candidates и вызвать confirm_report_ai_job_candidate.
  5. Если статус existing_report_matched, сразу вызвать get_report_ai_job_data.
  6. Если статус ready_to_save и нужны строки, явно вызвать save_report_ai_job_as_report с вменяемым названием отчёта; это write-tool, отчёт станет видимым в Vetmanager.
  7. get_report_ai_job_data(job_id) — получить columns, rows, total, limited.

save_report_ai_job_as_report нельзя прятать внутри read-only сценария: пользователь или вызывающий агент должен понимать, что создаётся persistent report. Для этого есть preset Analytics (report_ai): full read-only scopes + report_ai.write, без общего Full access. Для названий использовать короткие осмысленные заголовки с вопросом, периодом и MCP-origin, например MCP debtors by negative balance 2026-06-15.

Для больших таблиц get_report_ai_job_data ограничен Vetmanager upstream: если limited=true, JSON rows обрезаны. Сначала стоит сузить отчёт периодом, фильтрами или агрегацией. Полный CSV/XLSX export — fallback-only путь, когда пользователю всё ещё нужны все строки и уже известен report_id:

  1. start_report_export(report_id, filter_json=None) — fallback-only запуск /rest/api/report/StartReport; использовать только если пользователь дал report_id, явно просит CSV/XLSX или limited=true требует полного export и report_id доступен.
  2. get_report_export_file(report_file_id) — fallback-only follow-up после start_report_export; получает html_file, csv_file, csv_semicolon_file, xlsx_file; если генерация ещё идёт, повторить вызов после задержки.
  3. get_report_ai_job_export(job_id, filter_json=None) — convenience fallback только для saved/existing_report_matched jobs с job.report_id; не сохраняет ready_to_save jobs автоматически.

Ограничения export flow: список отчётов по REST не опубликован, поэтому list_reports tool нет; export работает только для отчётов с включённым REST access (allow_rest_api=1). AI reports, сохранённые текущим upstream path, могут возвращать not REST-exportable. Значения html_file/csv_file/csv_semicolon_file/xlsx_file считать чувствительными ссылками/путями к bulk clinic data.

Product metrics (ad-hoc report)

Для быстрого ответа на «сколько живых аккаунтов, кто мёртв, сколько токенов выдано, какие failures» — /product-metrics skill + scripts/product_metrics_report.py. Запускается on-demand через SSH → docker compose exec mcp.

Примеры:

/product-metrics                              # default 30-day window, top-10
/product-metrics --top-n=5                    # top-5 tables
/product-metrics --format=json                # машинно-читаемый

Метрики (не покидают prod-сервер при обычном просмотре; email маскируются al***@ex***.com — маскирование снижает случайный disclosure, но это не анонимизация: в небольшой customer base и при characteristic доменах результат остаётся частично re-identifiable. Отчёт предназначен для owner-local просмотра):

  • Accounts: total / new 24h-7d-30d / live (req within 7d) / dead (reg>30d, 0 req in 30d) / no tokens / no active connection + dead-accounts table.
  • Tokens: active / expiring in 7d / issued 24h / revoked 24h-7d.
  • Requests: total 24h-7d-30d + top-N accounts by 30d request count.
  • Failures (24h / 7d / 30d breakdown): rate_limited, revoked, expired, ip_denied, no_scopes, no_connection.
  • Feedback: reports 24h-7d-30d, new/possible-PII 30d, breakdowns by source/status/severity/category, top related tools, known-issue match events 7d-30d and top known issues by 30d match count. Feedback section outputs aggregates only; raw report text, account ids and bearer token ids are not included.

Параллельно в Prometheus-expose'е /metrics копится vetmanager_business_events_total{event=...} counter для будущих Grafana-дашбордов без изменения кода.

CI/CD

Воркфлоу Триггер Что делает
test.yml push / pull request → main два обязательных contour job: fast и default
test-real.yml вручную (workflow_dispatch) opt_in_real contour: real API e2e и opt-in real browser tests
deploy-prod.yml автоматически после успешного test.yml на main rsync кода на сервер, docker build, restart, smoke checks

Добавить secrets для real tests:

  • VETMANAGER_TEST_API_KEY
  • опционально VETMANAGER_TEST_USER_TOKEN
  • либо VETMANAGER_TEST_USER_LOGIN и VETMANAGER_TEST_USER_PASSWORD
  • для real browser tests дополнительно включать RUN_REAL_BROWSER_TESTS=1

Артефакты

Путь Назначение
artifacts/prd-vetmanager-mcp-ru.md Требования к продукту: видение, цели, персоны, функциональные и нефункциональные требования
artifacts/technical-requirements-vetmanager-mcp-ru.md Технические требования: архитектура, стек, структура проекта
artifacts/api_entity_reference-ru.md Справочник по сущностям Vetmanager API (38 сущностей)
artifacts/api_crud_permissions-ru.md Матрица разрешённых CRUD-операций по Vetmanager REST API
artifacts/api-research-notes-ru.md Накопленные неочевидные знания об API (чеклист полей, filter operators, edge cases) — читать перед work with admission/pet/medical_card/timesheet
artifacts/vetmanager_openapi_v6.json Спецификация OpenAPI v6 для Vetmanager REST API
artifacts/vetmanager_postman_collection.json Коллекция Postman для ручного тестирования
artifacts/review/*.md Периодические deep-review (super-review) отчёты с findings + Codex arbitration
artifacts/security-deployment-notes-vetmanager-mcp-ru.md Чек-лист для production deploy'я (если присутствует)
artifacts/observability-runbook-vetmanager-mcp-ru.md Runbook для операций с метриками и логами (если присутствует)
artifacts/operations-readiness-vetmanager-mcp-ru.md Go/No-Go чеклист готовности к прод (если присутствует)
artifacts/release-checklist-vetmanager-mcp-ru.md Регламент релиза (если присутствует)

Self-hosted / Развернуть у себя

Этот проект — open-source. Вы можете развернуть собственный экземпляр MCP-сервера для вашей клиники или организации.

Что нужно:

  1. Сервер с Docker (любой VPS/dedicated)
  2. Домен с DNS A-record на IP сервера
  3. SSH-доступ к серверу
git clone https://github.com/otis22/vetmanager-mcp.git
cd vetmanager-mcp
./scripts/init_server.sh root@<your-server-ip>
./scripts/deploy_server.sh root@<your-server-ip>

Скрипт init_server.sh автоматически настроит Docker, PostgreSQL, Nginx reverse proxy и TLS-сертификат через Let's Encrypt. Подробнее — в секции Деплой на сервер.

Contributing

Проект открыт для контрибуций. Если вы нашли баг или хотите предложить улучшение:

Перед отправкой PR убедитесь, что тесты проходят: docker compose --profile test run --rm test.

License

MIT

推荐服务器

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 模型以安全和受控的方式获取实时的网络信息。

官方
精选