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.
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
- 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. - Serve your UCP profile at
/.well-known/ucpon that hostname so endpoint discovery works, or save a target with explicit endpoints to skip discovery. - 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
- 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.
- 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--targetsit allows every port onlocalhost, which includes anything else running on your machine. - 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.
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.