Localhost & Tunnels
Test locally

Localhost & Tunnels

Testing a store that runs on your own machine — with a tunnel, or with the Local runtime (private beta).

What counts as local

The harness treats a host as local when it is localhost, a private or loopback address, a bare hostname, or ends in .local, .localhost, .internal, or .test. Local hosts keep their http:// scheme — public hosts are always upgraded to HTTPS — and a saved target with a local endpoint is stamped environment: local automatically.

Warning

Keeping the scheme doesn't make the host reachable. Sessions execute in the Cloud runtime — our infrastructure — and your localhost is not on our network. A local address saved as a target is valid, but a Cloud session cannot reach it.

Use a tunnel

  1. Expose your dev store on a public HTTPS hostname with the tunnel tool you already use (ngrok, cloudflared, or similar) — e.g. https://mystore.ngrok.app.
  2. Serve your UCP profile at /.well-known/ucp on that hostname so endpoint discovery works, or save a target with explicit endpoints to skip discovery.
  3. Point the Inspector or an Agent run at the tunnel domain like any other store.

Mind that a tunnel makes your dev store publicly reachable while it's up — put it behind auth if the catalog matters, and remember the harness sees exactly what any visitor would.

The Local runtime (private beta)

The Local runtime removes the tunnel entirely: a small runner process on your machine connects out to the Playground, and each merchant-bound request of your session executes inside your network — localhost works as-is. Three properties are the point of the design:

  • Store traffic stays local; the session does not. The runner makes the store-bound requests on your machine, so your store never has to be publicly reachable — but the responses return to your session in the cloud, where they are processed and stored like any other run. Don't point it at a store whose responses you can't send us.
  • An allowlist on both ends. The runner declares which hosts it may reach (by default only localhost), and both the cloud and the runner enforce that list independently — neither side can widen it alone.
  • Keys stay in custody. Signed requests arrive at the runner with signature headers already computed — no signing key material ever crosses onto your machine.

The Local runtime is in private beta. For access, get in touch; we send beta participants the runner, a single Node.js file (Node 22 or newer).

If you're in the beta

  1. In Settings → Tokens, create a token of type Local runner. It can only claim and answer jobs for your runner; it cannot read sessions or start runs. Copy it — it is shown once.
  2. Start the runner next to your store, naming only your store's host and port: RUNNER_TOKEN=<token> node ucp-playground-runner.cjs --targets=localhost:3000. Without --targets it allows every port on localhost, which includes anything else running on your machine.
  3. On the Agent page, pick Local under Runtime (it shows your runner as online), enter your store's local address and run as usual.

A Local session can complete a checkout on your store, so you can test payment through to the order. Playground holds no payment credentials and moves no money: the agent pays only with the payment handlers your store declares, sending each instrument with that handler's id. Use your payment provider's sandbox keys.

Note

Store-page (WebMCP) runs always use a browser on our side, in the Cloud runtime. To run one against a store on your machine, expose it with a tunnel.