Skip to main content

Stable Remote URL

Use this tutorial when a remote MCP client needs the same HTTPS URL every day while Coding Tools MCP continues to run on your local machine. A stable domain solves one problem: the client can keep the same server URL. It does not make a public multi-user relay by itself, and it does not discover every developer’s laptop automatically.

What one URL means

There are two different designs:
  • Personal stable URL: one developer runs Coding Tools MCP locally and maps a stable hostname such as https://mcp.example.com/mcp to that machine through ngrok, Cloudflare Tunnel, devtunnel, or another reverse tunnel.
  • Hosted relay for many users: a separate service authenticates users, registers devices, keeps outbound sessions from local agents, and routes each request to the right connected workspace.
Coding Tools MCP provides the local MCP runtime and helper tunnel scripts. It does not include a hosted relay, dashboard, device registry, or per-user routing service. Use a subdomain for MCP traffic instead of the apex domain:
docs.example.com       documentation or marketing site
app.example.com        future dashboard, if you build one
mcp.example.com        Coding Tools MCP tunnel or relay entrypoint
local.example.com      private dogfood tunnel
For a personal stable URL, this tutorial uses:
https://mcp.example.com/mcp

Choose the auth mode

Use OAuth when the MCP client supports OAuth 2.1 Authorization Code + PKCE discovery:
export CODING_TOOLS_MCP_SERVER_URL="https://mcp.example.com"
export CODING_TOOLS_MCP_OAUTH_PASSWORD="replace-with-login-password"
export CODING_TOOLS_MCP_OAUTH_TOKEN_SECRET="$(python3 -c 'import secrets; print(secrets.token_bytes(32).hex())')"

coding-tools-mcp \
  --workspace /absolute/path/to/repo \
  --host 127.0.0.1 \
  --port 8765 \
  --tool-profile read-only \
  --oauth-mode
Use bearer auth only when the client can send a custom Authorization header:
export CODING_TOOLS_MCP_AUTH_TOKEN="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"

coding-tools-mcp \
  --workspace /absolute/path/to/repo \
  --host 127.0.0.1 \
  --port 8765 \
  --tool-profile read-only \
  --auth-token "$CODING_TOOLS_MCP_AUTH_TOKEN"
Do not put the bearer token in a query string. Coding Tools MCP checks the Authorization: Bearer <token> header on /mcp.

ngrok reserved domain

Claim a reserved ngrok domain, then run Coding Tools MCP on loopback and point ngrok at the local port. Terminal 1:
export CODING_TOOLS_MCP_SERVER_URL="https://your-domain.ngrok-free.dev"
export CODING_TOOLS_MCP_OAUTH_PASSWORD="replace-with-login-password"
export CODING_TOOLS_MCP_OAUTH_TOKEN_SECRET="$(python3 -c 'import secrets; print(secrets.token_bytes(32).hex())')"

coding-tools-mcp \
  --workspace /absolute/path/to/repo \
  --host 127.0.0.1 \
  --port 8765 \
  --tool-profile read-only \
  --oauth-mode
Terminal 2:
ngrok http --domain=your-domain.ngrok-free.dev 8765
Configure the remote MCP client with:
https://your-domain.ngrok-free.dev/mcp
The repository helper script can also run ngrok for quick testing:
CODING_TOOLS_MCP_AUTH_MODE=oauth \
CODING_TOOLS_MCP_TOOL_PROFILE=read-only \
CODING_TOOLS_MCP_SERVER_URL="https://your-domain.ngrok-free.dev" \
scripts/tunnel.sh ngrok /absolute/path/to/repo
Use that script with a stable domain only after your ngrok configuration pins the reserved domain. Otherwise the helper starts a normal ngrok tunnel and the URL may change.

Cloudflare named tunnel

Create a named tunnel and route a DNS hostname to it. Then map that hostname to the local Coding Tools MCP port. One-time setup:
cloudflared tunnel login
cloudflared tunnel create coding-tools-mcp
cloudflared tunnel route dns coding-tools-mcp mcp.example.com
Create or update the Cloudflare tunnel config so the hostname forwards to the local server:
tunnel: <tunnel-id>
credentials-file: /home/YOU/.cloudflared/<tunnel-id>.json
ingress:
  - hostname: mcp.example.com
    service: http://127.0.0.1:8765
  - service: http_status:404
Terminal 1:
export CODING_TOOLS_MCP_SERVER_URL="https://mcp.example.com"
export CODING_TOOLS_MCP_OAUTH_PASSWORD="replace-with-login-password"
export CODING_TOOLS_MCP_OAUTH_TOKEN_SECRET="$(python3 -c 'import secrets; print(secrets.token_bytes(32).hex())')"

coding-tools-mcp \
  --workspace /absolute/path/to/repo \
  --host 127.0.0.1 \
  --port 8765 \
  --tool-profile read-only \
  --oauth-mode
Terminal 2:
cloudflared tunnel run coding-tools-mcp
Configure the remote MCP client with:
https://mcp.example.com/mcp

Quick tunnel for demos

For temporary testing, the checked-in script can install the package, start the local server, and expose a tunnel:
curl -fsSL https://raw.githubusercontent.com/xyTom/coding-tools-mcp/main/scripts/install.sh \
  | bash -s -- --tunnel cloudflared --auto-install-tunnel --workspace /absolute/path/to/repo
This is convenient for demos, but the public URL may change on restart. Use a reserved ngrok domain or Cloudflare named tunnel when the client configuration must stay stable.

Verify the URL

Replace BASE_URL with the tunnel origin without /mcp:
curl "$BASE_URL/.well-known/mcp.json"
curl "$BASE_URL/.well-known/oauth-authorization-server"
curl "$BASE_URL/.well-known/oauth-protected-resource"
For a JSON-RPC ping over HTTP:
curl "$BASE_URL/mcp" \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2025-06-18" \
  --data '{"jsonrpc":"2.0","id":1,"method":"ping","params":{}}'
If you use bearer auth, add:
-H "Authorization: Bearer $CODING_TOOLS_MCP_AUTH_TOKEN"

Security notes

  • Keep Coding Tools MCP bound to 127.0.0.1; expose only the tunnel URL.
  • Prefer --tool-profile read-only for remote sessions until you explicitly need write tools.
  • Use OAuth or bearer auth for every non-loopback deployment.
  • Treat full plus exec_command as remote code execution inside the configured workspace boundary.
  • Stop the tunnel when testing is done, and rotate tokens or OAuth secrets if they were shared.