MCP сервер
B1 API поставляется с MCP (Model Context Protocol) сервером на /mcp. Любой клиент, осведомленный о MCP — Claude Code, Claude Desktop, OpenAI Agents SDK, Cursor или ваш собственный — может подключиться к нему и вызвать базовый REST API от имени аутентифицированного пользователя церкви. Это тонкая, универсальная обертка: есть три инструмента, и они предоставляют всю поверхность API динамически, а не ручное моделирование каждой конечной точки.
Перед началом
- B1 API ключ (
cak_…) с областями, которые должен иметь клиент - Доступный хост B1 API —
https://api.churchapps.orgдля размещенных церквей или ваше собственное развертывание - Клиент MCP. Смотрите Claude и ChatGPT для настройки пользователя
Конечная точка
POST /mcp
Content-Type: application/json
Accept: application/json, text/event-stream
Authorization: Bearer cak_<prefix>.<secret>
| Аспект | Значение |
|---|---|
| Путь | /mcp (относительно хоста API) |
| Метод | POST только — запрос/ответ и потоковая SSE происходят на одной и той же конечной точке |
| Транспорт | MCP Streamable HTTP |
| Модель сессии | Без состояния. Свежий экземпляр MCP сервера создается на запрос — нет session id, нет возобновления |
| Auth | Bearer токен. Работают оба cak_… API ключи и B1 JWTs; разрешение то же самое, что и любой другой аутентифицированной конечной точки |
Запрос, чей заголовок Authorization отсутствует или недействителен, возвращает:
{ "error": "Unauthorized — MCP требует допустимый bearer токен (cak_* API ключ или JWT)." }
с HTTP 401.
Инструменты
Три инструмента, все универсальные. Модель использует list_endpoints для обнаружения, describe_endpoint для изучения формы полезной нагрузки и api_call для фактического вызова API.
list_endpoints
Возвращает полный инвентарь зарегистрированных REST маршрутов, отфильтрованный по необязательной подстроке и/или HTTP глаголу. Каждая запись включает имя контроллера и области API ключа, которые наиболее вероятны.
Ввод:
| Поле | Тип | Описание |
|---|---|---|
filter | string (optional) | Игнорирующая регистр подстрока, сопоставленная с путем, например "people" |
method | enum (optional) | GET / POST / PUT / DELETE / PATCH |
Вывод: документ JSON формы
{
"total": 24,
"endpoints": [
{
"method": "GET",
"path": "/membership/people",
"controller": "PersonController.getAll",
"likelyScopes": ["people:read", "people:write"]
}
]
}
Инвентарь создается один раз при запуске API из живой таблицы маршрутов — все, что вы можете hit с curl, появляется здесь.
describe_endpoint
Возвращает краткое резюме плюс, где доступно, тщательно отобранный образец тела запроса и ответа для одной конечной точки.
Ввод:
| Поле | Тип | Описание |
|---|---|---|
method | string | HTTP глагол |
path | string | Полный путь, как возвращено list_endpoints |
Вывод: для отобранных конечных точек пример с summary, requestBody и responseSample. Для неотобранных конечных точек резервное сообщение, инструктирующее модель вызвать GET первым, чтобы увидеть форму. Примерно дюжина высокотрафик маршрутов (люди, группы, пожертвования, посещаемость, фонды) отобраны.
api_call
Вызывает выбранную REST конечную точку, в процессе, через ту же стек Express middleware, что и обычный HTTP запрос — аутентификация, парсинг тела, аудит логирования и per-church scoping все применяются.
Ввод:
| Поле | Тип | Описание |
|---|---|---|
method | enum | GET / POST / PUT / DELETE / PATCH |
path | string | Путь, включая любой префикс модуля, например /membership/people |
query | object (optional) | Плоский объект параметров строки запроса |
body | any (optional) | Тело JSON запроса — обычно массив объектов модели для POST |
Вывод:
{
"status": 200,
"truncated": false,
"body": [ /* ответ JSON контроллера */ ]
}
Результат инструмента помечен isError: true для любого ответа со статусом ≥ 400.
Модель аутентификации
Сам MCP запрос проходит через CustomAuthProvider.getUser() — тот же путь, который использует каждая аутентифицированная B1 конечная точка. Носитель cak_… разрешается в Principal, чьи разрешения — это текущие RBAC выдавшего человека, пересекаемые с областями, предоставленными ключом. Это пересечение пересчитывается на каждом запросе, поэтому:
- Удаление области из ключа (путем удаления и пересоздания) отключает доступ на следующий вызов.
- Удаление разрешения из базового человека в B1Admin отключает доступ на следующий вызов, даже если ключ все еще существует.
Для вложенных вызовов api_call, исходный заголовок Authorization копируется на синтетический запрос, поэтому CustomAuthProvider запускается снова и пересечение области переприменяется для каждого вызова. Нет кэширования токена.
Списков блокировки пути
Небольшой набор маршрутов не доступен через api_call, даже с действительным ключом:
| Паттерн | Почему |
|---|---|
/giving/donate/webhook/* | Конечные точки webhook поставщика ожидают исходные, проверенные подписью тела от Stripe/PayPal — не общих вызывающих |
/membership/oauth/clients* | Регистрация OAuth клиента — только для оператора |
/membership/people/apiEmails | Заблокировано оператором jwtSecret, не разрешения пользователя |
Любой маршрут, ожидающий multipart/form-data | Загрузки файлов не дружелюбны к JSON-RPC |
Заблокированный путь возвращает результат инструмента isError: true с описательным сообщением; базовый маршрут никогда не вызывается.
Крышка размера ответа
Каждый телу ответа api_call ограничен 64 KB захватываемого вывода. Если запрос превышает крышку, ответ содержит "truncated": true и модель ожидается повторить с более узкими параметрами запроса. Это предотвращает один результат инструмента от взрыва контекстного окна клиента.
Ограничение скорости
На /mcp нет ограничения скорости уровня приложения. Дросселирование отложено на API Gateway / Lambda параллелизм в production, и на всё, что ваш обратный прокси применяет в self-hosted развертываниях.
Обнаружение OAuth
MCP сервер не рекламирует метаданные OAuth 2.1 (/.well-known/oauth-authorization-server, динамическую регистрацию клиента, поток PKCE). Клиенты, которые требуют обнаруженные MCP серверы — в частности, пользовательский интерфейс "Add custom connector" Claude.ai и функция "Connectors" ChatGPT — не могут подключиться без этой поверхности.
Клиенты, которые принимают статический bearer токен в своей конфигурации — Claude Code, Claude Desktop, OpenAI Agents SDK, Cursor, пользовательский код — работают сегодня. Существующий OAuthController уже выпускает токены через authorization-code + PKCE для приложений третьих сторон; слой обнаружения, совместимый с MCP-spec, на его вершине закрыл бы пропасть.
Локальная разработка
Конечная точка MCP монтируется наряду со всем остальным, когда API запускается локально:
cd Api
npm run dev
# Server listening on http://localhost:8084
При запуске логирование строки 📡 MCP server ready at /mcp — N routes in inventory подтверждает, что инвентарь был создан.
Зондируйте его с помощью MCP Inspector:
npx @modelcontextprotocol/inspector
В пользовательском интерфейсе Inspector укажите на http://localhost:8084/mcp и установите заголовок Authorization на Bearer cak_<prefix>.<secret>. Сначала вызовите list_endpoints; вы должны увидеть полный список маршрутов. Затем api_call({ method: "GET", path: "/membership/people" }) должен возвращать ваших локальных семян людей.
Макет кода
MCP сервер находится в src/modules/mcp/ в репо Api. Заметные файлы:
| Файл | Назначение |
|---|---|
McpController.ts | @controller("/mcp"); провода StreamableHTTPServerTransport на запрос |
McpServer.ts | Создает MCP Server, регистрирует три инструмента |
RouteInventory.ts | Ходит по inversify-express-utils метаданным при запуске для перечисления маршрутов |
internalDispatch.ts | Синтетический req/res, который переходит в Express приложение в процессе |
tools/ | listEndpoints.ts, describeEndpoint.ts, apiCall.ts |
examples.ts | Отобранные образцы запроса/ответа для высокотрафик конечных точек |