- Python 100%
Потолок в общей библиотеке сильнее любого решения в сервере: он держал бы на legacy весь флот, потому что зависимость одна на всех. Проверено, а не предположено — на 3.4.7 все 20 тестов проходят без правок: auth=, RemoteAuthProvider.get_routes(mcp_path), Middleware, get_access_token и ToolError в 3.x на месте. Из того, чем пользуется флот, 3.0 убрал только tool_serializer, а его этот пакет не трогает. |
||
|---|---|---|
| mcp_fleet_auth | ||
| tests | ||
| .gitignore | ||
| pyproject.toml | ||
| README.md | ||
mcp-fleet-auth
Обвязка OAuth 2.1 и гейт лицензии — одна на весь флот MCP-серверов.
Токены выпускает общий издатель mcp-auth, здесь — только проверка предъявленного bearer, публикация метаданных RFC 9728 и отказ инструмента тому, у кого нет лицензии на этот сервис.
Зачем пакет, а не файл в каждом сервере
Он и был файлом. К третьему серверу их стало три разных: excel гейтил инструменты декоратором, rusprofile — прослойкой, web-search двумя своими модулями. Одна работа, три ответа, и не сказать, какой текущий. Здесь это опаснее обычного дублирования: код решает, кому дать платный доступ, поэтому разъехавшаяся копия либо раздаёт доступ, либо запирает — и молча в обоих случаях.
Установка
Пин по тегу, чтобы обновление было осознанным:
dependencies = [
"mcp-fleet-auth @ https://git.dev.andyjazzy.cloud/airaneel/mcp_fleet_auth/archive/v0.2.0.tar.gz",
]
Использование
Три строки в сервере:
from mcp_fleet_auth import LicenseMiddleware, auth_provider
mcp = FastMCP("Закупки ЕИС", auth=auth_provider("Zakupki MCP"))
mcp.add_middleware(LicenseMiddleware())
Всё остальное — окружение:
| Переменная | Смысл |
|---|---|
MCP_AUTH_URL |
корень издателя. Пусто — авторизации нет вовсе (stdio, разработка) |
PUBLIC_URL |
адрес, по которому сервер видят клиенты, без /mcp |
REQUIRED_SCOPE |
имя сервиса: и область токена, и срез лицензии |
LICENSE_URL |
куда идти за лицензией. Видно только внутри отказа |
MCP_AUTH_URL задан, а PUBLIC_URL или REQUIRED_SCOPE нет — сервер не
поднимется. Оба уезжают в метаданные, по которым клиент строит запрос
авторизации, и угаданное значение сломается при подключении, где ничто не
укажет обратно сюда.
За фронтом с префиксом пути
nginx на raidmen раздаёт флот по /mcp-servers/<имя>/ и проксирует
метаданные, срезая префикс. Поэтому документ отдаётся по двум путям
сразу: /.well-known/oauth-protected-resource/<путь ресурса> для клиента
и голый /.well-known/oauth-protected-resource — в него фронт превращает
первый. Один документ, не копия: алиас переиспользует тот же обработчик.
PUBLIC_URL в этом случае содержит путь целиком:
https://raidmen.ru/mcp-servers/zakupki.
Проверки
pytest