test-mcp
A minimal MCP server that tests Authgear's Dynamic Client Registration and resource-indicator flow, verifying JWTs bound to a specific resource.
README
test-mcp
A minimal MCP resource server for manually testing Authgear's Dynamic Client
Registration (DCR) + resource-indicator support (docs/specs/dcr.md,
docs/specs/access-token-audience-binding.md in the authgear-server repo).
It does nothing interesting on its own — its only job is to sit behind
Authgear-as-authorization-server and let a real MCP client exercise the
whole flow: discovery → DCR self-registration → PKCE authorize+consent →
token exchange bound to this server's resource → an authenticated MCP tool
call.
How the pieces fit together
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)--
Steps 1-2 and 11-12 happen against this server. Everything in between is Authgear, discovered automatically by any spec-compliant MCP client — you don't configure the client with Authgear's URL directly.
Prerequisites
-
A running Authgear instance with DCR enabled, e.g. in
authgear.yaml:oauth: dynamic_client_registration: enabled: true initial_access_token_required: false # open registration, for easy testing -
A Resource registered in that project matching
RESOURCE_URIbelow, withaccess_policy.allow_dynamic_third_party_client_access: trueon the Resource itself and on every Scope the test tools need — otherwise a DCR client'sresource=request getsinvalid_target/invalid_scope. Create it via the Admin API GraphQL playground (oradmin_api_graphqlin an e2e test, if you're doing this from within theauthgear-serverrepo):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:8090must matchRESOURCE_URIbelow byte-for-byte, and must be this server's own real origin (scheme + host + port), not an arbitrary placeholder. Two independent constraints pin it down:- Authgear requires every Resource URI to be
https://(pkg/lib/resourcescope/formats.go). - RFC 9728 protected resource metadata's
resourcefield is expected to match the URL (or origin) the client actually connected to, and strict clients enforce this — MCP Inspector will refuse to connect with an error likeProtected resource ... does not match expected ... (or origin)if you pointRESOURCE_URIat an unrelated identifier instead of the server's real address.
That combination is exactly why this server defaults to serving HTTPS (self-signed) rather than plain HTTP:
https://localhost:<PORT>is simultaneously a valid Authgear Resource URI and this server's genuine origin. If you changePORT, update the Resource's URI (andRESOURCE_URIbelow) to match. - Authgear requires every Resource URI to be
Setup
npm install
npm run setup # generates a self-signed TLS cert for localhost (see below)
Running
npm start
Environment variables (all optional):
| Var | Default | Meaning |
|---|---|---|
PORT |
8090 |
Port this server listens on. |
AUTHGEAR_ENDPOINT |
http://localhost:4000 |
Base URL of your Authgear instance. Use http://localhost:3000 if you're hitting the make start process directly, or http://localhost:3100 if you're going through the conventional local-dev nginx proxy (docker compose up -d proxy) — either way this must be wherever /.well-known/openid-configuration actually resolves. |
RESOURCE_URI |
https://localhost:<PORT> |
The RFC 8707 resource identifier — must match the Resource created above, and must be this server's real origin (see above). |
USE_HTTP |
unset | Set to 1 to serve plain HTTP instead of HTTPS. Not recommended: with USE_HTTP=1, RESOURCE_URI can no longer equal this server's real origin (it'd have to be http://..., which Authgear rejects as a Resource URI), so a strict MCP client's resource-match check will fail. Only use this against a client you know doesn't enforce that check. |
Testing with a real MCP client
MCP Inspector (recommended first step)
npx @modelcontextprotocol/inspector
Open the printed local URL, set the server URL to https://localhost:8090/mcp,
and connect — Inspector's "Auth" panel walks through discovery, DCR, and the
authorize/token exchange step by step, so you can see exactly what each
response contains.
Since the cert is self-signed, you may need to tell Node to trust it for the Inspector's own outgoing requests:
NODE_EXTRA_CA_CERTS=$(pwd)/certs/localhost.crt npx @modelcontextprotocol/inspector
(Only do this for local testing — never disable certificate validation for anything that talks to a real server.)
mcp-remote (for testing against Claude Desktop)
npx mcp-remote https://localhost:8090/mcp
and point Claude Desktop's config at the resulting local stdio bridge per
mcp-remote's own docs.
What to look for
- No
resource=requested (a plain OIDC client, or an MCP client that doesn't sendresource): Authgear issues an opaque token to a third-party/DCR client by default. This server can't verify an opaque token at all (it isn't a JWT), so every tool call fails with 401 — this is the intended behavior (docs/specs/dcr.md,access-token-audience-binding.md): an unbound third-party token is only usable at Authgear's own/oauth2/userinfo, nowhere else. resource=<RESOURCE_URI>requested: Authgear issues a JWT withaud: [RESOURCE_URI].whoamishould now succeed regardless of granted scopes;list_widgets/run_widgetsucceed only if the corresponding scope (read:tools/execute:tools) was granted at consent time.- A resource-bound token from a different resource, or one whose
Resource/Scope lacks
allow_dynamic_third_party_client_access: rejected at Authgear itself (invalid_target/invalid_scope) before it ever reaches this server.
Troubleshooting
Failed to connect ... Protected resource <X> does not match expected <Y> (or origin)(MCP Inspector, or another RFC-9728-strict client) —RESOURCE_URIis set to something other than this server's real origin. FixRESOURCE_URI(and the matching Resource in Authgear) to behttps://localhost:<PORT>, not an arbitrary placeholder — see "Prerequisites" above.invalid_targetat/oauth2/authorizeor/oauth2/token— the Resource (and/or the specific Scope) doesn't haveaccess_policy.allow_dynamic_third_party_client_access: true, or theresource=value the client sent doesn't exactly match what's registered.- 401 from this server with
error_description: "fetch failed"— this server couldn't reachAUTHGEAR_ENDPOINTto fetch discovery metadata; check Authgear is actually running there. - 401 with a JWT-verification error — the token is real but either expired,
signed by a different issuer, or bound to a different
audthanRESOURCE_URI.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。