-
Notifications
You must be signed in to change notification settings - Fork 1
Remote Hosts (SSH Port Forwarding)
You can run the upstream MCP server (or the proxy itself) on a remote machine and still complete the interactive OAuth login from your local browser. The trick is SSH port forwarding plus the fact that the proxy prints the authorize URL to stderr.
The interactive authorization_code flow needs two things to reach your laptop:
- Your browser must open the IdP authorize URL.
- The IdP must redirect back to the proxy's local callback listener
(
127.0.0.1:53682by default).
When the proxy runs on a remote host, neither happens automatically. Port forwarding bridges the callback, and copy-pasting the printed URL bridges the browser.
If only the upstream MCP server is remote, the simplest approach is to run the proxy on your laptop and forward a local port to the remote upstream:
# Forward local 8443 -> remote upstream on the bastion/VM
ssh -L 8443:mcp.internal:443 user@bastionThen point the proxy at the loopback end of the tunnel:
UPSTREAM_URL=https://127.0.0.1:8443/mcp \
OAUTH2_GRANT=authorization_code \
OAUTH2_CLIENT_ID=<your-client-id> \
npx -y mcp-oauth2-proxyBecause 127.0.0.1 is loopback, the cleartext/secure checks are satisfied even
if you terminate TLS at the tunnel. The browser and callback both stay on your
laptop — no extra forwarding needed.
If the proxy must run remotely (e.g. it's launched by a remote MCP client), forward the callback port back to your laptop and open the URL manually.
-
Open a reverse-friendly tunnel for the callback. Forward your local
53682to the remote host's53682:ssh -L 53682:127.0.0.1:53682 user@remote-host
This makes the remote callback listener reachable from your local browser at
http://127.0.0.1:53682/callback. -
Launch the proxy on the remote host as usual.
-
Open the printed URL locally. The proxy writes the authorize URL to stderr (it can't open a browser on a headless box). Copy it from the logs and paste it into your local browser. After you authenticate, the IdP redirects to
http://127.0.0.1:53682/callback, which the tunnel delivers to the remote listener.
-
Redirect URI registration. Whatever host/port the listener uses, the
resulting
http://127.0.0.1:53682/callback(or your override) must be registered as a redirect URI with the IdP. The loopback address is what the browser hits, so register the loopback form. -
Port collisions. If
53682is taken, setOAUTH2_CALLBACK_PORTand forward that port instead. Keep both sides of the tunnel in sync. -
Keep the callback on loopback. Don't set
callbackHostto0.0.0.0to "make it reachable" — the proxy hardens the listener against non-loopbackHostheaders and will warn you. Use SSH forwarding instead. See Security. -
Windows. Use the OpenSSH client (
ssh -L …) the same way; or run the proxy locally with a tunnel to the upstream as above. -
Docker. Publish/forward the callback port out of the container
(
-p 53682:53682) and bind the listener to127.0.0.1inside the container; reach it via the host. -
Bastion / jump host. Chain hops with
ssh -J bastion user@targetand add the-Lforward for the callback port on the final connection.
For the listener's security checks, see Security; for the flow itself, see OAuth2 Grants and Tokens.
GitHub repo · npm package · Licensed under MIT
Overview
Guides
- Getting Started
- Configuration
- OAuth2 Grants and Tokens
- Discovery
- Security
- Remote Hosts (SSH Port Forwarding)
- Troubleshooting
Internals