Skip to main content
Glama
beaconfire-projects

mcp-oauth-test

FastMCP OIDC Server

This is an MCP server written with FastMCP and protected by OIDC login. It uses FastMCP's OIDCProxy: the MCP client authenticates via the OAuth metadata exposed by the server, while the actual login and token exchange are forwarded to the QA OIDC provider.

It currently integrates the QA MGT OpenAPI and generates MCP tools for trainees, orders, products, customers, campus recruitment, and international recruitment.

The OIDC discovery address is configured by default as:

https://auth-qa.drillinsight.com/.well-known/openid-configuration

Authentication preparation

First register an OAuth application on auth-qa.drillinsight.com and add the following callback address to the allowlist:

http://localhost:8000/auth/callback

When deploying to another address, replace http://localhost:8000 with the value of BASE_URL. The callback address must exactly match FastMCP's BASE_URL.

Running locally

cp .env.example .env
# 编辑 .env,至少填写 OIDC_CLIENT_ID 和 OIDC_CLIENT_SECRET
uv sync
uv run mcp-oidc-server

You can also run the module directly:

uv run python -m oidc_mcp_server.server

The service listens on http://127.0.0.1:8000 by default. If the MCP client runs on another machine or in a container, set a BASE_URL that the client can access and an appropriate HOST (for example, 0.0.0.0).

Claude Code plugin

The repository contains a private Claude Code marketplace and an MCP plugin:

.claude-plugin/marketplace.json
└── plugins/mcp-oauth-test/
    ├── .claude-plugin/plugin.json
    ├── .mcp.json
    └── README.md

The plugin only connects Claude Code to an already deployed remote MCP service; it does not start a local Python service. For development testing, you can load it directly:

claude --plugin-dir ./plugins/mcp-oauth-test

The plugin is pinned to connect to the development environment MCP Server:

https://api-mcp-oauth-dev.beaconfireinc.com/mcp

You can also install it from the private marketplace:

/plugin marketplace add /path/to/mcp-oauth-test
/plugin install mcp-oauth-test@authsome-internal

The current marketplace root directory is the repository root. Keep this marketplace in the company's private GitHub repository and do not submit it to a public marketplace. Shared environments should use HTTPS addresses and restrict access to company users on both the company IdP and the MCP Server side.

Why use OIDCProxy

The upstream auth-qa.drillinsight.com does not need to support DCR or CIMD. OIDCProxy is exactly for this scenario:

ChatGPT ── MCP OAuth / CIMD ──> FastMCP OIDCProxy
                                      │
                                      └── 固定 client_id/client_secret ──> auth-qa.drillinsight.com

The only thing that needs to be registered upstream in advance is the FastMCP OAuth application, with ${BASE_URL}/auth/callback configured. The CIMD used by ChatGPT is handled by the FastMCP proxy layer and is not forwarded to the upstream OAuth server.

ChatGPT CIMD configuration

When creating a custom MCP in ChatGPT, under OAuth advanced settings, select the following for “Client registration”:

客户端标识元数据文档(CIMD)

The ChatGPT connector currently generates the following information:

CIMD Client ID / 客户端元数据 URL:
https://chatgpt.com/oauth/0Buhw3sHVv1-/client.json

ChatGPT Callback URL:
https://chatgpt.com/connector/oauth/0Buhw3sHVv1-

The CIMD URL itself is the client_id ChatGPT uses when accessing the FastMCP OAuth proxy. It does not need to be, and should not be, registered with the upstream auth-qa.drillinsight.com.

This project has two distinct layers of OAuth Client ID:

OAuth flow

client_id

Configuration location

ChatGPT → FastMCP OIDCProxy

https://chatgpt.com/oauth/0Buhw3sHVv1-/client.json

Provided automatically by ChatGPT; no need to fill it in manually after selecting CIMD

FastMCP OIDCProxy → auth-qa.drillinsight.com

app_74a4b555-5b87-4212-9dda-d584fa78caf8

MCP Server's OIDC_CLIENT_ID

The corresponding data flow is:

ChatGPT
  │ client_id=https://chatgpt.com/oauth/0Buhw3sHVv1-/client.json
  ▼
FastMCP OIDCProxy
  │ client_id=app_74a4b555-5b87-4212-9dda-d584fa78caf8
  ▼
auth-qa.drillinsight.com

MCP Server environment variable configuration:

OIDC_CLIENT_ID=app_74a4b555-5b87-4212-9dda-d584fa78caf8
OIDC_CLIENT_SECRET=<上游 OAuth Server 颁发的客户端密钥>

The upstream OAuth Server only needs to configure FastMCP's callback address for the app_... application:

https://heroic-verbally-crawdad.ngrok-free.app/auth/callback

Do not configure ChatGPT's callback address https://chatgpt.com/connector/oauth/... on the upstream OAuth Server; that address is used by the FastMCP proxy layer after authentication is complete.

When authentication starts, ChatGPT's CIMD Client ID should appear first in the normal logs:

CIMD document fetched and validated
GET /authorize?client_id=https://chatgpt.com/oauth/.../client.json ... 302

After that, FastMCP uses app_74a4b555-... to redirect to the upstream OAuth Server.

MCP client configuration

Configure the MCP address as:

http://localhost:8000/mcp

FastMCP provides the following authentication discovery addresses:

http://localhost:8000/.well-known/oauth-authorization-server
http://localhost:8000/.well-known/oauth-protected-resource/mcp

The client should automatically read these MCP/OAuth discovery endpoints. After successful login, two protected tools can be called:

  • ping: health check.

  • who_am_i: returns the client_id, scope, and claims that FastMCP extracts from the current authentication token.

Configuration options

Environment variable

Required

Default

Description

OIDC_CLIENT_ID

Yes

-

Upstream OIDC client ID

OIDC_CLIENT_SECRET

One of the two

-

confidential client secret

JWT_SIGNING_KEY

One of the two

-

public PKCE client or production FastMCP token signing key

OIDC_CONFIG_URL

No

QA discovery URL

OIDC discovery URL

BASE_URL

No

http://localhost:8000

MCP server public address

OIDC_REQUIRED_SCOPES

No

openid

OAuth authorization request / default advertised scope; not used for access token scope validation

OIDC_TOKEN_ISSUER

No

OIDC discovery issuer

JWT iss validation value; set when the auth middleware rewrites the issuer

OIDC_JWKS_URI

No

https://auth-qa.drillinsight.com/oauth/jwks

JWKS URL for the custom token issuer

OIDC_TOKEN_AUDIENCE

No

-

Optional JWT aud validation value

HOST

No

127.0.0.1

Listening address

PORT

No

8000

Listening port

MGT_API_BASE_URL

No

QA MGT address

Actual Base URL used for MGT API calls

MGT_OPENAPI_SPEC_PATH

No

specs/mgt-qa-openapi.json

Local OpenAPI spec path

For production, explicitly set a random JWT_SIGNING_KEY and use an HTTPS BASE_URL. Do not commit .env or any client secret to Git.

Custom token issuer

If the token's iss is not the issuer returned by OIDC discovery, but has been rewritten by the auth middleware to a tenant address, for example:

实际 token iss:
https://api-authsome-qa.drillinsight.com/auth-middleware/t_adecdb63-afab-4346-a1aa-b50bbbae7aee/

Set:

OIDC_TOKEN_ISSUER=https://api-authsome-qa.drillinsight.com/auth-middleware/t_adecdb63-afab-4346-a1aa-b50bbbae7aee/
OIDC_JWKS_URI=https://auth-qa.drillinsight.com/oauth/jwks

OIDC_CONFIG_URL is still used for OAuth login and authorization endpoint discovery; OIDC_TOKEN_ISSUER is used only for validating the iss of the JWT access token. The two may differ. OIDC_TOKEN_ISSUER must exactly match the iss in the token, including the trailing /.

The current project does not validate the scope or scp claim of access tokens, because legacy MGT tokens use a non-standard scope format. OIDC_REQUIRED_SCOPES is still used for OAuth authorization requests, but it will not block valid tokens that lack the standard scope claim. Signature, issuer, audience, expiration time, and JWKS validation remain in place.

QA MGT OpenAPI integration

The QA OpenAPI spec is pinned and saved to:

specs/mgt-qa-openapi.json

The MCP does not access the online /api-docs at runtime, so keeping API documentation closed in future production environments will not affect operation. Simply switch the actual API address via MGT_API_BASE_URL. The Docker image copies specs/mgt-qa-openapi.json to /app/specs/mgt-qa-openapi.json and automatically sets MGT_OPENAPI_SPEC_PATH.

API scope exposed in the first version:

/api/v1/user/current
/course/list
/batch/list
/batch/trainee/list
/equity/userequity/give
/api/v1/order/**
/api/v1/item/**
/api/v1/open/getSku*
/api/v1/customers
/api/v1/campus-recruitment/**(排除 export)
/api/v1/recruitment-info/**(排除 export)

The order payment link endpoint is integrated according to current requirements:

/api/v1/order/queryPayLink
/api/v1/order/reGenaratePayLink

Refund, payment callback, and customer data export endpoints remain excluded:

/mall/v1/order/refund
/alipay/**
/stripe/**
/weixin/refund/**
/api/v1/customers/export
/api/v1/campus-recruitment/export
/api/v1/recruitment-info/export

On each MGT call, the OpenAPI client obtains the user's upstream OAuth access token from the current FastMCP request and sends:

Authorization: Bearer <user access token>
X-Application-Id: <token.app_id>

Among these, X-Application-Id requires no additional configuration; it is read directly from the app_id claim of the verified JWT. Tokens without app_id are rejected, preventing incomplete requests from being sent to MGT.

Therefore, MGT must trust user tokens issued by auth-qa.drillinsight.com and enforce access control based on the user identity.

Troubleshooting ChatGPT CIMD timeouts

If the logs contain:

CIMD fetch failed for https://chatgpt.com/.../client.json: Timeout fetching
Unregistered client_id=https://chatgpt.com/.../client.json

This means FastMCP cannot directly access the client metadata hosted by ChatGPT. If the current machine must access the internet through a trusted outbound proxy, configure:

FASTMCP_SSRF_TRUST_PROXY=true
HTTPS_PROXY=http://127.0.0.1:7897

Then completely stop and restart the service. The program automatically loads .env from the project root before importing FastMCP; by default, FastMCP performs DNS validation and IP pinning on CIMD/JWKS requests, so it does not automatically use ordinary proxy environment variables. When this option is enabled, SSRF protection responsibility is delegated to the specified proxy, and NO_PROXY is ignored. Enable it only for trusted proxies.

The first POST /mcp 401 in the logs is the client probing a protected resource before authentication; 404s for the various /.well-known/... addresses are also ChatGPT compatibility probes. As long as /.well-known/oauth-authorization-server returns 200, these are not the cause of the current failure.

If the logs show Unregistered client_id=app_... or another non-URL client ID, it means ChatGPT has cached an old DCR registration that has been lost from the server-side storage. Pin JWT_SIGNING_KEY, restart the service, and then delete and recreate the custom MCP in ChatGPT so that it calls /register again. Simply retrying the login will not restore an old client ID that is unknown to the server.

Testing

uv run pytest
-
license - not tested
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • MCP server for AI access to Swagger by SmartBear.

  • Read-only MCP server for ClassQuill, a tutoring-business-management platform.

  • MCP Server for agents to onboard, pay, and provision services autonomously with InFlow

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/beaconfire-projects/mcp-oauth-test'

If you have feedback or need assistance with the MCP directory API, please join our Discord server