Skip to main content
Glama
tung2744
by tung2744

test-mcp

Минимальный MCP-сервер ресурсов для ручного тестирования поддержки динамической регистрации клиентов (Dynamic Client Registration, DCR) и индикатора ресурса в Authgear (docs/specs/dcr.md, docs/specs/access-token-audience-binding.md из репозитория authgear-server).

Сам по себе он не делает ничего интересного — его единственная задача: разместиться позади Authgear как авторизационного сервера и дать реальному MCP-клиенту прогнать весь процесс: discovery → самостоятельная DCR-регистрация → authorize/consent через PKCE → обмен токена с привязкой к resource этого сервер → аутентифицированный вызов MCP-инструмента.

Как компоненты связаны между собой

MCP client  --1. GET /mcp (no token)-->  test-mcp
            <--2. 401 + WWW-Authenticate: Bearer resource_metadata="..."--

MCP client  --3. GET /.well-known/oauth-protected-resource-->  test-mcp
            <--4. { resource, authorization_servers: [Authgear] }--

MCP client  --5. GET /.well-known/oauth-authorization-server-->  Authgear
            <--6. { registration_endpoint, authorization_endpoint, ... }--

MCP client  --7. POST /oauth2/register-->  Authgear   (DCR)
MCP client  --8. /oauth2/authorize + consent, resource=<RESOURCE_URI>--> Authgear
MCP client  --9. POST /oauth2/token, resource=<RESOURCE_URI>-->  Authgear
            <--10. JWT access token, aud=[RESOURCE_URI]--

MCP client  --11. POST /mcp, Authorization: Bearer <token>-->  test-mcp
            <--12. tool result (or 401 if scope/audience don't match)--

Шаги 1–2 и 11–12 выполняются на этом сервере. Всё, что между ними, делает Authgear, который любой MCP-клиент, соответствующий спецификации, обнаруживает автоматически: вы не указываете клиенту URL Authgear напрямую.

Related MCP server: MCP Server OAuth Toy

Предварительные требования

  • Запущенный экземпляр Authgear с включённой DCR, например в authgear.yaml:

    oauth:
      dynamic_client_registration:
        enabled: true
        initial_access_token_required: false # open registration, for easy testing
  • Ресурс (Resource) в этом проекте, соответствующий указанному ниже RESOURCE_URI, с access_policy.allow_dynamic_third_party_client_access: true на самом Ресурсе и на каждом Scope, который нужен тестовым инструментам, — иначе запрос DCR-клиента с resource= получит invalid_target/invalid_scope. Создайте его через admin API GraphQL (или через admin_api_graphql в e2e-тесте, если вы работаете из репозитория authgear-server):

    mutation {
      createResource(input: {
        resourceURI: "https://localhost:8090"
        name: "test-mcp"
        accessPolicy: { allowDynamicThirdPartyClientAccess: true }
      }) {
        resource { id }
      }
    }
    
    mutation {
      createScope(input: {
        resourceURI: "https://localhost:8090"
        scope: "read:tools"
        accessPolicy: { allowDynamicThirdPartyClientAccess: true }
      }) {
        scope { id }
      }
    }
    
    mutation {
      createScope(input: {
        resourceURI: "https://localhost:8090"
        scope: "execute:tools"
        accessPolicy: { allowDynamicThirdPartyClientAccess: true }
      }) {
        scope { id }
      }
    }

    https://localhost:8090 должен байт-в-байт совпадать с RESOURCE_URI ниже, и это должен быть реальный origin данного сервера (схема + хост + порт), а не какой-то произвольный плейсхолдер. Это фиксируют два независимых ограничения:

    • Authgear требует, чтобы URI любого Ресурса был https:// (pkg/lib/resourcescope/formats.go).

    • Согласно RFC 8707, поле resource в metadata защищённого ресурса должно соответствовать URL (или origin), к которому клиент фактически подключается, и строгие клиенты это проверяют — MCP Inspector откажется подключаться с ошибкой вида Protected resource ... does not match expected ... (or origin), если вы направите RESOURCE_URI на какой-нибудь unrelated идентификатор вместо реального адреса сервера.

    Именно поэтому этот сервер по умолчанию работает через HTTPS (самоподписанный сертификат), а не через обычный HTTP: https://localhost:<PORT> одновременно является валидным URI ресурса Authgear и уже настоящим origin этого сервера. Если вы меняете PORT, приведите URI Ресурса (и RESOURCE_URI ниже) в соответствие.

Настройка

npm install
npm run setup   # generates a self-signed TLS cert for localhost (see below)

Запуск

npm start

Переменные окружения (все не обязательны):

Переменная

Значение по умолчанию

Описание

PORT

8090

Порт, который прослушивает этот сервер.

AUTHGEAR_ENDPOINT

http://localhost:4000

Базовый URL вашего экземпляра Authgear. Используйте http://localhost:3000, если вы идёте напрямую к процессу make start, или http://localhost:3100, если используете традиционный локальный dev-прокси nginx (docker compose up -d proxy) — в любом случае это должен быть адрес, где реально отвечает /.well-known/openid-configuration.

RESOURCE_URI

https://localhost:<PORT>

Идентификатор ресурса по RFC 8707. Он должен совпадать с созданным выше ресурсом и быть реальным origin данного сервера (см. выше).

USE_HTTP

не задано

Установите в 1, чтобы работать по простому HTTP вместо HTTPS. Не рекомендуется: при USE_HTTP=1 RESOURCE_URI больше не может равняться реальному origin сервера (пришлось бы использовать http://..., который Authgear не принимает как Resource URI), поэтому у строгого MCP-клиента проверка соответствия ресурса не пройдёт. Используйте только способом, при котором вы точно знаете, что клиент такую проверку не делает.

Тестирование с реальным MCP-клиентом

MCP Inspector (рекомендуемый первый шаг)

npx @modelcontextprotocol/inspector

Откройте выводимый локальный URL, введите URL сервера https://localhost:8090/mcp и подключитесь — панель «Auth» в Inspector пошагово проводит через discovery, DCR и обмен authorize/token, так что вы можете увидеть содержимое каждого ответа.

Раз сертификат самоподписанный, возможно, потребуется указать Node, чтобы он доверял ему в исходящих запросах самого Inspector:

NODE_EXTRA_CA_CERTS=$(pwd)/certs/localhost.crt npx @modelcontextprotocol/inspector

(Делайте это только для локального тестирования — никогда не отключайте валидацию сертификатов для того, что обращается к настоящему серверу.)

mcp-remote (для тестирования с Claude Desktop)

npx mcp-remote https://localhost:8090/mcp

и настройте конфигурацию Claude Desktop на получившийся локальный stdio-мост в соответствии с документацией mcp-remote.

Что важно проверить

  • Запрос без resource= (обычный OIDC-клиент или MCP-клиент, который не передаёт resource: Authgear по умолчанию выдаст стороннему/DCR-клиенту opaque токен. Этот сервер не может проверить opaque-токен вообще (это не JWT), поэтому все вызовы инструментов будут оканчиваться 401 — это ожидаемое поведение (docs/specs/dcr.md, docs/specs/access-token-audience-binding.md): непривязанный сторонний токен можно использовать только в Authgear /oauth2/userinfo и нигде ещё.

  • Запрошенный resource=<RESOURCE_URI>: Authgear выдаёт JWT с aud: [RESOURCE_URI]. После этого whoami должен успешно работать, независимо от выданных scope; list_widgets/run_widget же работают, только если на этапе consent был выдан соответствующий scope (read:tools/execute:tools).

  • Токен, привязанный к другому ресурсу, или токен, чей Scope/Resource не имеет allow_dynamic_third_party_client_access: будет отклонён самим Authgear (invalid_target/invalid_scope) ещё до того, как попадёт на этот сервер.

Устранение неполадок

  • Failed to connect ... Protected resource <X> does not match expected <Y> (or origin) (MCP Inspector или другой клиент, строго следующий RFC 8728) — значит, в RESOURCE_URI указано не то, что является реальным origin этого сервера. Приведите RESOURCE_URI (и соответствующий ресурс в Authgear) к https://localhost:<PORT>, а не к произвольному плейсхолдеру — см. «Предварительные ре-clerk».

  • invalid_target в /oauth2/authorize или /oauth2/token — у Ресурса и/или конкретного Scope не задан access_policy.allow_dynamic_third_party_client_access: true, либо значение resource=, которое передал клиент, не совпадает с зарегистрированным.

  • 401 с этого сервера и error_description: "fetch failed" — сервер не смог связаться с AUTHGEAR_ENDPOINT, чтобы получить discovery-метаданные; проверьте, что Authgear там полностью поднят.

  • 401 с ошибкой проверки JWT — токен настоящий, но либо истёк, либо подписан другим решение об эмиттере, либо привязан к другому aud, нежели RESOURCE_URI.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A proof-of-concept MCP server implementing OAuth 2.1 authorization with CIMD client registration and PKCE, demonstrating protected resource access and step-up authentication.
    -
  • -
    license
    Not graded
    quality
    F
    maintenance
    A minimal remote (Streamable HTTP) MCP server that is an OAuth 2.1 resource server, demonstrating the MCP authorization spec with token validation and audience checks.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    A demo MCP server protected by OAuth (DCR), enabling hands-on exploration of OAuth flow for local MCP servers.
    MIT