firebase-agent-sandbox
Hand an agent one URL and it’s signed in to your Firebase project, with scoped access you can revoke or throw away.
One URL
From a request to a signed-in page in eight steps.
The agent asks for a persona and gets back a URL. Opening it signs the browser in as the same test user every time, with sample data seeded for this run alone. Try placing an order, ending the session, and reusing the old link.
- Agent
create_session({ persona: "buyer" })through the MCP server. - MCP server
POST /grantsto the token service, with the service key. - Token service
Signs a grant: a JWT, good for 10 minutes at most, single use, bound to one origin.
- MCP server
Hands the agent a URL with the grant in it.
- Agent
Opens the URL. The client SDK takes the grant out of the address bar and history.
- Browser
POST /exchange. The service seeds the fixtures before it answers. - Token service
Mints a Firebase custom token for
agent.buyer.01. It stays in memory, never in a URL. - Browser
signInWithCustomToken. Firestore rules see the claims and confine the session.
Your orders signed in as Agent Buyer
- Anvil × 1$42.50buyer.01.k3f9-o1 · Jan 1, 2026
- Rope, 50m × 1$9.99buyer.01.k3f9-o2 · Jan 1, 2026
Run k3f9: signed in as agent.buyer.01 in lane 01, with fresh sample data.
Firestore demo-agent-sandbox
users/agent.buyer.01Agent Buyer_sandbox: { sandboxId: "buyer.01.k3f9" }orders/buyer.01.k3f9-o1Anvil × 1 · $42.50_sandbox: { sandboxId: "buyer.01.k3f9" }orders/buyer.01.k3f9-o2Rope, 50m × 1 · $9.99_sandbox: { sandboxId: "buyer.01.k3f9" }catalog/p-anvilAnvil · $42.50_sandbox: { shared: true }catalog/p-ropeRope, 50m · $9.99_sandbox: { shared: true }catalog/p-lampDesk lamp · $24.00_sandbox: { shared: true }
For the agent
Five MCP tools.
The MCP server runs over stdio, and the CLI does the same jobs from a terminal. Both talk to a token service you deploy yourself, and its grant endpoint only answers to your service key.
list_personasWho it can be
Each persona with its lanes, fixtures and claims, plus every environment and its origins.
create_sessionA signed-in URL
Takes a persona, returns the URL and the session. Same as
agent-sandbox url -p buyeron the command line.get_session_stateIs it still live
Whether the session is active, and how long it has left.
reset_sessionStart the data over
Wipes the session’s data and seeds it again. Name one fixture to seed just that one, without the wipe.
end_sessionClean up
Wipes the data, frees the lane and revokes the tokens.
Setting it up
Personas live in one config file.
Each persona has a number of lanes, so two agents can be buyer at once as agent.buyer.01 and agent.buyer.02. This is the demo app’s config. It’s pre-1.0: nothing is on npm yet, and the repository isn’t public.
personas: {
buyer: {
lanes: 2,
claims: { role: 'customer' },
fixtures: ['catalog', 'buyer-profile'],
},
seller: { lanes: 1, claims: { role: 'merchant' },
fixtures: ['catalog', 'seller-shop'] },
},
sessionTtl: '45m',Capabilities
What it does.
Teams who want an agent working against a live backend without giving it a service account.
- 01
Named test personas, so an agent comes back as the same user every run
- 02
Sample data created for each session and wiped afterward, so runs don’t leak into each other
- 03
Separate lanes so several agents can work on one project at the same time
- 04
An MCP server and CLI for creating access grants, with the grant endpoint protected by a service key
Specification
What it’s built with, and when.
Pulled from the repository each time this site is built.
- State
- Prototype
- Discipline
- Developer tooling
- Shape
- Developer tooling
- Started
- Aug 2026
- Last change
- Aug 2026
- Commits
- 7
- TypeScript
- Firebase
- MCP
- TypeScript 96%
- JavaScript 4%
Related work
Built alongside firebase-agent-sandbox
Next
Put firebase-agent-sandbox in your pipeline
Ask about hooking it up to the repositories and agents you already run.