Get started

Introduction

Record every LLM and agent run as one trace, keep it as long as you like in a Firebase project you own, and read it back from your agents over MCP.

Deploy your ownSign in

Get started

Everything below talks to this deployment at https://tracing.art3m1s.me. All of it authenticates with one project API key (ft_live_<keyId>_<secret>), created in the dashboard under Project settings → API keys and shown exactly once. There is no keyless mode: FireTrace stores your traces in your own database, so it never accepts anonymous writes.

Connect an agent over MCP

Set up FireTrace MCP

See all setup options →

Pick a client, paste the snippet, and your agent can list traces, walk span trees, record its own runs and leave scores. Tools are registered per key scope, so a read-only key never exposes a write tool.

Claude Code
claude mcp add --transport http firetrace https://tracing.art3m1s.me/api/mcp \
  --header "Authorization: Bearer ft_live_..."

Using another MCP client? Point it at https://tracing.art3m1s.me/api/mcp with an Authorization: Bearer header.

Give an investigating agent a read-only key with an expiry, then ask it something like “list the last 10 error traces and tell me what broke.” Full tool reference in MCP.

Send your first trace

One request stores one complete trace with all of its spans; a long agent run can instead start the trace first and stream spans as they finish. No agent to install. Any language that can POST JSON is a supported client.

curl -s -X POST https://tracing.art3m1s.me/api/v1/traces \
  -H "Authorization: Bearer $FIRETRACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "schemaVersion": 1,
    "trace": {
      "id": "42f38ac8295345a7a12c4e3f60d6da23",
      "name": "answer-question",
      "status": "ok",
      "startedAt": "2026-09-02T19:01:02.120Z",
      "endedAt": "2026-09-02T19:01:04.812Z",
      "model": "example-model",
      "input": {
        "prompt": "Explain vector search in two sentences."
      },
      "output": {
        "text": "Vector search finds items whose embeddings are nearest to a query embedding."
      },
      "usage": {
        "inputTokens": 412,
        "outputTokens": 96,
        "totalTokens": 508
      },
      "tags": [],
      "metadata": {},
      "spans": [
        {
          "id": "00f067aa0ba902b7",
          "parentSpanId": null,
          "name": "generate-text",
          "kind": "llm",
          "status": "ok",
          "startedAt": "2026-09-02T19:01:02.145Z",
          "endedAt": "2026-09-02T19:01:04.780Z",
          "model": "example-model",
          "attributes": {},
          "events": [],
          "usage": {}
        }
      ]
    }
  }'
Response · trace stored
{
  "ok": true,
  "traceId": "42f38ac8295345a7a12c4e3f60d6da23",
  "projectId": "5eedc0ffee5eedc0ffee5eed",
  "spanCount": 1,
  "duplicate": false,
  "running": false,
  "requestId": "0f1e2d3c4b5a6978"
}

The trace is then visible at /projects/{projectId}/traces/{traceId}. Sending the same body again is a 200 with "duplicate": true rather than a second trace, so retrying after a timeout is safe. See the ingestion API for the full field list, limits and error codes.

Other ways to get started

Fastest

Deploy with an AI agent

Paste one prompt into your coding agent. It creates the Firebase project, sets the environment variables, deploys to Vercel and sends a smoke-test trace.

Step by step

Set up Firebase by hand

Create the project and database, enable sign-in providers, and mint the Admin credential the server uses.

Step by step

Deploy to Vercel

Import the repository, set the environment variables, authorize the domain, and run the smoke test.

No deploy

Try it on this deployment

Sign in with any verified Google or email account and record up to 5 traces against this instance before you deploy your own.

What can FireTrace do?

Record

A POST stores a run as a tree of spans with inputs, outputs, tokens, cost, errors and tags, whole or streamed as it happens.

Read

Filter and page through traces from the dashboard, the REST API or an agent; open one as a span tree, waterfall and inspector.

Judge

Attach typed scores by hand, from an agent, or from an LLM-as-judge evaluator you define.

Why FireTrace?

  • Yours, not rented: the app runs on your Vercel account against a Firebase project you own. FireTrace has no servers of its own and no access to your data.
  • No expiry, ever: there is no TTL field, no expireAt, no cleanup job and no age-based deletion. Only an explicit owner action deletes a trace. Capacity is bounded by your Firebase plan, not by a retention window.
  • One POST per run, or streamed: a complete trace with up to 200 nested spans in a single request, deduplicated by a hash of the normalized body; or a start, span batches and an end, so a crash still leaves what happened.
  • Agent-native: the same data is available over MCP, over a REST API described by an OpenAPI 3.1 document, and in the dashboard.
  • Environments come from the key: the server stamps production, preview or whatever slug the key carries onto every trace, so your application never sends an environment field and cannot mislabel one.

Record

POST /api/v1/traces with scope traces:write. Bodies are strict: an unknown key at any level is a 400 invalid_trace, so a typo can never be silently dropped. Trace ids are 32 hex characters, span ids 16 — the OpenTelemetry widths. Beyond the quickstart above, a real run is a tree: up to 200 spans, each with a parentSpanId, its own timings, tokens and cost, and up to 50 events.

There is no dedicated error field. A failing step sets status: "error" and records error.type and error.message in its attributes; the dashboard's Error section reads them from there. Full rules in the ingestion API.

const trace = client.startTrace("answer-question", { input: { prompt } });

const agent = trace.startSpan("answer-question", { kind: "agent" });
agent.addEvent("plan.ready", { steps: 2 });

const tool = agent.startSpan("lookup", { kind: "tool", input: { url } });
tool.end({ status: "ok", output: { status: 200 } });

const llm = agent.startSpan("generate-text", {
  kind: "llm",
  provider: "example-provider",
  model: "example-model",
  input: { messages },
});
llm.end({ status: "ok", output: { text }, usage, costUsd });

agent.end({ status: "ok" });
await trace.end({ status: "ok", output: { text }, usage });

Read

GET /api/v1/traces with scope traces:read returns a newest-first page with cursor pagination. Filters (status, model, name, tag, environment, sessionId, userId, from, to) combine with AND, and sort can be newest, slowest or costliest. The query string is strict too, so a misspelled filter is a 400 rather than an accidental unfiltered list.

curl -s "https://tracing.art3m1s.me/api/v1/traces?status=error&limit=5" \
  -H "Authorization: Bearer $FIRETRACE_API_KEY"
Response · one trace from a page
{
  "traces": [
    {
      "id": "42f38ac8295345a7a12c4e3f60d6da23",
      "name": "answer-question",
      "status": "ok",
      "environment": "production",
      "startedAt": "2026-09-02T19:01:02.120Z",
      "endedAt": "2026-09-02T19:01:04.812Z",
      "durationMs": 2692,
      "provider": null,
      "model": "example-model",
      "sessionId": "session-123",
      "userId": null,
      "tags": [],
      "usage": { "inputTokens": 120, "outputTokens": 84, "totalTokens": 204 },
      "costUsd": null,
      "spanCount": 5,
      "errorCount": 1,
      "estimatedBytes": 4310,
      "ingestedAt": "2026-09-02T19:01:05.004Z",
      "scores": {
        "helpful": {
          "scoreId": "9c1e7a2b3d4f5061",
          "dataType": "boolean",
          "value": true,
          "evaluatorId": null
        }
      }
    }
  ],
  "nextCursor": "eyJz...",
  "prevCursor": null,
  "pageSize": 50
}

Judge

A score is a typed judgement attached to a trace after the run: a thumbs rating, a reviewer's verdict, an evaluator's result. Scores are indexed and append-only, they show up on the trace page and in the trace list, and every trace carries the newest score per name. Configure the optional LLM judge and you can run an evaluator over one trace or over a filtered list — see scores and evaluators.

curl -s -X POST https://tracing.art3m1s.me/api/v1/traces/$TRACE_ID/scores \
  -H "Authorization: Bearer $FIRETRACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"helpful","dataType":"boolean","value":true,"comment":"answered the question"}'
Response · score stored
{
  "ok": true,
  "score": {
    "id": "9c1e7a2b3d4f5061",
    "traceId": "42f38ac8295345a7a12c4e3f60d6da23",
    "spanId": null,
    "name": "helpful",
    "dataType": "boolean",
    "value": true,
    "comment": "answered the question",
    "source": "api",
    "evaluatorId": null,
    "runId": null,
    "createdAt": "2026-09-03T10:12:00.000Z"
  },
  "requestId": "0f1e2d3c4b5a6978"
}

More capabilities

Environments

Label a key production, preview or your own slug; the server stamps it on every trace and the dashboard filters by it.

Scoped keys

traces:write, traces:read and traces:delete, with optional expiry and one-transaction rotation.

Metadata patch

PATCH merges free-form facts into a stored trace without touching its body hash, for judgements that only arrive later.

Idempotent ingest

The body hash is computed after normalization, so a retry that differs only in key order or timestamp notation is a duplicate, not a conflict.

MCP tools

list_traces, get_trace, find_spans, record_trace, add_score and more, registered per key scope.

LLM-as-judge evaluators

Point FireTrace at any OpenAI-compatible endpoint, define a judge from a template, and run it over a trace or a filtered list.

Security model

Deny-all Firestore rules, Admin-SDK-only access, HMAC key digests, origin checks and a deployment checklist.

Operational notes

What each call costs in Firestore reads, what is cacheable, and how cold starts show up in latency.

Resources

API reference

Every key-authenticated endpoint, with scopes, filters, responses and error codes.

Ingestion API

The wire format in full: field rules, limits, normalization, idempotency, error conventions.

TypeScript SDK

@firetrace/sdk — no runtime dependencies, never throws into your application unless you ask it to.

Source on GitHub

The whole application, MIT licensed. Fork it, audit it, run it yourself.

For AI agents: fetch /api/v1/openapi.json for this deployment's machine-readable contract, browse /api/v1 for the endpoint index, or connect an MCP client to POST /api/mcp with a bearer key. Every page in these docs has a Copy page as Markdown button for pasting into a model.