Token2OAuth
Click on "Deploy 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., "@Token2OAuthadd a new bearer token to the credential pool"
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.
Token2OAuth
Token2OAuth is a self-hosted OAuth facade + smart credential pool + reverse proxy for remote MCP servers that normally expect a static bearer token.
ChatGPT connects to one Token2OAuth MCP URL using OAuth. Token2OAuth keeps upstream provider credentials encrypted on the host, chooses a healthy credential for each new MCP session, and forwards the request to the real upstream MCP server.
It was designed for the common case where you own several legitimate provider accounts or keys with separate quotas and do not want to configure one ChatGPT connector per account.
Use Token2OAuth only with accounts and credentials you are authorized to use, and in accordance with the upstream service's terms and limits.
Architecture
OAuth authorization code + PKCE
┌─────────────┐ ┌──────────────────────────────┐
│ ChatGPT Web │────────────▶│ Token2OAuth │
│ │◀────────────│ OAuth AS + MCP resource │
└─────────────┘ │ │
│ adaptive credential pool │
│ A healthy │
│ B cooldown (429) │
│ C healthy │
│ D exhausted │
│ E healthy │
└──────────────┬───────────────┘
│ Bearer upstream-token
▼
┌──────────────────────┐
│ Upstream MCP server │
└──────────────────────┘Highlights
One ChatGPT connection, many upstream credentials
OAuth authorization code flow with PKCE S256
Protected-resource and authorization-server metadata
Dynamic Client Registration (DCR)
Resource-bound short-lived gateway access tokens
Rotating refresh tokens
AES-256-GCM encryption for upstream bearer credentials at rest
No upstream bearer token is returned to ChatGPT or rendered back into the admin UI
Streamable HTTP/SSE-friendly MCP proxy
MCP session affinity using Mcp-Session-Id
Smart 401 / 402 / 429 / 5xx handling and Retry-After support
Configurable quota/error body matching and cooldown recovery
Six pool strategies
Polished browser admin console
Headless CLI
Idempotent installer
Tailscale detection and optional automatic installation
Tailscale path mounting without resetting existing routes
systemd user service, Docker, tests, and GitHub Actions
Related MCP server: MCP Credentials Broker
Quick install
curl -fsSL https://raw.githubusercontent.com/p5n-n3t/token2oauth/main/install.sh | bashThe installer detects an existing Tailscale installation. If Tailscale is missing, it can install the official client. It does not reset existing Serve/Funnel routes.
Item | Default |
Local listen | 127.0.0.1:2030 |
Public path | /token2oauth |
Exposure | Tailscale Funnel |
State | ~/.config/token2oauth |
Program | ~/.local/share/token2oauth |
CLI | ~/.local/bin/token2oauth |
An existing root service can coexist with Token2OAuth:
https://my-host.my-tailnet.ts.net/
├── / → existing service
└── /token2oauth → Token2OAuth :2030Tailscale supports path-specific mounts with --set-path, so Token2OAuth does not have to replace the root Funnel.
Installer switches
--prefix PATH
--port N
--path PATH
--mode funnel|serve|none
--public-base-url URL
--upstream-url URL
--branch NAME
--repo URL
--no-tailscale
--no-service
--force-root
--yes
--dry-runExamples:
./install.sh --port 2030 --path /token2oauth
./install.sh --mode serve --path /token2oauth
./install.sh --mode none \
--public-base-url https://mcp.example.com/token2oauth
./install.sh \
--upstream-url https://provider.example.com/mcpFirst run
1. Open the admin console
The installer prints the Gateway, MCP, and Admin URLs. On first initialization it also generates an admin password. Only its scrypt hash is persisted.
2. Configure the real upstream MCP URL
Use the web Admin console, or:
token2oauth config set upstreamUrl https://provider.example.com/mcp3. Add your authorized provider accounts
token2oauth account add --label "Pro account 1"
token2oauth account add --label "Pro account 2" --weight 1.5
token2oauth account add --label "Pro account 3" --priority 50The interactive CLI disables terminal echo while the token is entered.
For automation:
printf '%s' "$PROVIDER_TOKEN" |
token2oauth account add --label "CI account" --token-stdin
token2oauth account add \
--label "Environment account" \
--token-env PROVIDER_TOKEN4. Add one URL to ChatGPT
Add only:
https://your-node.your-tailnet.ts.net/token2oauth/mcpToken2OAuth advertises OAuth metadata. ChatGPT performs OAuth with Token2OAuth; upstream bearer tokens stay on your host.
See ChatGPT OAuth.
Smart pooling
The default strategy is adaptive-sticky.
For a new MCP session, Token2OAuth favors an enabled credential with low normalized usage, low error rate, no cooldown, and low current concurrency. Once the upstream establishes an MCP session, Token2OAuth pins that session to the same upstream credential.
This distributes load without randomly switching accounts in the middle of a stateful MCP session.
Signal | Default action |
2xx | mark healthy and clear failure streak |
401 | mark credential auth-failed |
402 | mark exhausted and cool down |
429 | honor Retry-After, cool down, choose another |
quota/credit exhaustion text | cool down even if provider uses another status |
5xx, timeout, network failure | short cooldown and failover |
request already has Mcp-Session-Id | no cross-account failover by default |
There is intentionally no fake "remaining credits" counter. If a provider publishes a real usage endpoint, a provider adapter can add proactive checks. Otherwise provider responses are more reliable than guessing.
See Pooling and failover.
Pool strategies
token2oauth pool strategy adaptive-sticky
token2oauth pool strategy round-robin
token2oauth pool strategy least-used
token2oauth pool strategy weighted-random
token2oauth pool strategy random
token2oauth pool strategy priorityadaptive-sticky — recommended; affinity + usage/error/concurrency score
round-robin — deterministic rotation among eligible accounts
least-used — request count normalized by weight
weighted-random — probabilistic distribution using account weights
random — uniform random eligible credential
priority — lowest numeric priority first
CLI
token2oauth init
token2oauth serve
token2oauth account add
token2oauth account list
token2oauth account enable ID
token2oauth account disable ID
token2oauth account remove ID
token2oauth account reset-health [ID]
token2oauth pool status
token2oauth pool strategy STRATEGY
token2oauth config list
token2oauth config get KEY
token2oauth config set KEY VALUE
token2oauth oauth clients
token2oauth oauth revoke-client CLIENT_ID
token2oauth admin reset-password
token2oauth tailscale status
token2oauth tailscale expose --mode funnel --path /token2oauth --port 2030
token2oauth doctorEvery command supports --help through Commander.
LightSprint
Token2OAuth is provider-agnostic. For LightSprint or another service, configure the actual remote MCP endpoint that accepts the account's bearer credential, then add each authorized account token to the pool.
Example labels:
LightSprint Pro #1
LightSprint Pro #2
LightSprint Pro #3
LightSprint Pro #4
LightSprint Pro #5The public LightSprint CLI/plugin demonstrates Bearer-token API calls, token refresh and explicit handling of HTTP 429. Token2OAuth therefore treats 429/Retry-After as a strong cooldown signal by default, while also supporting configurable credit/quota patterns. It does not assume an undocumented balance endpoint.
Tailscale
Token2OAuth never calls tailscale funnel reset or tailscale serve reset.
The generated route is equivalent to:
tailscale funnel --bg \
--https=443 \
--set-path=/token2oauth \
http://127.0.0.1:2030See Tailscale deployment.
Docker
Tailscale normally stays on the host:
docker compose up -d --build
tailscale funnel --bg --https=443 --set-path=/token2oauth http://127.0.0.1:2030Persist the data volume. It contains encrypted account envelopes, OAuth client state, and the local master key.
Security
Upstream tokens are encrypted with AES-256-GCM using a random 256-bit key stored separately in ~/.config/token2oauth/master.key. The admin UI never reveals a stored bearer token. Gateway OAuth access tokens are short-lived and audience-bound; refresh tokens are stored as hashes and rotated.
Read SECURITY.md before putting the gateway on a public hostname.
Development
git clone https://github.com/p5n-n3t/token2oauth.git
cd token2oauth
npm install
npm test
npm run devThe test suite includes RFC 7636 PKCE verification, encryption round-trip, failure classification, a full DCR/authorization/token flow, live 429→healthy credential failover, and refresh-token rotation.
Documentation
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Connect MCP clients to 2,000+ AI models without managing provider API keys.
Governed MCP gateway: one endpoint for your tools, with credential custody and audit log.
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
- 0bridgeOAuthdev.0bridge
Every service you connect, one MCP endpoint for all your AI tools. Sign in once.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA transparent proxy server that simplifies authentication by chaining its own OAuth layer with an upstream MCP server's credentials. It manages dual token sets behind a single interface, enabling secure and streamlined access to protected MCP resources.MIT
- AlicenseAqualityFmaintenanceProvides secure OAuth2-based credential management for MCP servers, allowing agents to obtain short-lived token references without exposing raw secrets.78 npmMIT
- AlicenseNot gradedqualityDmaintenanceActs as a secure OAuth 2.0/2.1 proxy gateway for MCP servers, enabling integration with Claude and ChatGPT platforms.32 npmMIT
- AlicenseNot gradedqualityAmaintenanceA client-side MCP proxy that injects OAuth 2.0 bearer tokens or API keys into MCP requests, enabling MCP clients to connect to OAuth/API-key-protected MCP servers like Amazon Bedrock AgentCore Gateway. It automatically fetches and refreshes credentials using AgentCore Identity or static values.MIT No Attribution