Cloudflare Tunnel is the recommended way to make the local MCP server reachable over the internet. Instead of opening a raw port on your router or relying on a dynamic DNS service, Cloudflare Tunnel creates an outbound-only encrypted connection from your machine to Cloudflare’s edge network. TLS termination happens at the Cloudflare edge, so your local server only needs to listen on plain HTTP atDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/XxYouDeaDPunKxX/chatgpt-local-agent-mcp/llms.txt
Use this file to discover all available pages before exploring further.
127.0.0.1:8789. No inbound firewall rules, no raw port forwarding, and no self-signed certificates are required.
The recommended setup is a Cloudflare Tunnel, not a Cloudflare Worker. You do not need to write or deploy a Worker for the normal MCP server configuration.
What you need
- A Cloudflare account
- A domain with DNS managed by Cloudflare
cloudflaredinstalled locally (the tunnel connector daemon)- The local MCP server already passing Path A smoke checks
Steps
Create a Cloudflare Tunnel
Create a named tunnel either through the Cloudflare Zero Trust dashboard or via the This creates a tunnel and writes credentials to
cloudflared CLI.Using the dashboard:Navigate to Zero Trust → Networks → Tunnels, click Create a tunnel, choose Cloudflared, and follow the prompts. Give the tunnel a name — for example, chatgpt-local-agent-mcp — and save the tunnel token or configuration credentials file that Cloudflare provides.Using the CLI:%USERPROFILE%\.cloudflared\<tunnel-id>.json.Create a DNS record pointing to the tunnel
In Cloudflare DNS, create a CNAME record for your MCP hostname that points to the tunnel. The target should be your tunnel’s UUID address in the format:Using the CLI:This creates the proxied CNAME record automatically. The record must be orange-clouded (proxied through Cloudflare) for the tunnel to work.
Configure the cloudflared ingress routes
Create or edit the The final
cloudflared configuration file at %USERPROFILE%\.cloudflared\config.yml. The tunnel must route the MCP hostname to the local server’s bind address.A minimal configuration looks like this:service: http_status:404 catch-all is required by cloudflared — any request that does not match a hostname rule returns a 404 instead of an error.The tunnel route must forward to
http://127.0.0.1:8789 (plain HTTP). Cloudflare handles TLS termination at the edge and sends decrypted traffic to the local connector. Do not point the ingress route at an HTTPS local address unless you have specifically configured the local server to use TLS.Set the matching .env values
Update the MCP server
.env to match the public hostname you configured:PUBLIC_BASE_URLmust match the public HTTPS URL exactly. The server uses this to construct OAuth redirect URLs and the MCP resource URI.CLOUDFLARE_TUNNEL_ENABLED=trueenables the tunnel status panel in the local dashboard and gates theAUTH_REQUIRED=falseguard (the server will refuse to start with auth disabled when the tunnel is enabled).CLOUDFLARED_CONFIGshould be the absolute path to theconfig.ymlfile you created in the previous step.
CLOUDFLARE_TUNNEL_ENABLED=true, the server automatically uses the CF-Connecting-IP header that Cloudflare injects to key rate limiting on the real client IP rather than the tunnel connector address. No additional setting is needed for this.TRUST_PROXY_HEADERS is a separate option. When set to true, the server uses X-Forwarded-For for rate-limit keying — this applies when traffic passes through a different proxy that sets that header, not through Cloudflare Tunnel. For a standard Cloudflare Tunnel setup, leave it at the default:Start cloudflared and verify active replicas
Start the tunnel connector:Or, if you are using the config file:After a few seconds, check the Cloudflare Zero Trust dashboard under Networks → Tunnels. The tunnel should show a green Healthy status with at least one active connector replica. A tunnel that shows Inactive or Down with zero replicas means
cloudflared is not connected, even if the DNS record and ingress configuration are correct.Test the public health endpoint
With both the local MCP server and This should return a successful health response. If it does, Cloudflare Tunnel is routing correctly and the local server is reachable from the public internet. You are ready to proceed with the full OAuth and ChatGPT connector setup in Path B.
cloudflared running, test that the public endpoint is reachable:Troubleshooting
Public hostname returns Cloudflare 530
Public hostname returns Cloudflare 530
A
530 error means Cloudflare resolved the DNS record to a tunnel, but the tunnel connector is not currently connected. The tunnel route configuration is likely correct.Check all of the following:- The MCP server is listening on
http://127.0.0.1:8789 cloudflaredis running (check Task Manager or your terminal)- The Cloudflare Zero Trust dashboard shows at least one active replica for the tunnel
- The tunnel ingress route points to
http://127.0.0.1:8789(not an HTTPS address)
cloudflared again should resolve a 530 caused by the connector being stopped.cloudflared starts but the tunnel shows 0 replicas
cloudflared starts but the tunnel shows 0 replicas
This usually means the credentials file path in
config.yml is wrong or the tunnel ID in config.yml does not match the credentials file. Verify that:- The
tunnel:field inconfig.ymlmatches the tunnel name or UUID you created - The
credentials-file:path points to the correct JSON file at%USERPROFILE%\.cloudflared\<tunnel-id>.json - The credentials file is readable by the Windows user running
cloudflared
Ingress route not matching the hostname
Ingress route not matching the hostname
If
cloudflared starts cleanly but requests to mcp.your-domain.example return 404, verify that the hostname: in the ingress block matches the CNAME record exactly — including any subdomain. The ingress catch-all (service: http_status:404) is required and must be the last entry in the ingress list.