This guide exposes coding-tools-mcp to remote MCP clients through an HTTPS tunnel.
The server implements Streamable HTTP at /mcp, publishes remote discovery metadata at /.well-known/mcp.json and /.well-known/mcp/server-card.json, and supports three auth modes on /mcp:
none— no authentication; only acceptable for local/testing tunnels with theread-onlyprofile.bearer— staticAuthorization: Bearer <token>for clients that can send custom headers.oauth2— OAuth 2.1 Authorization Code + PKCE for MCP clients that perform the standard discovery + authorization-code flow. Discovery metadata is published at/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resource.
Use --tool-profile read-only first. It exposes inspection and git read tools plus set_default_cwd for navigation, and omits workspace mutation tools such as apply_patch, exec_command, write_stdin, and kill_session.
Use --tool-profile full only for trusted MCP clients that support write tools and truthful annotations. Avoid full and compat-readonly-all for anonymous tunnel testing.
Install the published package from PyPI, start the local server, and expose a read-only bearer-token tunnel:
curl -fsSL https://raw.githubusercontent.com/xyTom/coding-tools-mcp/main/scripts/install.sh \
| bash -s -- --tunnel cloudflared --auto-install-tunnel --workspace /path/to/repoThe script prints the local MCP URL, the tunnel provider's HTTPS URL, and the bearer header to configure in clients that support custom headers.
CODING_TOOLS_MCP_AUTH_MODE=noauth \
CODING_TOOLS_MCP_TOOL_PROFILE=read-only \
scripts/tunnel.sh cloudflared /path/to/repoConfigure the remote MCP client with the HTTPS tunnel URL:
https://<tunnel-host>/mcp
The discovery metadata reports auth type none in this mode. Anyone who can reach the tunnel URL can use the exposed read-only tools, so avoid sensitive workspaces and stop the tunnel when testing is done.
For clients that can send custom headers:
export CODING_TOOLS_MCP_AUTH_TOKEN="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
CODING_TOOLS_MCP_AUTH_MODE=bearer \
CODING_TOOLS_MCP_TOOL_PROFILE=read-only \
scripts/tunnel.sh cloudflared /path/to/repoUse:
URL: https://<tunnel-host>/mcp
Header: Authorization: Bearer <token>
For MCP clients that perform OAuth 2.1 Authorization Code + PKCE discovery on the server URL, run the tunnel script with CODING_TOOLS_MCP_AUTH_MODE=oauth. The only env var you must set yourself is CODING_TOOLS_MCP_SERVER_URL (the script cannot know your tunnel's public URL); CLIENT_ID, CLIENT_SECRET, and PASSWORD are generated and printed for you on startup:
export CODING_TOOLS_MCP_SERVER_URL="https://<stable-tunnel-host>"
CODING_TOOLS_MCP_AUTH_MODE=oauth \
CODING_TOOLS_MCP_TOOL_PROFILE=read-only \
scripts/tunnel.sh cloudflared /path/to/repoThe script adds --oauth-mode to the server and prints the OAuth metadata URLs and the generated credentials (copy them before they scroll out of view; they regenerate on every run unless you preset the env vars). The same flow works with scripts/install.sh --tunnel <provider> --auth-mode oauth. For local-only OAuth testing without a tunnel, scripts/install.sh --start --auth-mode oauth defaults CODING_TOOLS_MCP_SERVER_URL to http://127.0.0.1:<port>.
Required:
CODING_TOOLS_MCP_SERVER_URL— public base URL (no trailing/mcp); used as theissuer/audclaim in issued tokens and in discovery metadata. Must match the tunnel's actual external URL.
Auto-generated if unset (override to keep stable values across restarts):
CODING_TOOLS_MCP_OAUTH_CLIENT_ID— the only client_id the server accepts.CODING_TOOLS_MCP_OAUTH_CLIENT_SECRET— paired with the client_id on/oauth/token(acceptsclient_secret_postor HTTP Basic).CODING_TOOLS_MCP_OAUTH_PASSWORD— the password an operator types on the/oauth/authorizeHTML form to grant the authorization code.
Optional:
CODING_TOOLS_MCP_OAUTH_TOKEN_SECRET— hex-encoded HS256 signing key. Without it, a random key is generated per process and all tokens are invalidated on restart. Generate one withpython3 -c "import secrets; print(secrets.token_bytes(32).hex())".CODING_TOOLS_MCP_OAUTH_TOKEN_TTL— access-token lifetime in seconds (default2592000, i.e. 30 days).
Endpoints exposed when --oauth-mode is active:
GET /.well-known/oauth-authorization-server— RFC 8414 authorization-server metadata.GET /.well-known/oauth-protected-resource— RFC 9728 protected-resource metadata.GET /oauth/authorize— renders an HTML password prompt; onlyresponse_type=codeandcode_challenge_method=S256are accepted. Authorization codes expire after 5 minutes.POST /oauth/authorize— accepts the password, issues a one-time code, and 302s back toredirect_uri.POST /oauth/token— exchangesgrant_type=authorization_code+code_verifierfor a Bearer JWT.
/mcp accepts the issued token as Authorization: Bearer <token>; unauthenticated requests get HTTP 401 with a WWW-Authenticate header pointing at the protected-resource metadata. --auth-token is ignored while OAuth is active.
OAuth metadata and issued JWT claims pin the public URL at server start. Ephemeral tunnels (e.g. cloudflared tunnel --url, default ngrok, default devtunnel) generate a fresh random subdomain each run, so the URL the script advertises to clients will not match the tunnel's actual host after any restart. Use one of:
- cloudflared named tunnel —
cloudflared tunnel create <name>+cloudflared tunnel route dns <name> mcp.example.com, then setCODING_TOOLS_MCP_SERVER_URL=https://mcp.example.com. - ngrok reserved domain — claim a domain in the ngrok dashboard and either configure it in
~/.config/ngrok/ngrok.yml, or runngrokyourself (ngrok http --domain=<reserved> 8765) and setCODING_TOOLS_MCP_SERVER_URL=https://<reserved>before launching the server. The bundledscripts/tunnel-ngrok.shrunsngrok http http://127.0.0.1:$PORTwithout extra flags, so to attach a reserved domain you either point ngrok at it via config or run ngrok separately and start the server withcoding-tools-mcp --oauth-modedirectly. - devtunnel persistent tunnel —
devtunnel create <id>+devtunnel port create <id> -p 8765 --protocol http, thendevtunnel host <id>.
If you only need ephemeral testing, prefer bearer mode over oauth.
Each script starts coding-tools-mcp on 127.0.0.1 and then starts the selected tunnel provider. If the provider CLI is missing, the script asks before installing it.
scripts/tunnel.sh cloudflared /path/to/repo
scripts/tunnel.sh ngrok /path/to/repo
scripts/tunnel.sh devtunnel /path/to/repoOptional environment variables:
CODING_TOOLS_MCP_AUTO_INSTALL_TUNNEL=1
CODING_TOOLS_MCP_AUTH_MODE=bearer # bearer | noauth | oauth
CODING_TOOLS_MCP_PORT=8765
CODING_TOOLS_MCP_TOOL_PROFILE=read-only
CODING_TOOLS_MCP_AUTH_TOKEN=<existing-token>
CODING_TOOLS_MCP_SERVER_BIN=coding-tools-mcp
# Required when CODING_TOOLS_MCP_AUTH_MODE=oauth:
CODING_TOOLS_MCP_SERVER_URL=https://<stable-tunnel-host>
# Auto-generated and printed at startup if unset:
CODING_TOOLS_MCP_OAUTH_CLIENT_ID=<client-id>
CODING_TOOLS_MCP_OAUTH_CLIENT_SECRET=<client-secret>
CODING_TOOLS_MCP_OAUTH_PASSWORD=<authorize-page-password>
# Optional (oauth):
CODING_TOOLS_MCP_OAUTH_TOKEN_SECRET=<hex-encoded-32-bytes>
CODING_TOOLS_MCP_OAUTH_TOKEN_TTL=2592000If the selected tunnel CLI is missing, the scripts prompt before installing it. cloudflared installs into ~/.local/bin when Homebrew is unavailable; ngrok uses Homebrew or npm; devtunnel uses the Microsoft installer script.
Replace BASE_URL with the tunnel origin, without /mcp.
curl "$BASE_URL/.well-known/mcp.json"For bearer mode only:
curl "$BASE_URL/mcp" \
-H "Authorization: Bearer $CODING_TOOLS_MCP_AUTH_TOKEN"
curl "$BASE_URL/mcp" \
-H "Authorization: Bearer $CODING_TOOLS_MCP_AUTH_TOKEN" \
-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":{}}'Missing or wrong bearer tokens on /mcp should return HTTP 401.
For OAuth mode, the discovery endpoints should respond without auth:
curl "$BASE_URL/.well-known/oauth-authorization-server"
curl "$BASE_URL/.well-known/oauth-protected-resource"A 401 response from /mcp includes:
WWW-Authenticate: Bearer realm="coding-tools-mcp", resource_metadata="<BASE_URL>/.well-known/oauth-protected-resource"
Keep the server bound to 127.0.0.1 and expose only the tunnel URL. Non-loopback binding is rejected unless a bearer token or --oauth-mode is configured. Use HTTPS tunnel URLs, rotate bearer tokens and OAuth client secrets if they are shared, set CODING_TOOLS_MCP_OAUTH_TOKEN_SECRET so OAuth tokens survive restarts only when you actually want that, and do not use full or compat-readonly-all with untrusted clients.
Anonymous remote MCP tunnel testing exposes whatever the selected profile permits to anyone who can reach the tunnel URL. Use read-only, avoid sensitive workspaces, and stop the tunnel when testing is done.