Spaces:
Running
Running
| title: mcp-bridge | |
| sdk: docker | |
| app_port: 7860 | |
| # mcp-bridge | |
| Authenticated MCP-over-SSE bridge for Hugging Face Spaces, with OAuth support for ChatGPT remote MCP clients. | |
| Public endpoints are intentionally quiet: | |
| ```json | |
| {"ok": true} | |
| ``` | |
| MCP endpoint: | |
| ```txt | |
| https://patdev-mcp-bridge.hf.space/sse | |
| ``` | |
| ## Required Space secrets / variables | |
| Secret: | |
| ```txt | |
| MCP_API_KEY=<your-long-random-access-key> | |
| ``` | |
| Recommended variable: | |
| ```txt | |
| PUBLIC_BASE_URL=https://patdev-mcp-bridge.hf.space | |
| ``` | |
| Optional secret to invalidate OAuth tokens independently from `MCP_API_KEY`: | |
| ```txt | |
| MCP_OAUTH_SIGNING_KEY=<another-long-random-secret> | |
| ``` | |
| ## ChatGPT remote MCP | |
| Use this URL in ChatGPT: | |
| ```txt | |
| https://patdev-mcp-bridge.hf.space/sse | |
| ``` | |
| If ChatGPT can use Dynamic Client Registration, it should discover OAuth automatically. During the OAuth authorization page, enter your `MCP_API_KEY` once. | |
| If ChatGPT says DCR/CIMD is unavailable, use manual OAuth: | |
| ```txt | |
| OAuth client ID: chatgpt | |
| OAuth client secret: leave empty | |
| Token endpoint auth method: none | |
| Scopes: mcp | |
| ``` | |
| No callback URL needs to be pasted by default. The bridge accepts ChatGPT callback URLs matching: | |
| ```txt | |
| https://chatgpt.com/connector/oauth/* | |
| ``` | |
| Sync with: | |
| ```powershell | |
| python .\scripts\sync_hf.py --space-id patdev/mcp-bridge --api-key "YOUR_LONG_RANDOM_KEY" --restart | |
| ``` | |
| Using the PowerShell wrapper: | |
| ```powershell | |
| powershell -ExecutionPolicy Bypass -File .\scripts\sync_hf.ps1 -ApiKey "YOUR_LONG_RANDOM_KEY" -Restart | |
| ``` | |
| For stricter security, you may still force an exact redirect allowlist by setting: | |
| ```powershell | |
| python .\scripts\sync_hf.py --space-id patdev/mcp-bridge --api-key "YOUR_LONG_RANDOM_KEY" --chatgpt-callback-url "EXACT_CHATGPT_CALLBACK_URL" --restart | |
| ``` | |
| ## OAuth endpoints included | |
| ```txt | |
| /.well-known/oauth-protected-resource | |
| /.well-known/oauth-authorization-server | |
| /oauth/register | |
| /oauth/authorize | |
| /oauth/token | |
| /oauth/revoke | |
| /oauth/jwks | |
| ``` | |
| ## Manual auth still works | |
| For non-ChatGPT clients, these still work: | |
| ```txt | |
| Authorization: Bearer <MCP_API_KEY> | |
| ``` | |
| or: | |
| ```txt | |
| x-api-key: <MCP_API_KEY> | |
| ``` | |
| ## Generate a key | |
| ```powershell | |
| python -c "import secrets; print(secrets.token_urlsafe(48))" | |
| ``` | |
| ## Notes | |
| - `/` and `/health` reveal only `{"ok": true}`. | |
| - OAuth client registrations and pending authorization codes are in memory. | |
| - Manual ChatGPT OAuth accepts `https://chatgpt.com/connector/oauth/*` unless `OAUTH_ALLOWED_REDIRECT_URIS` is set. | |
| - OAuth access/refresh tokens are stateless signed tokens. | |
| - Rotate `MCP_OAUTH_SIGNING_KEY` or `MCP_API_KEY` to invalidate all OAuth tokens. | |
| - `mcp_connect` expects an HTTP(S) MCP URL such as `https://example.com/sse` or `http://127.0.0.1:4000/mcp`; bare remote hosts are normalized to `https://...`, while `localhost`/`127.0.0.1` targets are normalized to `http://...`. | |
| - `mcp_connect` now accepts optional `bearer_token` and `x_api_key` arguments for protected upstream MCP servers and verifies the remote endpoint by default during connect. | |
| - If you do not want to pass remote credentials on each call, set `REMOTE_MCP_BEARER_TOKEN` or `REMOTE_MCP_X_API_KEY` in the bridge environment and they will be applied automatically when explicit auth headers are absent. | |
| - FastMCP tool results are serialized as text-first when a tool returns mixed text plus image content, so the bridge emits image-only outputs whenever the upstream result includes images. This preserves screenshot rendering in hosts that otherwise hide the image behind the leading text item. | |
| - For Windows-MCP specifically, `Snapshot` only includes an actual screenshot when called with `use_vision=true`; plain `Snapshot` calls can legitimately return text-only desktop state. | |
| - Bridge sessions created by `mcp_connect` are in memory and reset when the Space restarts or sleeps. | |