Перейти к основному содержимому

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, нет возобновления
AuthBearer токен. Работают оба 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 ключа, которые наиболее вероятны.

Ввод:

ПолеТипОписание
filterstring (optional)Игнорирующая регистр подстрока, сопоставленная с путем, например "people"
methodenum (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

Возвращает краткое резюме плюс, где доступно, тщательно отобранный образец тела запроса и ответа для одной конечной точки.

Ввод:

ПолеТипОписание
methodstringHTTP глагол
pathstringПолный путь, как возвращено list_endpoints

Вывод: для отобранных конечных точек пример с summary, requestBody и responseSample. Для неотобранных конечных точек резервное сообщение, инструктирующее модель вызвать GET первым, чтобы увидеть форму. Примерно дюжина высокотрафик маршрутов (люди, группы, пожертвования, посещаемость, фонды) отобраны.

api_call

Вызывает выбранную REST конечную точку, в процессе, через ту же стек Express middleware, что и обычный HTTP запрос — аутентификация, парсинг тела, аудит логирования и per-church scoping все применяются.

Ввод:

ПолеТипОписание
methodenumGET / POST / PUT / DELETE / PATCH
pathstringПуть, включая любой префикс модуля, например /membership/people
queryobject (optional)Плоский объект параметров строки запроса
bodyany (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Отобранные образцы запроса/ответа для высокотрафик конечных точек

Связанное