Skip to main content
Glama
tung2744
by tung2744

test-mcp

Authgear의 DCR(동적 클라이언트 등록) + resource-indicator 지원(docs/specs/dcr.md, docs/specs/access-token-audience-binding.md, authgear-server 리포지토리)을 수동으로 테스트하기 위한 최소 MCP 리소스 서버입니다.

이 서버 자체로는 특별한 기능이 없습니다. Authgear를 권한 부여 서버로 하여 그 뒤에 위치하는 것이 전부이며, 실제 MCP 클라이언트가 전체 흐름을 수행해보도록 하는 것입니다: discovery → DCR 자체 등록 → 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 클라이언트는 이를 자동으로 발견합니다. 즉, 클라이언트에 Authgear의 URL을 직접 설정할 필요가 없습니다.

Related MCP server: MCP Server OAuth Toy

전제 조건

  • DCR이 활성화되어 실행 중인 Authgear 인스턴스. 예를 들어 authgear.yaml에서:

    oauth:
      dynamic_client_registration:
        enabled: true
        initial_access_token_required: false # open registration, for easy testing
  • 아래 RESOURCE_URI와 일치하는 Resource를 해당 프로젝트에 등록하고, Resource 자체와 테스트 도구에 필요한 모든 Scope에 access_policy.allow_dynamic_third_party_client_access: true를 설정해야 합니다. 그렇지 않으면 DCR 클라이언트의 resource= 요청이 invalid_target/invalid_scope 오류를 받게 됩니다. Admin API GraphQL 플레이그라운드(또는 authgear-server 리포지토리 안에서 진행 중이라면 e2e 테스트의 admin_api_graphql)로 생성하세요:

    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와 byte-for-exact해 일치해야 하며, 그리고 이 서버의 실제 오리진(스킴 + 호스트 + 포트)이어야 합니다. 임의의 자리표시자가 아닙니다. 이는 두 가지 독립적인 제약 때문입니다.

  • Authgear는 모든 Resource URI가 https://이도록 요구합니다 (pkg/lib/resourcescope/formats.go).

  • RFC 9728 보호 리소스 메타데이터의 resource 필드는 클라이언트가 실제로 연결한 URL(또는 오리진)과 일치할 것으로 기대되며, 엄격한 클라이언트는 이를 강제합니다. RESOURCE_URI를 서버의 실제 주소 대신 연결 관련 식별자를 지정하면 MCP Inspector는 Protected resource ... does not match expected ... (or origin) 같은 오류로 연결을 거부합니다.

이 바로 그 조합 때문에 이 서버는 일반 HTTP가 아니라 HTTPS(자체 서명)를 기본으로 서빙합니다. https://localhost:<PORT>는 동시에 유효한 Authgear Resource URI이면서 이 서버의 진짜 오리진입니다. PORT을 변경하면 Resource의 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

Authgear 인스턴스의 기본 URL. make start 프로세스 서버를 직접 사용하면 http://localhost:3000, 또는 일반적인 https://http://localhost:3100`을 local dev nginx 프록시(docker compose up -d proxy)를 거치면 http://localhost:3100을 로컬하십시오. 어느 쪽이든 /.well-known/openid-configuration이 실제로 resolve되는 어디 안 되는 주소여야 합니다.

RESOURCE_URI

https://localhost:<PORT>

RFC 8707 리소스 식별자. 위에서 등록한 Resource와 일치해야 하며, 이 서버의 실제 오리진이어야 합니다(위 참고).

USE_HTTP

unset 가리키지 않음

1 값을 주면 HTTPS 대신 일반 HTTP를 제공합니다. 권장하지 않음: USE_HTTP=1이면 RESOURCE_URI가 더 이상 이 서버의 실제 오리진과 같을 수 없습니다(http://여야 하는데, Authgear는 그런 Resource URI를 거부합니다). 따라서 엄격한 MCP 클라이언트의 리소스 일치 검사가 실패합니다. 이 검사를 강제하지 않는 클라이언트에 대해서만 사용하세요.

실제 MCP 클라이언트로 테스트하기

MCP Inspector (첫 단계로 권장)

npx @modelcontextprotocol/inspector

출력된 로컬 URL을 열고 서버 URL을 https://localhost:8090/mcp로 설정한 뒤, 연결하세요. Inspector의 “Auth” 패널이 discovery, DCR, 인가/토큰 교환을 단계별로 진행하므로 각 응답에 무엇이 들어 있는지 정확히 확인할 수 있습니다.

인증서가 자체 서명이므로 Inspector의 자체 요청을 위해 Node에 신뢰를 알려줘야 할 수 있습니다:

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

(로컬 테스트에서만 하세요. 실제 서버와 통신하는 어떤 것에서도 인증서 검증을 비활성화하면 안 됩니다.)

mcp-remote (Claude Desktop용 테스트)

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

그런 다음 mcp-remote의 문서에 따라 Claude Desktop 설정이 생성된 로컬 stdio 브리지를 바라보게 하면 됩니다.

확인할 것들

  • resource= 요청 없음 (일반 OIDC 클라이언트 또는 resource를 보내지 않는 MCP 클라이언트): Authgear는 기본적으로 제3자/DCR 클라이언트에 불투명(opaque) 토큰을 발급합니다. 이 서버는 불투명 토큰을 전혀 검증할 수 없습니다(JWT가 아니므로), 그래서 모든 도구 호출은 401로 실패합니다. 이는 의도된 동작입니다 (docs/specs/dcr.md, access-token-audience-binding 참고): 바인딩되어 있지 않은 제3자 token은 Authgear 자체의 /oauth2/userinfo`에서만 사용할 수 있고 다른 곳에서는 사용할 수 없는 것입니다.

  • resource=<RESOURCE_URI> 요청됨: Authgear는 aud: [RESOURCE_URI]を持つ JWT를 발급합니다. 이로 부여된 스코프와 무관하게 whoami는 성공해야 합니다. list_widgets/run_widget은 동의 시 해당 스코프(read:tools/execute:tools)가 부여된 경우에만 성공합니다.

  • 다른 리소스에 바인딩된 리소스 바인딩된 토큰, 또는 Resource/Scope에 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-9728 엄격 클라이언트) — RESOURCE_URI가 이 서버의 실제 오리진이 다른 값으로 설정되어 있습니다. RESOURCE_URI(및 Authgear의 일치하는 Resource)를 임의의 자리표시자가 아닌 https://localhost:<PORT>로 수정하세요. 위 “전제 조건” 참고.

  • /oauth2/authorize 내지 /oauth2/token 솔에 invalid_target — Resource(및/또는 특정 Scope에 access_policy.allow_dynamic_third_party_client_access: true가 없거나, 클라이언트가 보낸 resource=` 값이 등록된 값과 정확히 일치하지 않는 경우입니다.

  • 이 서버에서 401과 error_description: "fetch failed" — 이 서버가 discovery 메타데이터를 회수 위해 AUTHGEAR_ENDPOINT에 접근하지 못했습니다. 실제로 Authgear가 그 주소에서 실행 중인지 확인하세요.

  • JWT 검증 오류와 401 — 토큰은 실제하지만 만료되었거나, 다른 issuer가 서명했거나, RESOURCE_URI가 아닌 다른 aud에 바인딩되어 있을 수 있습니다.

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