mcp-resource-server
mcp-resource-server
MCP-сервер, предоставляющий инструменты преимущественно для чтения в десяти имитированных вертикалях (банковское дело, здравоохранение, государственное управление, производство, розничная торговля, спортивные товары, университет, рабочая сила, Abercrombie & Fitch, авиалинии), а также набор инвестиционных инструментов. Каждая вертикаль читает свою собственную встроенную базу данных SQLite — для работы не требуется никакой другой сервис. Инвестиционные инструменты могут при необходимости проксировать запросы к предоставленному вами банковскому API (см. ниже).
Предварительные требования
Docker + Docker Compose
Окружение PingOne для выпуска и проверки bearer-токенов (или любой OAuth AS, способный выдавать JWT с правильными claims
aud/scopeи публиковать конечную точку JWKS — этот сервер полагается только на стандартное OIDC discovery, а не на что-то специфичное для PingOne)
Related MCP server: mock-mcp
Быстрый старт
cp .env.example .envОтредактируйте .env: задайте MCP_RESOURCE_SERVER_RESOURCE_URI, PINGONE_ENVIRONMENT_ID и PINGONE_REGION для вашего собственного окружения PingOne. Оставьте PINGONE_ISSUER закомментированным, пока у вас не появятся настоящие токены (см. «Режимы аутентификации»).
docker compose up --buildСервер прослушивает http://localhost:8081. Базы данных SQLite сохраняются в ./data (заполняются при первом использовании из seed/; перезапуск никогда не перезаполняет непустую базу данных).
Проверка, что сервер работает
curl http://localhost:8081/health
curl http://localhost:8081/.well-known/oauth-protected-resourceВторой вызов возвращает объявленные области действия ресурса и, если заданы PINGONE_ENVIRONMENT_ID/PINGONE_REGION, его сервер авторизации — это метаданные RFC 9728, которые MCP-клиент использует для OAuth discovery.
Режимы аутентификации
Проверяется ли подпись токена, определяется наличием источника JWKS — а не STRICT_AUTH:
Источник JWKS задан (
PINGONE_ISSUER,PINGONE_JWKS_URIилиPINGONE_BASE_URL) — каждый токен проверяется по ключам вашего окружения PingOne и отклоняется при неудаче.STRICT_AUTHне влияет. Используйте этот режим, когда начнут поступать настоящие токены.Нет источника JWKS —
STRICT_AUTH=false(поставляемое по умолчанию) принимает корректно сформированный токен с предупреждением в консоли, так что вы можете опробовать каждый инструмент с вручную созданным токеном до подключения PingOne;STRICT_AUTH=trueвместо этого отклоняет каждый токен. Не оставляйте значение по умолчанию доступным для большего числа пользователей, чем необходимо.
.env.example поставляется со всеми тремя переменными JWKS закомментированными по этой причине — раскомментируйте одну, когда у вас появятся настоящие токены.
Получение bearer-токена
Каждый вызов инструмента требует bearer-токен, у которого claim aud совпадает с MCP_RESOURCE_SERVER_RESOURCE_URI, а claim scope покрывает вызываемый инструмент (см. tools/list для авторитетного актуального списка — он генерируется из собственного реестра этого сервера).
Локальное тестирование (STRICT_AUTH=false) — подойдёт любой корректно сформированный JWT с нужными claims; подпись не проверяется.
node -e "
const b64 = s => Buffer.from(JSON.stringify(s)).toString('base64url');
const header = b64({alg:'none',typ:'JWT'});
const payload = b64({sub:'test-user',scope:'airlines:read',aud:'your-resource-uri',exp:Math.floor(Date.now()/1000)+3600});
console.log(header+'.'+payload+'.');
"(замените aud на ваше собственное значение MCP_RESOURCE_SERVER_RESOURCE_URI, а scope — на те инструменты, которые вы тестируете)
Настоящие токены (STRICT_AUTH=true) — выпустите один из вашего окружения PingOne. Grant client-credentials против конечной точки токенов вашего PingOne, запрашивающий аудиторию этого сервера в качестве ресурса:
curl -s -X POST "https://auth.pingone.<region>/<env-id>/as/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=<your PingOne worker app client id>" \
-d "client_secret=<your PingOne worker app client secret>" \
-d "scope=<space-separated scopes, e.g. banking:read airlines:read>" \
-d "resource=<MCP_RESOURCE_SERVER_RESOURCE_URI value>"Это требует, чтобы приложение этого клиента было авторизовано для этого ресурса и этих областей действия в PingOne (Applications → ваше приложение → Resources) — шаг настройки на стороне PingOne, который этот сервер за вас не выполняет.
Прямой вызов инструмента (проверка работоспособности)
TOKEN="<paste a token from above>"
curl -s -X POST http://localhost:8081/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_airline_bookings","arguments":{}}}'Подключение MCP-клиента
Сервер говорит по MCP как через WebSocket, так и через HTTP (streamable, POST /mcp) на одном порту — ws://localhost:8081 или http://localhost:8081/mcp.
MCP Inspector (официальный инструмент разработчика — работает с любым сервером и позволяет задать заголовок вручную, так что это самый надёжный способ протестировать этот сервер):
npx @modelcontextprotocol/inspectorУстановите Transport в «Streamable HTTP», URL — http://localhost:8081/mcp, и добавьте заголовок Authorization: Bearer <token> в настройках подключения Inspector перед подключением.
Claude Desktop / Cursor / Windsurf (статическая конфигурация, HTTP-транспорт):
{
"mcpServers": {
"mcp-resource-server": {
"url": "http://localhost:8081/mcp",
"transport": "http"
}
}
}Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.jsonCursor:
.cursor/mcp.jsonв корне вашего проектаWindsurf:
~/.codeium/windsurf/mcp_config.json
В этих конфигурациях нет поля для статического bearer-токена — когда клиент вызывает защищённый инструмент, он читает /.well-known/oauth-protected-resource, находит ваше окружение PingOne как сервер авторизации и предлагает вам войти. Это работает только после того, как в вашем окружении PingOne зарегистрирован OAuth-клиент для этого конкретного MCP-клиентского приложения, с использованием redirect URI, указанного в документации этого клиента — шаг настройки на стороне PingOne, вне этого сервера. Перезапустите клиент после редактирования его конфигурации.
Инвестиционные инструменты
get_investment_accounts, get_investment_balance, get_portfolio_summary и get_investment_transactions работают из коробки с встроенной базой данных SQLite (data/invest.db, заполняется из seed/invest.seed.json при первом использовании), точно так же, как и любая другая вертикаль.
Задайте BANKING_API_BASE_URL только в том случае, если вы запускаете собственный банковский API и хотите, чтобы эти четыре инструмента пересылали bearer-токен вызывающего к нему и возвращали то, что он возвращает. При заданном значении встроенная база данных invest не используется.
Добавление инструмента
Инструменты — это код, а не конфигурация. Каталог (tools/list), шлюз областей действия для каждого инструмента и scopes_supported, объявляемые в /.well-known/oauth-protected-resource, — всё это выводится из ALL_TOOLS в src/tools/registry.ts, так что инструмент, добавленный вами в список вертикали, становится доступен везде после пересборки образа (команда --build в «Быстром старте»).
1. Добавление инструмента в существующую вертикаль
Два изменения, без изменения реестра:
Добавьте определение инструмента в массив
src/tools/<vertical>Tools.tsэтой вертикали, например вsportingGoodsTools.ts:{ name: 'gear_return_status', description: 'Show the status of a sporting-goods return.', inputSchema: { type: 'object', properties: { orderId: { type: 'string', description: 'Order ID' } }, required: ['orderId'], }, requiredScopes: ['read'], // the bearer token must carry every scope listed readOnly: true, intentHints: ['check my gear return'], // required — tests/registry.test.ts asserts it },Добавьте соответствующий
case 'gear_return_status':вswitchвsrc/tools/<vertical>ToolHandler.ts. Обработчик просто возвращает JSON — читайте изsrc/db/<vertical>Db.tsэтой вертикали или откуда угодно ещё.
2. Добавление новой вертикали
Те же два файла, что и выше (src/tools/<vertical>Tools.ts, экспортирующий <VERTICAL>_TOOLS: McpToolDef[], и src/tools/<vertical>ToolHandler.ts, экспортирующий dispatch<Vertical>Tool), плюс:
Данные (необязательно):
src/db/<vertical>Db.ts+seed/<vertical>.seed.json. СкопируйтеsportingGoodsDb.ts— он открываетdata/<vertical>.db, создаёт схему и применяет сид только когда таблицы пусты.Dockerfileуже копируетseed/, а compose-файл уже монтируетdata/.Регистрация в
src/tools/registry.ts: импортируйте два экспорта, разверните<VERTICAL>_TOOLSвALL_TOOLS, добавьтеconst <VERTICAL>_TOOL_NAMES = new Set(<VERTICAL>_TOOLS.map((t) => t.name))и одну строку вdispatch():if (<VERTICAL>_TOOL_NAMES.has(toolName)) return dispatch<Vertical>Tool(toolName, args, subject);(subject— этоsubтокена; принимайте его в своём обработчике, когда чтение должно быть ограничено вызывающим, как это делают банковское дело и авиалинии.)Ресурсы (необязательно):
tools/listавтоматический, но MCP ресурсы (resources/list,resources/read) берутся из вручную поддерживаемогоRESOURCE_CATALOGвsrc/index.ts— добавьте запись туда, если вертикаль также должна предоставлять свой список инструментов как ресурс.
Что может укусить
requiredScopesдолжны быть областями действия, которые ваш ресурс PingOne действительно предоставляет. Токену, у которого отсутствует любая из них, возвращается 403 наtools/call, аtools/listполностью скрывает инструмент от этого токена.Имена инструментов глобальны.
dispatch()маршрутизирует по первому совпавшему набору имён, поэтому имя, повторно использованное в двух вертикалях, молча уходит в ту, которая проверяется первой.Используйте вертикаль с данными (например, спортивные товары) как шаблон, а не invest — invest является запасным вариантом в
dispatch()и несёт переключатель прокси-и-SQLite, который вам не нужен.
This server cannot be deployed
Maintenance
Related MCP Connectors
Demo banking MCP server: accounts, transactions and payments. OAuth 2.1 with DPoP.
An MCP server that provides read access to your cloud storage providers, bank accounts and more.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
MCP server for Modern Treasury — payment orders, transactions, counterparties and ledgers.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA lightweight MCP server that simulates financial data interactions with dummy authentication and static JSON datasets for testing financial applications.2MIT
- FlicenseNot gradedqualityBmaintenanceA minimal local HTTP MCP mock server for development and testing, providing predictable tool responses with OAuth token support and zero dependencies.-
- FlicenseNot gradedqualityDmaintenanceA production-grade MCP server for a fictional digital bank, exposing tools for an AI copilot to service customers across the full risk spectrum from read-only lookups to money movement and destructive admin actions, with OAuth 2.1 security and a realistic dataset.21 npm1-
- FlicenseNot gradedqualityDmaintenanceA Test/Sandbox MCP server that integrates with the SnapTrade API Sandbox environment to provide portfolio oversight, market data, and trading capabilities.-