Firetool CLI is an agent-first command line tool for controlling Firebase emulators without ever falling back to real Firebase resources.
Firebase emulators are excellent for local development, but they are awkward to inspect, seed, reset, and automate across Auth, Firestore, Realtime Database, Storage, Functions, Pub/Sub, and Rules. That friction gets worse when AI agents, scripts, or CI jobs need predictable structured output and safe failure modes.
Firetool solves that by giving developers and automation a single local-only CLI with consistent JSON output, explicit exit codes, dry-run support, and guardrails around destructive operations.
Modern development workflows increasingly rely on agents, repeatable test data, and local integration environments. Firebase projects often end up needing one-off scripts for common emulator tasks:
- creating and deleting Auth emulator users;
- seeding Firestore or Realtime Database data;
- exporting local state for debugging;
- clearing emulator data between tests;
- calling local Functions;
- publishing local Pub/Sub messages;
- checking Firestore and Storage rules behavior;
- detecting whether the right emulator context is running.
Firetool turns those tasks into a stable CLI surface designed for both humans and machines.
- Local-only safety model: Firetool blocks non-local hosts and is designed to operate only against Firebase emulators.
- Agent-friendly output: every command can emit structured JSON with
--json. - Predictable failures: error categories map to distinct exit codes for shell scripts and AI agents.
- Destructive-operation guardrails: dangerous operations require
--force; many support--dry-run. - Firebase context discovery:
firetool doctorinspectsfirebase.json,.firebaserc, emulator environment variables, and running ports — and when emulators are down, it prints the exactfirebase emulators:startcommand for the ones this project declares. - Multi-service coverage: Auth, Firestore, Realtime Database, Storage, Functions, Pub/Sub, and security rules.
- Typed internals: built with TypeScript, Zod schemas, and a tRPC-style internal router.
| Service | What Firetool helps with | Common methods |
|---|---|---|
| Auth Emulator | Create, inspect, update, and clean up local users for tests and demos. | create-user, list-users, get-user, update-user, delete-user, clear-users |
| Firestore Emulator | Work with local documents and collections, including repeatable seed/import/export flows. | get, list, set, update, query, seed, import, export, delete, delete-collection, clear |
| Realtime Database Emulator | Read, write, query, seed, export, and reset local RTDB paths. | get, set, update, push, query, seed, import, export, delete, clear |
| Storage Emulator | Manage local bucket objects without touching production storage. | list, upload, download, remove, clear |
| Functions Emulator | Invoke local Firebase Functions by name or URL with JSON payloads. | call |
| Pub/Sub Emulator | Publish structured local messages with optional attributes. | publish |
| Rules checks | Probe Firestore and Storage security rules locally for a path, intent, and auth context. | check |
| Diagnostics | Discover Firebase project context and emulator availability before acting. | doctor, help-info |
This repository includes a concise skill for AI agents at skills/firetool-cli/SKILL.md. It tells agents how to discover and use the CLI through firetool --help, firetool help-info, and JSON-first command execution instead of relying on copied command lists.
Install the skill into a supported agent with:
npx skills add AJCastello/firetool-cliThe skill teaches agents how to drive Firetool, but it does not install the Firetool CLI binary itself — install that separately as described in Installation.
Try it without installing anything, from inside a Firebase project using the Emulator Suite:
npx firetool-cli doctor
npx firetool-cli doctor --jsonInstall it globally once you want the firetool binary on your path:
npm install -g firetool-cli
firetool doctor --jsonFiretool requires Node.js 22.12 or newer. That floor is not a guess: CI installs exactly that version, builds the CLI, and runs it, so the compatibility claim is exercised on every change rather than asserted.
If you are still on Node 20 or 21, install 0.1.2 instead — it declares node >=20 and contains every fix released before the floor was raised:
npm install -g firetool-cli@0.1.2The 0.2.0 release raised the floor because commander 15 requires it. Runtime dependencies are not bundled, so a dependency's own floor becomes Firetool's floor on the installing machine.
Published packages include only the built CLI bundle plus the public package files:
dist/README.mdLICENSE
This repository uses Bun.
bun install
bun run check
bun run buildRun the CLI directly from source:
bun run dev -- doctor --jsonBuild output is generated under dist/ and is intentionally ignored by Git.
If you want to contribute, start with CONTRIBUTING.md. For security reports, use SECURITY.md instead of filing a public issue.
Diagnose the local context first — this works whether or not the emulators are up, and tells you what to start if they are not:
firetool doctor --json⚠ No configured emulator is running. Start them with: firebase emulators:start --only auth,database,firestore
Run that command, then start operating. Note that the Firebase CLI names its Realtime Database emulator database while Firetool calls the service rtdb; doctor always emits the name the Firebase CLI accepts.
Create a local Auth user:
firetool auth create-user \
--email user@example.test \
--password secret123 \
--jsonSeed a Firestore collection:
firetool firestore seed products \
--file ./products.seed.json \
--dry-run \
--jsonCall a local Firebase Function:
firetool functions call createUserProfile \
--data '{"uid":"abc123"}' \
--jsonPublish a local Pub/Sub message:
firetool pubsub publish user-created \
--data '{"uid":"abc123"}' \
--attribute source=local-test \
--jsonCheck local security rules:
firetool rules check \
--service firestore \
--path products/abc123 \
--intent read \
--auth-uid user_123 \
--json| Area | Command | What to use it for | Useful flags |
|---|---|---|---|
| Diagnostics | firetool doctor |
Check whether Firetool found a Firebase project and which emulators are configured/running, and get the command to start the ones that are down. | --json |
| Discovery | firetool help-info [service] |
Print the agent-first usage guide, service catalog, and error model. | --json |
| Auth | firetool auth <method> |
Manage local Auth emulator users for tests, demos, and repeatable local setup. | --json, --force |
| Firestore | firetool firestore <method> |
Inspect, mutate, seed, import/export, and clear local Firestore data. | --json, --dry-run, --force, --file, --data |
| Realtime Database | firetool rtdb <method> |
Inspect, mutate, seed, import/export, and clear local RTDB paths. | --json, --dry-run, --force, --file, --data |
| Storage | firetool storage <method> |
List, upload, download, remove, and clear local Storage emulator objects. | --json, --dry-run, --force, --bucket, --file |
| Functions | firetool functions call <name-or-url> |
Invoke a local Firebase Function with an optional JSON payload. | --json, --data |
| Pub/Sub | firetool pubsub publish <topic> |
Publish local Pub/Sub messages with optional attributes. | --json, --data, --attribute |
| Rules | firetool rules check |
Check Firestore or Storage rules locally for a path, operation intent, and optional auth context. | --json, --service, --path, --intent, --auth-uid |
Firetool is intentionally scoped to local Firebase emulator workflows. It discovers emulator settings from:
firebase.json;.firebaserc;- Firebase emulator environment variables such as
FIRESTORE_EMULATOR_HOST; - default local emulator ports.
Before sensitive operations, it checks that the target service is configured, running, local, and unambiguous. Non-local hosts are blocked instead of being treated as valid targets.
Accepted by default: localhost, the full 127.0.0.0/8 loopback range, 0.0.0.0 (commonly used by Firebase emulators and WSL setups), IPv6 loopback (::1, expanded forms, ::ffff:127.x.x.x), and custom hostnames whose DNS resolves exclusively to loopback addresses.
For Docker, devcontainer, and WSL topologies where the emulator is reachable via a non-loopback hostname (e.g. host.docker.internal), add that hostname to the explicit allowlist:
export FIRETOOL_ALLOWED_EMULATOR_HOSTS=host.docker.internal,firebase-emulatorPrivate LAN IPs (192.168.x.x, 10.x.x.x, 172.16–31.x.x) are blocked by default because they may represent another machine, a shared environment, or a container Firetool should not target without an explicit decision. Add a specific IP to the allowlist if you genuinely run emulators there.
See docs/host-strategy.md for the full explanation of the classification model, all supported topology scenarios, and the admin vs rules-check distinction.
Data commands for Auth, Firestore, Realtime Database, and Storage talk to the emulators with admin credentials, so they work regardless of firestore.rules, database.rules.json, or storage.rules. This keeps seeding and resetting local state predictable.
The consequence is worth stating plainly: a successful data command does not mean your app could perform the same operation. To check what your rules actually allow for a given identity, use firetool rules check.
Use --json when integrating with agents, scripts, or CI:
{
"ok": true,
"operation": "firestore.seed",
"target": {
"service": "firestore",
"resourcePath": "products"
},
"result": {},
"warnings": []
}Known error categories use distinct exit codes:
| Error code | Meaning |
|---|---|
CONTEXT_NOT_FOUND |
No Firebase project context was found. |
SERVICE_NOT_CONFIGURED |
The requested emulator is not configured. |
EMULATOR_NOT_RUNNING |
The emulator is configured but unavailable locally. |
INVALID_INPUT |
JSON, flags, paths, or identifiers are invalid. |
CONFIRMATION_REQUIRED |
A destructive operation needs confirmation or --force. |
RULE_DENIED |
Local rules denied the requested operation. |
AMBIGUOUS_TARGET |
Firetool cannot determine the local target safely. |
Firetool uses a tag-first release cycle, published to npm through trusted publishing (OIDC) with build provenance. No npm token is stored in the repository, and releases are never published by hand.
- open a release pull request updating
CHANGELOG.mdand the version inpackage.json; - run
bun run release:check; - merge it;
- tag the merged commit
v<version>and push the tag.
The workflow verifies that the tag matches package.json before releasing, and creates the GitHub release only after npm publishing succeeds.
The npm package ships only the built CLI bundle and essential package files, not the full repository source tree.
See CONTRIBUTING.md for the full release procedure, including how to verify a publication actually reached the registry.
MIT
