Expose a local MCP server

AI clients call tools on remote MCP servers over HTTPS. Hosted ones — custom connectors in Claude, connectors in ChatGPT — reach your server from their own infrastructure, so localhost is out of their reach. devgate gives the server on your machine a public HTTPS URL in one command, and you can try every change from a real client without deploying it.

Before you start

You need an MCP server that speaks HTTP — Streamable HTTP, or the older SSE transport — on a local port, say http://localhost:8000/mcp. A server that only speaks stdio has no port to expose; run it behind an HTTP transport first. And you need the devgate agent with your authtoken added (Quickstart).

Put it online

$ devgate http 8000 --url https://

https:// with no host means your dev domain: every account has one, and its name never changes, so a client you register it with keeps working across restarts. The agent prints the URL; your MCP endpoint is that URL plus the server's path, e.g. https://brave-otter-moon.devgate-free.run/mcp. Without --url you get a random address that changes on every run.

Connect a client

  • Claude and ChatGPT — add the URL as a custom connector (the menu names vary between versions). These clients connect from their own servers, which is why the URL has to be public.
  • Claude Code — add it as an HTTP server:
    $ claude mcp add --transport http my-tools https://<your-dev-domain>/mcp
  • Anything else that speaks Streamable HTTP — Cursor, the MCP Inspector, your own code — takes the same URL.

Every call then shows up in the dashboard's Traffic Inspector; turn on full capture to see the JSON-RPC bodies, which is the quickest way to find out what a client actually sent.

Keep it private

A public URL is reachable by anyone who has it, and an MCP server is often a direct line into your data. If your client can send a header — Claude Code, Cursor and the MCP Inspector can — require a token at the edge, so requests without it never reach your machine. Make one with openssl rand -hex 32, put it in the policy, and keep the file out of git:

mcp-policy.yml
on_http_request:
  - expressions:
      - "!('Bearer <your-token>' in getReqHeader('authorization'))"
    actions:
      - type: custom-response
        config:
          status_code: 401
          headers:
            content-type: application/json
            www-authenticate: Bearer
          body: '{"error":"unauthorized"}'
$ devgate http 8000 --url https:// --traffic-policy-file mcp-policy.yml

Then give the client the same header:

$ claude mcp add --transport http my-tools https://<your-dev-domain>/mcp --header "Authorization: Bearer <your-token>"

custom-response costs no Traffic Policy units, so this works on every plan. Hosted connectors can't always send a header of your choosing: for them, put authorization (OAuth) in the MCP server itself, or allow only the provider's published IP ranges with restrict-ips (a paid action, Hobby and up).

If it doesn't connect

  • “Tunnel offline” — the agent isn't running, or isn't connected; start it again and keep it running while you use the client.
  • 404 — the path is wrong: the server listens on /mcp, /sse or its own path, not the root.
  • 403 or “invalid host” — the server checks the Host header to guard against DNS rebinding, and doesn't know your devgate hostname. Add the hostname to its allowed hosts, or start with --host-header=rewrite so it sees localhost:8000 (more on Host headers).
  • 401 — the client doesn't send the token, or sends a different one; compare them in the Traffic Inspector.
  • 429 — the account went over its per-minute rate limit, usually a client stuck in a retry loop.

Questions people actually ask

Does devgate work with Streamable HTTP and SSE MCP servers?

Yes. Responses pass through as they are written, so each server-sent event reaches the client at once, not when the stream ends — with full capture on as well. A stream may stay open for up to an hour without traffic.

Will the Free plan's warning page break my MCP client?

No. The warning page is only for people opening a page in a browser. An MCP client sends no browser User-Agent and asks for JSON or an event stream, so its requests go straight to your server.

How do I keep the URL the same, so I register the connector once?

Start the endpoint on your dev domain: devgate http 8000 --url https:// — every account has one, and it never changes. A run without --url gets a new random address each time.