mcp-client-credentials-auth
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-client-credentials-authList all available tools"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-client-credentials-auth
This app acts as an MCP server that authenticates to remote OAuth-protected MCP servers using the client_credentials grant (MCP OAuth Client Credentials extension, client secrets variant). Throughout this document, we refer to it as the auth proxy.
Drop it into any MCP client configuration for autonomous agents, background services, CI/CD, and other machine-to-machine use (no user in the loop). It acquires access tokens and forwards MCP traffic transparently. By default the token endpoint and scopes are auto-discovered via MCP Authorization and RFC 9728 / RFC 8414. When discovery is unavailable, set a manual token endpoint; see Servers without OAuth discovery. For a shared on-premises deployment instead of local stdio, see On-premises HTTP deployment.
To obtain client_id and client_secret, look for a "Service Account", "API Key", or "Machine-to-Machine Application" option in the remote MCP service's account or developer settings. Naming varies by provider. Well-behaved services issue credentials that already work with their MCP Server published discovery metadata (or provide instructions how to self-grant them), so you normally need only the remote URL plus those two values (see Notes for MCP Server Developers).
How It Works
MCP Client ←→ [stdio] ←→ mcp-client-credentials-auth ←→ [HTTP/SSE + Bearer] ←→ Remote MCP Server
↓
IdP Token Endpoint
(client_credentials grant)Resolves the IdP token endpoint and scopes via MCP Authorization discovery, or uses
MCP_CC_PROXY_TOKEN_ENDPOINT/MCP_CC_PROXY_SCOPESwhen configuredAcquires tokens with OAuth
client_credentialsForwards all MCP methods bidirectionally with
BearerauthenticationRefreshes tokens proactively and retries on 401
On Streamable HTTP, if the remote returns
403withinsufficient_scope, can expand scopes, get a new token, and retry once (see Known Issues)
Also: Streamable HTTP with SSE fallback for transport failures only; fail-closed startup (local transport binds only after a usable token when auth is required and the remote is reachable); automatic reconnection and stale Streamable HTTP session recovery; capability polling; identity/capability forwarding; timeouts on all outgoing calls; generic pass-through (no hardcoded method tables). See When the proxy starts or stays up.
Related MCP server: MCP Server OAuth Toy
Quick Start
{
"mcpServers": {
"my-remote-server": {
"command": "npx",
"args": ["-y", "mcp-client-credentials-auth"],
"env": {
"MCP_CC_PROXY_REMOTE_MCP_URL": "https://mcp.example.com/mcp",
"MCP_CC_PROXY_CLIENT_ID": "my-service",
"MCP_CC_PROXY_CLIENT_SECRET": "s3cr3t"
}
}
}
}Security note: npx downloads and runs the published mcp-client-credentials-auth package on your machine. Use trusted packages only. If discovery is unavailable, see Servers without OAuth discovery.
Configuration
All configuration via MCP_CC_PROXY_* environment variables:
Variable | Required | Default | Description |
| Yes | — | Remote MCP server URL ( |
| Yes | — | OAuth client_id |
| Yes | — | OAuth client_secret |
| No | — | IdP token endpoint URL. When set, skips MCP Authorization discovery (PRM/AS) and posts tokens here. See Servers without OAuth discovery. |
| No | auto-discovered | Space-separated scopes for the token request. Overrides discovered |
| No |
| Enable debug logging to stderr |
| No |
| Proactive token refresh window (seconds before expiry) |
| No |
| Timeout for MCP requests, OAuth discovery, and token acquisition (ms) |
| No |
| Shared wall-clock budget for a usable access token (when auth is required) and a reachable remote MCP before exit |
| No |
| Interval to poll remote capability changes (0 = disabled) |
| No |
| Local transport: |
| No |
| HTTP bind host (HTTP mode only; container image defaults to |
| No |
| HTTP bind port (HTTP mode only) |
| No |
| MCP Streamable HTTP mount path (HTTP mode only) |
| No |
| HTTP mode: close sessions with no inbound MCP traffic for this long ( |
| No |
| Emit per-call usage audit lines to stderr. See Call audit log. |
| No |
| Interval to re-fetch OAuth PRM/AS metadata (both transports). |
Servers without OAuth discovery
Use when the remote MCP server accepts Bearer tokens but MCP Authorization discovery (RFC 9728 / RFC 8414) is not available. The proxy still uses client_credentials; take the token endpoint and scopes from the MCP server provider (do not invent them). Prefer auto-discovery when available. If MCP_CC_PROXY_TOKEN_ENDPOINT is set, the manual path is used even when discovery would have worked (no PRM/AS fetch at startup or at runtime).
Required: MCP_CC_PROXY_REMOTE_MCP_URL, MCP_CC_PROXY_CLIENT_ID, MCP_CC_PROXY_CLIENT_SECRET, MCP_CC_PROXY_TOKEN_ENDPOINT.
Usually also: MCP_CC_PROXY_SCOPES (there is no discovered scopes_supported in this mode).
Prefer https:// for the token endpoint; cleartext HTTP to a non-loopback host logs a warning (client secret would travel unencrypted). Startup still requires a usable token and a reachable remote within MCP_CC_PROXY_STARTUP_TIMEOUT_MS.
{
"mcpServers": {
"my-remote-server": {
"command": "npx",
"args": ["-y", "mcp-client-credentials-auth"],
"env": {
"MCP_CC_PROXY_REMOTE_MCP_URL": "https://mcp.example.com/mcp",
"MCP_CC_PROXY_CLIENT_ID": "my-service",
"MCP_CC_PROXY_CLIENT_SECRET": "s3cr3t",
"MCP_CC_PROXY_TOKEN_ENDPOINT": "https://auth.example.com/oauth/token",
"MCP_CC_PROXY_SCOPES": "inventory.read orders.write"
}
}
}
}On-premises HTTP deployment (alternative)
Use this when several MCP clients on a private network should share one outbound client_credentials identity, and you do not want to deploy this proxy on every client machine. Prefer local stdio (Quick Start) whenever a single client can spawn the proxy; HTTP adds a shared network attack surface.
Set MCP_CC_PROXY_TRANSPORT=http. The process listens for cleartext Streamable HTTP on MCP_CC_PROXY_LISTEN_HOST / PORT / PATH (defaults: 127.0.0.1:8080/mcp). There is no inbound authentication on the proxy: anyone who can open an MCP session gets the full remote service-account privilege.
Sessions with no inbound MCP traffic for MCP_CC_PROXY_HTTP_SESSION_IDLE_SECONDS (default 30 minutes; 0 disables) are closed and removed. That cleans up clients that abandon a session without DELETE (for example some MCP reload/logout paths). A later request that still sends the old mcp-session-id receives 404 / Session not found; the proxy does not silently attach that id to a new session. Spec-compliant Streamable HTTP clients re-initialize without a session id and continue on a new session.
Recommended HTTPS deployment
Terminate TLS and client authentication at a reverse proxy or ingress (nginx, Envoy, OpenShift Route, etc.). Clients use https://mcp-proxy.example.com/mcp; the reverse proxy forwards to http://127.0.0.1:8080/mcp or the container service on a private network. This process does not terminate TLS. Do not publish :8080 to untrusted networks; plain http:// to a non-loopback host is not a production layout. HTTPS alone is not who-may-call; use mTLS, OAuth at ingress, or IP allowlists in practice. An MCP gateway can fill this same edge role (TLS + inbound auth) instead of a generic reverse proxy.
MCP Client ←→ [HTTPS + edge auth] ←→ Reverse proxy ←→ [HTTP private] ←→ mcp-client-credentials-auth ←→ [HTTPS + Bearer] ←→ Remote MCP Server
↓
IdP Token Endpoint
(client_credentials grant)How to run
Both options need the same OAuth env vars (MCP_CC_PROXY_REMOTE_MCP_URL, MCP_CC_PROXY_CLIENT_ID, MCP_CC_PROXY_CLIENT_SECRET, plus optional discovery overrides).
A. Node / npm (no image)
From source (clone this repo, then build and run locally):
git clone https://github.com/velias/mcp-client-credentials-auth.git
cd mcp-client-credentials-auth
npm ci && npm run build
export MCP_CC_PROXY_REMOTE_MCP_URL=https://mcp.example.com/mcp
export MCP_CC_PROXY_CLIENT_ID=my-service
export MCP_CC_PROXY_CLIENT_SECRET=s3cr3t
# optional: MCP_CC_PROXY_LISTEN_HOST / PORT / PATH (defaults: 127.0.0.1:8080/mcp)
npm run start:httpFrom the npm registry (no clone; runs the published mcp-client-credentials-auth package):
export MCP_CC_PROXY_TRANSPORT=http
export MCP_CC_PROXY_REMOTE_MCP_URL=https://mcp.example.com/mcp
export MCP_CC_PROXY_CLIENT_ID=my-service
export MCP_CC_PROXY_CLIENT_SECRET=s3cr3t
npx -y mcp-client-credentials-authB. Container image (GHCR)
Prerequisites: Docker or Podman. Podman is used in examples; you can use the docker command instead.
Image: ghcr.io/velias/mcp-client-credentials-auth with tags X.Y.Z, X.Y, X, and latest. Defaults: TRANSPORT=http, LISTEN_HOST=0.0.0.0, port 8080.
podman run --name mcp-cc-proxy --rm \
-p 127.0.0.1:8080:8080 \
-e MCP_CC_PROXY_REMOTE_MCP_URL=https://mcp.example.com/mcp \
-e MCP_CC_PROXY_CLIENT_ID=my-service \
-e MCP_CC_PROXY_CLIENT_SECRET=s3cr3t \
ghcr.io/velias/mcp-client-credentials-auth:latestOr pass --env-file for credentials.
Health probes
HTTP mode exposes unauthenticated Kubernetes-style probes (process up only; they do not check the IdP, remote MCP, or token usability). Restrict at ingress if you do not want them reachable beyond the private network.
Path | Use | Response |
| Liveness |
|
| Readiness |
|
Security considerations
Beyond the HTTPS layout and health probes above:
Reachability is authorization — anyone who can open an MCP session (including via a mis-published
:8080or the container's0.0.0.0bind) gets the full service-account privilege. Keep the listener on a private network only.One shared machine identity — all sessions share the same Bearer token and scope set (including step-up). Session IDs are capability-like on the private hop. Not multi-tenant; use separate deployments per trust boundary.
Credentials and logs — env-held secrets are a shared blast radius; prefer vault/
--env-file(see Security) and rotate on compromise. Protect log sinks (URLs/client names; secrets are redacted). When call audit is on,resources/readlogs the fulluri(including embedded credentials in query/userinfo if present). Keep images and the pinned base digest updated.
Using an MCP gateway (optional hardening)
A generic MCP gateway can address several weak points above (inbound OAuth/OIDC, RBAC/tool ACL, rate limits, audit) and, when it terminates TLS for clients, can replace the separate reverse proxy in Recommended HTTPS deployment. It typically does not replace this proxy’s outbound client_credentials role.
MCP Client ←→ [user/agent identity] ←→ MCP gateway ←→ [trusted private MCP] ←→ mcp-client-credentials-auth ←→ [HTTPS + Bearer] ←→ Remote MCP Server
↓
IdP Token Endpoint
(client_credentials grant)Gateway: inbound auth, who-may-call / tool policy, multi-tenant edge isolation, audit/rate limits, TLS for clients
This proxy: machine-token acquire/refresh on the private side
Keep proxy and secrets reachable only from the gateway (or an equivalent trusted path)
Security
Also see On-premises HTTP deployment when exposing the proxy over the network.
Protecting Secrets with Vaults
The Quick Start example puts secrets directly in the MCP client config (typically plain text on disk). Prefer fetching them at launch from a vault or keychain.
1Password CLI
Create a 1Password vault item (e.g. named "My MCP Credentials") with fields client-id and client-secret, then use op run with a template file that contains op:// references to those fields:
# ~/.config/mcp/my-server.env.tpl
MCP_CC_PROXY_REMOTE_MCP_URL=https://mcp.example.com/mcp
# op://<vault>/<item>/<field> - references to 1Password vault item fields
MCP_CC_PROXY_CLIENT_ID=op://Private/My MCP Credentials/client-id
MCP_CC_PROXY_CLIENT_SECRET=op://Private/My MCP Credentials/client-secret{
"mcpServers": {
"my-remote-server": {
"command": "op",
"args": ["run", "--env-file=~/.config/mcp/my-server.env.tpl", "--", "npx", "-y", "mcp-client-credentials-auth"]
}
}
}op run resolves the op:// references at launch, injects the real values as environment variables, and executes the proxy. Secrets never touch the file system.
Bitwarden CLI
Create a Bitwarden vault login item (e.g. named "My MCP Credentials") and store the client_id as the username and client_secret as the password. Then use a wrapper script that fetches them via bw get:
#!/usr/bin/env bash
export MCP_CC_PROXY_REMOTE_MCP_URL="https://mcp.example.com/mcp"
# "My MCP Credentials" is the name of the Bitwarden vault login item
export MCP_CC_PROXY_CLIENT_ID="$(bw get username 'My MCP Credentials')"
export MCP_CC_PROXY_CLIENT_SECRET="$(bw get password 'My MCP Credentials')"
exec npx -y mcp-client-credentials-authSave the script (e.g. ~/.local/bin/mcp-my-server.sh), make it executable, and point your MCP config at it:
{
"mcpServers": {
"my-remote-server": {
"command": "/Users/you/.local/bin/mcp-my-server.sh"
}
}
}Bitwarden must be unlocked (bw unlock) before the MCP client starts the proxy.
macOS Keychain
Store the client secret in the built-in macOS Keychain. The -s flag is the service name (a label you choose to identify this entry), -a is the account name, and -w is the secret value:
security add-generic-password -s "My MCP Credentials" -a "client_secret" -w "my-client-secret"Then use a wrapper script to read it at launch:
#!/usr/bin/env bash
export MCP_CC_PROXY_REMOTE_MCP_URL="https://mcp.example.com/mcp"
# The OAuth client ID assigned by your identity provider
export MCP_CC_PROXY_CLIENT_ID="my-oauth-client-id"
# -s and -a must match the values used in add-generic-password above
export MCP_CC_PROXY_CLIENT_SECRET="$(security find-generic-password -s 'My MCP Credentials' -a 'client_secret' -w)"
exec npx -y mcp-client-credentials-authNo extra software required; the Keychain is built into macOS and protected by your login password or Touch ID.
Runtime Credential Safeguards
Access token is stored only in memory, never logged
Client secrets loaded at startup, never forwarded or logged
Authorization header always set by the auth proxy, never influenced by MCP client content
Auth-like metadata keys (
authorization,token,bearer,access_token,client_secret) stripped from_metain all client-to-server messages before forwarding
Troubleshooting
All auth proxy logs go to stderr (stdout is reserved for MCP protocol). To see them:
Cursor - MCP server output panel (Developer: Show MCP Logs)
Claude Desktop -
~/Library/Logs/Claude/mcp-server-*.log(macOS) or%APPDATA%\Claude\logs\(Windows)Claude Code - terminal with
--mcp-debug
Set MCP_CC_PROXY_DEBUG=true for verbose OAuth discovery, forwarding, and refresh scheduling details.
Runtime failures use the same three categories in JSON-RPC errors and stderr (category="authentication" etc.), prefixed with mcp-client-credentials-auth:
Category | Meaning | Example |
| Token/IdP failure (no usable token, scopes, client secret, OAuth discovery) |
|
| Proxy cannot reach or stay connected to the remote MCP transport |
|
| Remote MCP server returned a protocol/application error (including resource-server 401/403) |
|
Stderr lines also include component=mcp-client-credentials-auth so they remain attributable inside generic MCP client log panels.
When the proxy starts or stays up
For stdio, MCP clients typically treat the server as healthy when the process is alive and initialize succeeds; they do not probe whether your IdP or remote MCP backend is working. This proxy fail-closes at startup and self-heals at runtime (stdio and HTTP).
Startup (process exits; your MCP client should show the server in error):
Situation | Behavior |
IdP unreachable, OAuth discovery fails, or other transient token prefetch failures within | Retries with backoff inside the shared startup budget, then exits. Check stderr for the real cause. |
Permanent OAuth config rejection from the IdP ( | Immediate exit; no startup retries. Logged as unrecoverable OAuth misconfiguration at the identity provider (IdP) (the IdP rejected the token request, not the MCP server). Typical causes: wrong |
Remote MCP server rejects the access token at startup (401/ | Immediate exit; logged as unrecoverable OAuth misconfiguration at the remote MCP server (token was issued, then rejected by the MCP server). Contact your MCP server provider to correct token validation, scopes/audience, or issued credentials. |
Authorization server metadata does not advertise | Immediate exit; no startup retries. |
Remote server needs no OAuth ( | Token not required; remote MCP must still be reachable within the startup budget or the process exits. |
Remote MCP unreachable within the startup budget | Retries with backoff, then exits. Real remote capabilities are never invented; the local session is not opened. |
Runtime (process stays up; the UI may stay green; requests fail until recovery):
Situation | Behavior |
Remote MCP disconnects | Requests get |
Remote MCP restarted / Streamable HTTP session not found | Detects stale session (HTTP 404 or session-loss message), recreates the remote session, retries the request once; logs session lost and session reacquired on stderr. |
Remote MCP / IdP OAuth metadata or discovered scopes change while running | Re-checked on successful remote reconnect (debounced) and on the rediscovery interval. See OAuth discovery and scopes at runtime. |
IdP down but a cached access token is still valid | Keeps serving until the token is no longer usable. |
No usable access token and refresh/auth fails | Requests get a short |
Client reconnect: for stdio MCP, "reconnect" means the client respawns this process and runs a new initialize. The MCP spec says clients SHOULD restart an unexpectedly exited server; in practice Cursor and many MCP Clients often need a manual toggle or Reload Window after a red startup failure. Mid-session problems show up on tool/resource calls, not necessarily as a red server dot.
Token acquired but remote server returns 403
Without MCP_CC_PROXY_SCOPES, the proxy requests the full discovered scopes_supported string. End users should not need to trim that list: the MCP service that issued your client_id / client_secret should have granted the scopes discovery will request, or provide you instructions how to self-setup them. If that alignment is wrong, contact the MCP server provider (or use a provider-documented MCP_CC_PROXY_SCOPES override). IdP behavior when the requested set is wider than the client grant:
Silent dropping (Keycloak, Auth0): disallowed scopes are removed from the token without error. The proxy starts but may lack permissions; hardest to debug. Runtime
403/insufficient_scopestep-up can request more later if the IdP will grant them.Strict rejection (Okta, AWS Cognito): any disallowed scope fails the token request with
invalid_scope. Startup exits immediately (unrecoverable OAuth misconfiguration at the identity provider). This is a provider/credentials misconfiguration for zero-config use; step-up never runs..defaultconvention (Entra ID / Azure AD): individual scope names are not accepted; providers should documentMCP_CC_PROXY_SCOPES={resource}/.default. See Entra ID (Azure AD) compatibility.
Debugging steps:
Look for the startup scopes log:
OAuth discovery complete(auto-discovery) orUsing manual token endpoint (skipping OAuth discovery)(manual token endpoint). Thescopesfield shows what will be requested, or(default)/(none)if none were set.Request a token directly from your IdP's token endpoint (using
curlor your IdP's admin UI), decode it at jwt.io or withjq, and inspect thescopeorscpclaim to see what was actually granted.Compare with the scopes the remote MCP server requires for the failing operation.
If scopes are missing, contact the MCP server operator (or follow their documented
MCP_CC_PROXY_SCOPES/ consent steps).
OAuth discovery and scopes at runtime
Protected resource metadata (RFC 9728), authorization server metadata (RFC 8414), and scopes for client_credentials are resolved at startup (or taken from MCP_CC_PROXY_SCOPES / MCP_CC_PROXY_TOKEN_ENDPOINT). They are re-checked after a successful remote reconnect and on a schedule (MCP_CC_PROXY_OAUTH_REDISCOVERY_SECONDS, default 1 hour; 0 disables the timer only). Reconnect-triggered rediscovery is debounced (at most once every 5 minutes) so a flapping remote does not spam .well-known endpoints; the scheduled timer always runs on its interval.
On a significant change (authorization server URL, token endpoint, issuer, discovered scopes_supported when MCP_CC_PROXY_SCOPES is unset, or auth mode flip), the proxy drops the cached access token and acquires a new one. Comparison uses PRM/AS discovery snapshots, not live step-up scope unions. Transient .well-known failures (including dual PRM/AS outage, or partial AS failure that would look like no-auth) keep the previous discovery and token when the process was already authenticated; rediscovery does not downgrade away from authenticated mode on those paths. With MCP_CC_PROXY_TOKEN_ENDPOINT, OAuth PRM/AS rediscovery is skipped entirely. MCP_CC_PROXY_SCOPES still wins over discovered scopes; if you rely on that override and scopes must change, update the env and restart.
Notes for MCP Server Developers
Expect most OAuth-protected MCP servers to serve both interactive clients (authorization code + user consent) and M2M clients (this proxy / client_credentials). One Protected Resource Metadata document is shared; the tension is scope policy and IdP grants, not two different MCP protocols.
This proxy targets end users with minimal config: ideally remote URL, client_id, and client_secret only. Design discovery and M2M credential issuance so that path works without MCP_CC_PROXY_SCOPES.
Dual interactive + M2M rules
Publish one PRM with granular, application-level scopes in
scopes_supported. Per MCP Authorization, treat that list as the baseline for basic functionality; optional or higher-privilege scopes can be added later via step-up.
{
"resource": "https://mcp.example.com/mcp",
"authorization_servers": ["https://auth.example.com"],
"scopes_supported": ["inventory.read", "orders.read"]
}Use separate OAuth clients for interactive apps vs service accounts, but the same resource / audience. Interactive consent and M2M client grants are different IdP objects; do not assume a user-consent app registration works as a service account for this proxy.
Align M2M grants with discovery. This proxy (and similar M2M clients) will request the full discovered
scopes_supportedstring at startup unless overridden. When you issue a service account / M2M client (or document self-serve setup), grant it at least that baseline. On strict IdPs, a mismatch means this proxy cannot start; on silent-dropping IdPs it starts with a weak token and fails later. End users should not need to hand-edit scopes for a normal install.Step-up is shared protocol, different UX. Return
403+insufficient_scope+scope=when an operation needs more than the current token (not401/invalid_tokenfor "missing scope"). Interactive clients can re-consent; M2M step-up only succeeds for scopes the service account is already allowed at the IdP. Pre-grant any scopes you expect M2M clients to obtain via step-up, or keep those operations out of the M2M product surface.If
scopes_supportedlists every advanced scope (not only the baseline), M2M credentials must be granted that full set (or a documented tier that matches a published subset). Putting "everything" in PRM is fine for interactive least-privilege-via-consent only if M2M issuance matches; otherwise zero-config M2M breaks on strict IdPs.Document
MCP_CC_PROXY_SCOPESonly as an escape hatch: IdP needs a different M2M scope format (Entra.default), MCP Authorization discovery is unavailable, or you intentionally publish a baseline that M2M clients must override. Do not rely on end users to invent scope lists.
Entra ID (Azure AD) compatibility
This is the common format clash for dual-flow servers: Entra requires {resource}/.default for client_credentials and rejects individual scope names, while interactive clients still want granular scopes in scopes_supported. Document the M2M override in your user setup:
// MCP client config for Entra ID
{
"mcpServers": {
"my-remote-server": {
"command": "npx",
"args": ["-y", "mcp-client-credentials-auth"],
"env": {
"MCP_CC_PROXY_REMOTE_MCP_URL": "https://mcp.example.com/mcp",
"MCP_CC_PROXY_CLIENT_ID": "my-service",
"MCP_CC_PROXY_CLIENT_SECRET": "s3cr3t",
"MCP_CC_PROXY_SCOPES": "api://my-api-client-id/.default"
}
}
}
}Identity and Capabilities Forwarding
During initialize, the auth proxy forwards identity and capabilities in both directions with a two-phase remote connection:
Discovery - connect to the remote to learn its identity/capabilities, then disconnect
Local handshake - present the remote server's identity to your MCP client
Reconnect - reconnect to the remote with your client's real identity (plus a suffix) and capabilities
Your MCP client sees the remote server's real name. The remote sees a name like cursor-vscode via mcp-client-credentials-auth v0.2.0 with the client's version. Local capabilities (sampling, roots, elicitation, etc.) are forwarded so server-to-client features work through the proxy.
The proxy also declares the io.modelcontextprotocol/oauth-client-credentials extension on both connections so the remote can apply machine-to-machine policies.
Call audit log
When MCP_CC_PROXY_AUDIT_CALLS is enabled, the proxy writes one INFO stderr line per audited MCP request. Defaults are off for stdio and on for HTTP; set the env var explicitly to override either default.
Client → server: tools/call, tools/list, resources/read, resources/list, resources/templates/list, prompts/get, prompts/list.
Server → client: sampling/createMessage, elicitation/create.
Each line uses the JSON-RPC method as msg, plus structured meta:
always:
outcome(okorerror),durationMswhen applicable:
tool,uri, orprompt(invoke identity only)HTTP mode:
sessionId(localmcp-session-id)on failure:
error_category(authentication/connection/remote) anderror
Arguments, sampling message contents, and elicitation bodies are never logged. Unrelated protocol methods (initialize, ping, roots/list, etc.) are not audited. Proxy-internal capability polling is not audited.
URI privacy: for resources/read, the audit line records the full client-supplied uri (including query string, userinfo, and fragment). If resource URIs embed credentials (presigned URLs, API keys in query params, etc.), those values reach stderr and any log sink that collects it. Treat audit logs like access logs: restrict retention and who can read them, or disable audit (MCP_CC_PROXY_AUDIT_CALLS=false) when URIs are sensitive. The proxy does not strip or redact URI components.
Filter audit lines with outcome= or an exact method such as msg="tools/call".
Known Issues
Scope step-up and the MCP TypeScript SDK
The MCP TypeScript SDK has known bugs around scope step-up (403 insufficient_scope handling):
Scope overwrite instead of accumulation (typescript-sdk#1582): when multiple operations require different scopes, the transport overwrites the active scope instead of merging, causing infinite re-authorization loops.
fetchTokenignores challenge scope (typescript-sdk#2255): forclient_credentials,fetchToken()usesclientMetadata.scopeinstead of theWWW-Authenticatechallenge scope, so new scopes never reach the token endpoint.
This proxy works around both for Streamable HTTP: on 403 with WWW-Authenticate error="insufficient_scope" and a scope parameter, it merges the challenge into its running scope set, acquires a new token with the full union, retries once, and logs on stderr (Scope step-up challenge received… / Scope step-up token acquired). Later calls that need more scopes expand the same set (when the IdP grants them). If the IdP rejects the stepped-up scopes, the proxy keeps the previous scopes and access token, returns the original 403, and stays usable for other requests.
Step-up is a runtime safety net when discovery (or a documented override) produced a valid starting token and the remote later challenges for more scopes the IdP will grant that client. For dual-flow servers, that means M2M step-up only works inside pre-granted service-account scopes; it is not a substitute for aligning scopes_supported with M2M credential issuance. A strict IdP rejecting the discovered set at startup is a server/provider configuration problem.
Not covered: some servers (including some FastMCP paths) return 401 + invalid_token for missing scopes instead of 403 + insufficient_scope. That cannot be stepped up reliably; restart after required-scope policy changes, or rely on OAuth rediscovery when discovered scopes change and MCP_CC_PROXY_SCOPES is unset.
When both upstream issues are fixed in a released SDK, remove the workaround (see the MCP SDK upgrade checklist in AGENTS.md).
Contributing
See CONTRIBUTING.md for development setup, testing, and PR guidelines. See AGENTS.md for architecture and code conventions.
License
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityBmaintenanceAggregates multiple MCP servers behind a single stdio interface, supporting stdio-based and HTTP SSE-based upstreams with OAuth.Last updated24MIT
- Flicense-qualityDmaintenanceA simple MCP server with OAuth 2.0 authentication for testing OAuth support in mcp-cli.Last updated
- Alicense-qualityDmaintenanceLocal stdio proxy for Uno MCP Gateway that enables MCP clients without OAuth support to securely connect to authenticated remote servers.Last updated1MIT
- Flicense-qualityDmaintenanceBridges stdio-based LLM harnesses to OAuth-protected remote MCP servers via Streamable HTTP, handling PKCE browser login and token refresh automatically.Last updated10
Related MCP Connectors
Streamable HTTP MCP server for Google Calendar and Sheets with OAuth login.
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
MCP server for Argo RPG Platform — connects AI assistants to campaign data via OAuth2
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/velias/mcp-client-credentials-auth'
If you have feedback or need assistance with the MCP directory API, please join our Discord server