Skip to content
mcpd
no daemon
Source

Data model

Every entity, field and enumerated value the daemon stores. Generated from the same specification the server, the OpenAPI document and all six SDKs are built from.

Generated file

Everything below this page's introduction is produced by tools/codegen from spec/mcpd.spec.json. Do not edit it by hand. Change the spec, run make gen, and the server, the SDKs and this page all move together. make check fails the build when any of them drift apart.

A hand-written reference describes the code as it was on the day somebody typed it out. This one is emitted from the specification the server's Rust types are compiled from, so a field can only appear here if it exists in the build you are running.

How to read it

There are 39 enumerations and 118 object shapes. The enumerations come first: closed sets of values, such as the 33 kinds a span can be, or the four values a span's status can take. Everything after them is an object.

  • An object with a Stored in line is an entity: a record mcpd keeps, with the table it lives in, its primary key and its indexes.
  • An object without one is a shape that travels in a request or a response, and is never stored under its own name.
  • A field's type links to the type it names. A field marked required must be present; everything else may be omitted, and an omitted field is absent rather than empty.

Why one model, not six

Spans, logs, metrics, scores, feedback and aspect notes are stored separately, but every one of them carries the trace identifier of the run it belongs to, and most of them the span identifier of the step as well. That is the reason they are one model. Without those identifiers you have six separate stores: a log line nothing ties to the run that emitted it, a cost figure that leads back to no prompt, a low score with nothing to read underneath it, a sentence about a run you cannot open. Correlation is what makes the rest of this page worth having.

  • Spans

    What ran, nested, with timings

  • Logs

    Lines the run emitted

  • Metrics

    Numbers, with exemplars back to a run

  • Scores

    Machine evaluation

  • Feedback

    What a person thought

  • Aspect notes

    A sentence a model wrote about the run

trace_id Store Search Export

Index

The document is long. This is a jump table into it.

mcpd stores five signals: spans, logs, metrics, scores and feedback. Every one of them is correlated by trace and span id. This page is generated from spec/mcpd.spec.json, the same file the server and all six SDKs are built from. If it says a field exists, it exists.

Enumerations

SpanKind

What an operation is. The kind drives how a span is rendered in the waterfall, which attributes are meaningful, and which metrics are auto-extracted from it.

Value Meaning
agent_run One agent invocation. Usually the root of a trace.
model_generation One logical generation: everything from the first request to the final answer, including every intermediate step.
model_step One step inside a generation. A generation that calls tools twice has three steps.
model_inference The provider round-trip alone, excluding processors and tool execution. Measure pure model latency here.
model_chunk A single streamed chunk. Recorded as an event, not a duration.
tool_call A tool your code executes.
client_tool_call A tool the calling client executes. The server records the marker; the client returns the detail.
provider_tool_call A tool the model provider executes server-side, such as hosted web search or code execution.
mcp_tool_call A tool reached over the Model Context Protocol.
workflow_run One workflow execution. Usually the root of a trace.
workflow_step One step within a workflow.
workflow_conditional A branch point, with the conditions it evaluated.
workflow_conditional_eval One condition evaluated inside a branch point.
workflow_parallel A fan-out of concurrent branches.
workflow_loop A loop construct: foreach, do-while or do-until.
workflow_sleep A deliberate pause.
workflow_wait_event A wait for an external event, with its timeout.
memory_operation A read from or write to agent memory.
rag_ingestion One ingestion pipeline run: load, chunk, extract, embed, upsert.
rag_embedding One embedding call, possibly batched.
rag_vector_operation Vector store I/O: query, upsert, delete or fetch.
rag_action A RAG-specific transform: chunk, extract metadata or rerank.
graph_action A graph build, traverse, update or prune.
processor_run An input or output processor.
scorer_run One scorer evaluating a target.
scorer_step One stage of a scorer’s pipeline.
guardrail A safety or policy check, and whether it tripped.
retrieval A generic retrieval that is not vector-store specific.
mapping An inline data transform between pipeline stages.
skill_resolution A dynamic resolver deciding which skills an agent may use.
workspace_action A filesystem, sandbox, search or computer-use action.
http_request An outbound HTTP call made on the agent’s behalf.
generic Anything else. Always valid; never inferred.

SpanStatus

The outcome of an operation.

Value Meaning
unset Still running, or the producer never said.
ok Completed successfully.
error Failed. error carries the detail.
cancelled Abandoned deliberately, by a caller or a timeout.

SignalType

The six signals mcpd stores. Every one of them is correlated by trace id, most of them by span id as well, and every one of them passes through the same redaction pipeline.

Value Meaning
span A unit of work with a start and, usually, an end.
log A structured log record.
metric A numeric observation.
score A machine evaluation of a span or trace.
feedback A human evaluation of a span or trace.
aspect_note One sentence recorded against a trace along one aspect. Stored, redacted and retained like the other five, and erased with the subject it belongs to.

LogLevel

Severity, ordered. Filtering is always >=.

Value Meaning
trace Finer than debug. Off by default.
debug Detail useful while diagnosing.
info Normal operation.
warn Recoverable, but worth reading.
error The operation failed.
fatal The process cannot continue.

MetricKind

How a numeric observation aggregates.

Value Meaning
counter Monotonically increasing. Aggregates by sum.
gauge A point-in-time value. Aggregates by last, min, max or mean.
histogram A distribution. Aggregates by bucket, and answers quantiles.

SamplingKind

How mcpd decides which traces to keep.

Value Meaning
always Keep everything. The default, and correct until volume says otherwise.
never Keep nothing. Useful to disable a single project without redeploying.
ratio Keep a fixed fraction, decided once at the root so a trace is never half-recorded.
deterministic Keep a fixed fraction, decided by hashing the trace id. Every collector in a fleet makes the same decision without coordinating.
tail Buffer the trace, then decide once it finishes. Keeps every error and every slow trace regardless of rate.

RedactionMode

What happens to a value a redaction rule matched.

Value Meaning
drop Remove the key entirely, as if it was never sent.
mask Replace the whole value with a fixed token.
partial Keep a documented prefix and suffix, mask the middle. Enough to recognise a value without disclosing it.
hash Replace with a keyed SHA-256 digest. Equal inputs stay equal, so you can still group and join, but the original cannot be recovered.

ScoreSource

Where a score came from. Kept separate from the scorer’s identity so the same scorer can run in more than one context.

Value Meaning
live Produced online, as the traced run happened.
offline Produced later, by scoring stored traces.
experiment Produced by an experiment run against a dataset.
manual Entered by a person, through the API or the UI.

FeedbackKind

The shape of a human judgement.

Value Meaning
thumbs Binary. value is up or down.
rating Ordinal. value is a number inside the scale the producer declares.
correction The right answer. value is text.
tag A label chosen from a set. value is the label.
comment Free prose, with no score attached.

EntityKind

The named thing a span belongs to. Bounded and low-cardinality, so it is safe as a metric label.

Value Meaning
agent An agent.
workflow A workflow.
tool A tool.
model A model.
scorer A scorer.
processor A processor.
pipeline A RAG or data pipeline.
service A plain service with no richer identity.

ExperimentStatus

Where an experiment run has got to.

Value Meaning
pending Created, not started.
running In progress.
succeeded Every item completed.
failed Aborted. error says why.
cancelled Stopped by a person.

AuditResult

Whether authorisation let the request through.

Value Meaning
allowed The key carried the scope the endpoint needs, so mcpd went on to handle the request.
refused Authorisation refused the request before it reached storage. Recording the attempt is the point of the row.

RunStatus

Where a run is. Only running can move; the other four are terminal, and a terminal run holds no deadline, no wait and no live attempt.

Value Meaning
running Making progress, or waiting for something that will move it: a lease, a timer or an event.
succeeded Reached the end of the graph. output holds what the definition’s output template rendered, when it declares one.
failed Ended on an error nothing handled: a step that failed under on_error: fail_run, a spent ceiling, or a cancelled child. error says which.
timed_out Ended because the run’s own deadline passed.
cancelled Ended because a caller asked, through the cancel endpoint or by cancelling an ancestor run.

AttemptStatus

Where one attempt at one step is. Four states are live and four are terminal, and the daemon never shows a live attempt on an ended run.

Value Meaning
pending On disk and offerable to a worker, once its retry timer, if any, has fired.
leased Held by a worker that has not yet posted started.
started Held by a worker that has posted started, so a body is running.
orphaned The lease expired with no heartbeat. Not offerable while the reclaim window is open: a heartbeat inside the window restores the attempt, and a window that closes with no contact requeues it or fails the run, depending on effect and on whether the body had started.
succeeded Finished with an output.
failed Finished with an error, or marked failed because the run ended while the attempt was live.
timed_out The step’s own deadline passed before the attempt ended.
skipped Never ran, because the step’s when guard was false.

StepKind

What a step attempt is an attempt at. task and agent are claimable. A wait row is written by the event that resumes it or the deadline that ends it, so a wait’s output and status read like any other step’s.

Value Meaning
task Claimable work. The worker runs whatever code it registered under this step id.
agent Claimable work whose body is the worker’s own agent loop. Identical on the wire to task; the kind is recorded so the trace and the interface can tell an agent turn from a plain step.
wait A suspension the daemon resumes on an event or ends on a deadline. Never offered to a worker.

ConstructKind

What a run’s cursor names: the construct at its current position in the definition’s steps. Every kind except end is a node kind a definition can declare.

Value Meaning
task Claimable work.
agent Claimable work whose body is the worker’s agent loop.
sleep A relative delay, in milliseconds. No process is alive during it.
sleep_until An absolute delay, to an RFC 3339 instant rendered from a template.
wait_event A suspension addressed by an event name and a key rendered per run.
parallel A fixed fan-out over named branches, each branch one claimable step. Done when every branch has ended.
foreach A fan-out over the items of an array, each item one claimable step, with at most concurrency of them offerable at once.
loop A bounded loop whose body is one claimable step per iteration, ended by until or by max_iterations.
subworkflow A child run of another definition. Its output becomes this step’s output, and a cancel walks down into it.
end The end of the graph. Never declared in a definition: the cursor reaches it when the last step has ended.

Effect

What running a step body twice would cost, which is what the daemon branches on when a lease is lost. Default at_least_once.

Value Meaning
at_least_once Running the body twice is acceptable. A lost lease requeues the attempt. The default.
at_most_once Running the body twice is not acceptable. started must be posted before the body runs, and a started attempt whose lease is lost fails the run rather than running again.

TimerKind

Every deadline the daemon keeps. Each one is a row the tick fires, and each firing is a transaction that moves the run.

Value Meaning
sleep A sleep or sleep_until coming due.
lease_expiry A lease that has not been heartbeated. Firing moves the attempt to orphaned and opens a reclaim window one lease length long.
reclaim The close of the reclaim window an expired lease opened. Firing requeues the attempt or fails the run, depending on effect and on whether the body had started.
run_timeout The whole run’s ceiling, from limits.run_timeout_ms.
step_timeout One attempt’s ceiling, from the node’s step_timeout_ms. It bounds a worker that heartbeats for ever.
wait_timeout A wait_event whose event has not arrived by timeout_ms.
retry The backoff before a requeued attempt becomes claimable. Firing writes the next attempt as pending.

RunEventKind

What a row in a run’s event log records. Every row is written in the transaction that made the change it describes, so a reader that has seen sequence number n has seen every change below it.

Value Meaning
run_started The run was created. Payload: definition_id, definition_version.
run_cursor The cursor moved. Payload: index, construct, and step_key when the construct has one.
step_leased A worker claimed an attempt. Payload: step_key, attempt, worker_id.
step_started A worker posted started. Payload: step_key, attempt.
step_finished An attempt succeeded. Payload: step_key, attempt.
step_failed An attempt failed. Payload: step_key, attempt, class, message, attempts_left.
step_skipped A when guard was false. Payload: step_key.
run_waiting The run reached a wait_event. Payload: step_key, event, key, expires_at.
run_resumed An event resumed a waiting run. Payload: step_key, event_id.
run_ended The run reached a terminal status. Payload: status, and error when there is one.

DefinitionKind

What kind of thing a definition describes.

Value Meaning
workflow A graph of steps the daemon walks. The only kind in version 1: agent definitions arrive with the in-process agent loop, which is not served yet.

EventOutcome

What the daemon did with a published event.

Value Meaning
delivered A waiting run took it and moved, in the same transaction.
parked No run was waiting. The event is held for 24 hours for the next run that reaches a matching wait with accept_parked.
duplicate This event id has been seen before, so nothing was written. The publish endpoint says when a derived id is treated this way.

ConflictReason

Why a worker call about a lease answered 409. Each reason tells the worker something different to do next.

Value Meaning
fenced A later attempt exists, or another lease terminated this attempt. Abandon the work and post nothing more for this lease.
already_applied This lease’s own earlier complete or fail landed and its reply was lost. The step is done; attempt_status says how. Post nothing more.
not_started complete or fail arrived for a leased attempt under at_most_once. A bug in the worker: post started first, and log it loudly.
run_terminal The run has ended. Stop, and stop claiming for this run.

StepErrorClass

How a step failed, which decides whether the daemon retries it.

Value Meaning
retryable Worth another attempt. The daemon retries while attempts remain, at the node’s backoff, and honours retry_after_ms when it is larger.
fatal Another attempt would fail the same way. No retry; the node’s on_error applies.
invalid_input The step’s input cannot be acted on. No retry; the node’s on_error applies.
unservable This worker does not serve the definition version, or does not recognise the step key. The attempt returns to pending after one lease length without spending the retry budget, so another worker can take it.
cancelled The worker gave the attempt up on request. No retry; the node’s on_error applies.

OnError

What happens to a run when a node fails after its retries. Default fail_run.

Value Meaning
fail_run End the run failed, with the step’s error message as the run’s error.
continue Mark the step failed and move on, so a later when guard can read steps.<id>.status and steps.<id>.error.

Backoff

How the delay between retries grows.

Value Meaning
fixed Every retry waits initial_ms.
exponential Each retry waits twice as long as the last, starting at initial_ms and capped at max_ms.

WaitTimeout

What a wait_event node does when its deadline passes with no event. Default fail.

Value Meaning
fail The node fails. Its on_error decides what the run does next.
continue The wait ends and the run moves on to the next node.

ValueKind

The JSON type of a value. Used where mcpd reports the shape of something it holds without returning the thing itself.

Value Meaning
null The value was JSON null.
boolean The value was true or false.
number The value was a JSON number.
string The value was a JSON string.
array The value was a JSON array.
object The value was a JSON object.

MonitorWatch

What a monitor measures over its window.

Value Meaning
error_rate The fraction of runs that failed, 0 to 1.
latency_p95 Run duration at the ninety-fifth percentile, in milliseconds.
cost Total spend across the window.
trace_count How many runs happened. Catches a pipeline that has gone silent.
score_mean The mean of the scorer named by scorer_id.
step_count_divergence How far a run strays from the usual shape of its own definition. This watch sets no threshold in steps, because a step count is never high in absolute terms, only high for that definition. It reads the recent completed runs of one definition version, takes the median step count as the usual shape, and reports a run whose modified z-score against the median absolute deviation exceeds the monitor’s bound, 3.5 by default. The median absolute deviation is used rather than a mean and a standard deviation because it tolerates up to half its sample being outliers, which is the case here: a runaway cohort must not be allowed to redefine normal. Fewer than twelve completed runs and the watch says nothing rather than guessing. A run that takes several times its usual number of steps trips this long before it reaches max_steps, and while its error rate is still zero.
run_repetition How much a run repeats itself. For each run in the window, the highest number of attempts it made at any one node of its definition, counting a retry and a loop iteration of that node alike; the watch reads the mean of that number. One means no node ran twice. A loop that re-enters the same node, and a retry storm, both raise this before either reaches error_rate.

MonitorCompare

Which way a monitor’s threshold is breached.

Value Meaning
above Trip when the value rises above the threshold.
below Trip when the value falls below it. This is the direction that catches a quality regression, and a silent pipeline.

MonitorCondition

Where a monitor or an objective stands. A tripped condition latches: it stays tripped until the number recovers, and too little data never clears it, because silence is not recovery.

Value Meaning
nominal Evaluated, and within its threshold.
tripped Tripped, and not yet acknowledged.
acknowledged Tripped, acknowledged, and still breaching. Returns to nominal on its own when the number recovers.
insufficient Not enough data in the window to judge. Also the state before the first evaluation.

TextSearch

What the q parameter matches on this deployment’s storage backend. Reported by GET /v1/api/capabilities so a search box can say what it searches instead of leaving a reader to infer it from a result set.

Value Meaning
none q is not applied at all. No backend that ships declares this; a backend that did would be telling an interface to hide its search box rather than let somebody type into one that returns everything.
substring q matches anywhere inside a word, so heck finds checkout.
words q matches whole words only, which is what a person typing two words into a search box expects. There is no stemming: refund does not find refunds.

Partitioning

How the storage backend cuts each signal table on its anchor time. It decides whether expiring a day of data is a metadata operation on one part or a row-by-row delete.

Value Meaning
none One table, no partitions. Retention removes rows.
day One partition per day.
month One partition per month.

Facet

A dimension a filter box can offer values for. The set is closed on purpose: each one is a column or a parsed field the daemon keeps a distinct-value list for as signals arrive, so answering is a read of a small table rather than a scan of every span in the retention window.

Value Meaning
entity_kind The kind of thing a step was: an agent run, a tool call, a retrieval, and so on.
entity_name The name of that thing, such as the tool that was called.
service_name The service that reported the signal.
environment The environment it came from, such as production.
tag A tag carried on a run.
metric_name A metric name, for the name box of a chart.
metric_label_key A label key a metric point carries. Project-wide: these are every key seen in the project, not the keys of one named metric.
metric_label_value A value one label key takes. Needs key, because the answer would otherwise be every value of every label mixed together.

DeltaSignal

A signal that can be polled for what has arrived. Only the three that carry a server-set arrival time: a score and a piece of feedback are stamped with the caller’s own timestamp, so a watermark over them would either skip a late row or repeat every row for ever.

Value Meaning
traces The run projection, which is what a run list refreshes from.
spans Steps, for a waterfall that is still filling in.
logs Log lines.

AspectReadiness

How far the daemon has got with one aspect for one entity. It is computed from the rows that are stored and never recorded, so it cannot go stale, and it is per entity and per aspect rather than global: one agent can have goal themes to read while its behaviour notes are still collecting.

Value Meaning
collecting Fewer notes than a clustering pass needs. The floor is 50 notes for one aspect on one entity, and below it the daemon writes no snapshot at all rather than sorting a handful of sentences into themes nobody should draw a conclusion from.
processing Enough notes to cluster, and no snapshot covers this aspect yet. The next pass will write one. Nothing is wrong and nothing needs doing.
ready A snapshot covers this aspect, so themes exist and every theme route answers.

ThemeState

What happened to a theme between the previous snapshot and this one. The daemon matches themes across consecutive snapshots by their wording, so a theme keeps its id while its wording holds, and this records what that match found. It is computed from the two clusterings alone and never from a trace attribute.

Value Meaning
birth The first snapshot this theme appears in.
continue Present in the previous snapshot and in this one, matched to the same id. The ordinary state of a theme that is holding together.
split The previous snapshot held one theme where this one holds several. The history point’s split_from names the theme it came out of.
merge Several themes in the previous snapshot are one theme here. The history point’s merged_from names them.
resurrection Present in an earlier snapshot, absent from the one immediately before this, and back now under its original id. A theme that comes and goes reads as one theme with gaps rather than as several short-lived ones.
death The theme was in the previous snapshot and is in none of this one’s clusters. Reachable only through a theme’s history: a dead theme has no row in the snapshot it died in, so it is never a node in a flow.

ThemeNodeKind

What one node of a theme flow stands for. A stage holds its themes, at most one noise node and at most one overflow node, and the three together account for every trace in the stage, which is what lets the shares add up to one.

Value Meaning
theme One theme. It carries a theme_id, so it can be filtered on, opened and followed through its history.
noise The notes in this stage that joined no theme. Noise is not a failure bucket: it holds rare requests, wording nothing else matched and patterns that have not recurred yet, and a large noise share means varied traffic or too few notes rather than something going wrong. Its notes are read from the examples route with noise as the theme id.
other The themes past theme_limit_per_stage, folded into one node so the stage totals still add up. It carries no theme_id and cannot be selected, filtered on or opened, because it is a presentation fold and not a group of anything.

SnapshotLandmarkReason

Why one snapshot is in a landmark selection. A timeline draws a couple of dozen points over a range that may hold thousands of passes, so the daemon chooses them and says why, rather than handing back the newest few and leaving the reader to assume that is the whole story.

Value Meaning
range_start The oldest snapshot in the requested range. Always included, so a timeline starts where the range does.
range_end The newest snapshot in the requested range. Always included, so a timeline ends at the current state.
time_sample Chosen to spread the remaining points evenly over the range. Sampling is by time and not by ordinal, so an hour of frequent passes does not crowd out a quiet week.

KeyScope

What an account API key may do. The same six scopes the daemon’s configured keys carry, under the same names as the auth value of each endpoint.

Value Meaning
ingest Write signals, and read nothing. The key that lives in every process that emits a span.
read Read stored signals: runs, spans, logs, metrics, scores, analytics and datasets.
write Change stored data: scores, feedback, datasets, experiments and acknowledgements.
admin Delete data and read the audit log. Also accepted where execute or worker is needed.
execute Start, cancel and signal runs, and publish events to them.
worker Claim steps and report their outcome. The key that lives in the process running step bodies.

Entities

InputTokenDetails

Input tokens broken down by how the provider billed them. Every field is optional; absent means the provider did not report it, which is different from zero.

Field Type Description
text u64 Ordinary text tokens: not cached, not audio, not image.
cache_read u64 Tokens served from a prompt cache. Usually billed at a large discount.
cache_write u64 Tokens written into a prompt cache. Usually billed at a premium.
cache_write_5m u64 Tokens written to a five-minute ephemeral cache, where the provider prices TTLs separately.
cache_write_1h u64 Tokens written to a one-hour ephemeral cache.
audio u64 Audio input tokens.
image u64 Image input tokens, including rendered PDF pages.

OutputTokenDetails

Output tokens broken down by kind.

Field Type Description
text u64 Ordinary completion tokens.
reasoning u64 Hidden reasoning tokens. Billed, and usually invisible in the response.
audio u64 Audio output tokens.
image u64 Image output tokens.

TokenUsage

Token counts for one model call. Totals are authoritative; the detail breakdowns explain them and are what cost is actually computed from.

Field Type Description
input_tokens u64 Total input tokens.
output_tokens u64 Total output tokens.
total_tokens u64 Input plus output, when the provider reports a total that differs from the sum.
input_details InputTokenDetails How the input tokens were billed.
output_details OutputTokenDetails How the output tokens were billed.

Cost

What a span cost, in a single currency. mcpd computes this from a price book at ingest and stores the result, so a later price change never silently rewrites history.

Field Type Description
currency string required ISO 4217 code. USD unless the price book says otherwise.
input_cost f64 Cost attributable to input tokens.
output_cost f64 Cost attributable to output tokens.
total_cost f64 The sum, including any meter that is neither input nor output.
price_book string Identifier of the price book used, so a figure can be traced back to the rates that produced it.
is_estimated bool required True when mcpd derived the cost from a price book, false when the producer reported it directly.

ModelParameters

The sampling settings a caller chose for one model call. Recorded because a run nobody can reproduce is a run nobody can debug, and the seed and the sampling settings are what a replay has to match. Every field is what the caller asked for, never what the provider did with it. None of them carries prompt or completion text, so a producer running with payload capture off still records all of them.

Field Type Description
temperature f64 The sampling temperature the caller asked for. Absent when the caller left the provider’s default in place, which is not the same as sending zero.
top_p f64 Nucleus sampling: the caller asked the sampler to consider the smallest set of tokens whose probabilities reach this fraction. Absent when the caller set none.
top_k u32 The caller asked the sampler to consider only this many of the most likely tokens. Not every provider offers it.
max_tokens u64 The ceiling the caller put on output length. An answer that stops in the middle of a sentence usually stopped here, and this is the first field to read when one does.
seed i64 The sampling seed the caller passed. Nothing in mcpd reads it. It is recorded because without it a run cannot be reproduced at all, and a comparison between two runs that differ only in their seed is worth nothing.
stop_sequences string[] The stop sequences the caller supplied. They are short strings the caller wrote, so they are settings rather than payload, and an answer that ends early often ends on one of them. Do not put prompt text here.
has_response_format bool required True when the caller constrained the output to a fixed shape, such as a JSON schema or a grammar. The shape itself is not recorded: it can be large and it is caller-owned content, and what a reader needs is whether the constraint was on.

SpanError

Why a span failed.

Field Type Description
message string required A human-readable message. Required: an error with no message is not actionable.
kind string The error class or code, such as RateLimitError or ECONNRESET.
stack string A stack trace, when the producer captured one.
domain string Which subsystem raised it.
retryable bool Whether the producer considered the failure worth retrying.
details json Structured detail. Passes through redaction like any other payload.

Correlation

The descriptive context a signal belongs to. Every field here is queryable and most are safe as metric labels; the high-cardinality ones (user_id, session_id, request_id) are queryable but blocked from metric labels by the cardinality filter.

Field Type Description
project string required Which project this belongs to. The primary tenancy key.
environment string Deployment environment, such as production or staging.
service_name string The emitting service.
service_version string The emitting service’s version, so a regression can be pinned to a release.
session_id string A user-facing session. Groups many traces into one conversation.
thread_id string A conversation thread within a session.
user_id string The end user. Hashed with a keyed digest at ingest, so runs can still be grouped by customer without storing who the customer is. Filtering on it hashes your query value the same way, so lookup keeps working. Rotating the key deliberately breaks the link.
resource_id string A tenant or workspace inside the calling application.
run_id string The producer’s own identifier for this run.
request_id string The inbound request that caused this work.
experiment_id string Set when the run was produced by an experiment.
release string A release or git sha, for regression attribution.

A reference from this span to another, in this trace or a different one. Links express causality that parentage cannot: a batch job caused by twenty requests has twenty links and one parent.

Field Type Description
trace_id string required The linked trace.
span_id string The linked span, when the link points at one rather than a whole trace.
kind string Why the link exists, such as follows_from, retry_of or replay_of.
attributes json Anything else about the relationship.

Page

Cursor pagination. Cursors are opaque and stable; never construct one by hand.

Field Type Description
next_cursor string Pass as cursor to fetch the next page. Absent on the last page.
has_more bool required Whether another page exists.
total u64 Total matching rows, when the backend could count them cheaply.

IngestBatch

One batch of signals sent to the native ingest endpoint. Any combination of the six signals may appear in a single batch; mcpd sorts them out. Supplying ingest_key makes the whole batch idempotent, so a client that retries after a timeout never double-counts.

Field Type Description
spans Span[] Spans to record.
logs LogRecord[] Log records to record.
metrics MetricPoint[] Metric observations to record.
scores Score[] Machine evaluations to attach.
feedback Feedback[] Human evaluations to attach.
aspect_notes AspectNote[] Aspect notes to record. A note is a sentence about a trace, so it arrives with that trace’s other signals, under the same idempotency key and through the same redactor.
ingest_key string Idempotency key for this batch. Replaying a batch with a key mcpd has already accepted is a no-op that still returns 200.

IngestError

Why one signal in a batch was refused.

Field Type Description
signal SignalType required Which signal kind the failure was in.
index u32 required Position within that signal’s array, so a client can find the offending record.
message string required What was wrong, in terms the sender can act on.

IngestResult

What mcpd did with a batch. Counts are per signal, and rejected is never silently zero: a signal mcpd could not store is always reported with a reason.

Field Type Description
accepted u32 required How many signals were stored.
rejected u32 required How many were refused.
duplicate u32 required How many were recognised as a replay of an already-accepted batch and skipped.
errors IngestError[] One entry per rejected signal, naming what was wrong and where.

TraceListResponse

A page of traces.

Field Type Description
traces Trace[] required The matching traces, in the requested order.
page Page required Cursor and totals.

TraceDetail

One trace and everything attached to it.

Field Type Description
trace Trace required The trace projection.
spans Span[] required Every span, ordered by start time then by depth, so a waterfall renders without sorting.
logs LogRecord[] Logs, when include asked for them.
scores Score[] Scores, when include asked for them.
feedback Feedback[] Feedback, when include asked for them.

SpanListResponse

A page of spans.

Field Type Description
spans Span[] required The matching spans.
page Page required Cursor and totals.

LogListResponse

A page of log records.

Field Type Description
logs LogRecord[] required The matching records.
page Page required Cursor and totals.

MetricSeriesPoint

One bucket of an aggregated series.

Field Type Description
timestamp timestamp required Start of the bucket.
value f64 required The aggregated value.
count u64 How many raw observations fell in this bucket. Distinguishes a genuine zero from no data.
exemplar_trace_id string A trace that contributed to this bucket, so a spike on the chart opens the run that caused it.

MetricSeries

One line on a chart: a single label combination over time.

Field Type Description
labels json The label values that identify this series.
points MetricSeriesPoint[] required Bucketed values, oldest first.

MetricSeriesResponse

An aggregated metric query result.

Field Type Description
name string required The metric queried.
unit string UCUM unit, for axis labelling.
bucket string required The bucket width actually used, which may differ from the request when mcpd chose it.
series MetricSeries[] required One entry per label combination.

ScoreListResponse

A page of scores.

Field Type Description
scores Score[] required The matching scores.
page Page required Cursor and totals.

FeedbackListResponse

A page of feedback.

Field Type Description
feedback Feedback[] required The matching feedback.
page Page required Cursor and totals.

LatencyStats

Latency distribution over a window, in milliseconds.

Field Type Description
p50 f64 Median.
p90 f64 Ninetieth percentile.
p95 f64 Ninety-fifth percentile.
p99 f64 Ninety-ninth percentile.
mean f64 Arithmetic mean. Reported alongside quantiles, never instead of them.
max f64 Slowest observation in the window.

GroupedStat

One row of a grouped breakdown.

Field Type Description
key string required The group’s value, such as a model id.
count u64 required How many runs fell in this group.
error_count u64 required How many of them failed.
total_cost f64 Spend attributable to this group.
input_tokens u64 Input tokens for this group.
output_tokens u64 Output tokens for this group.
latency LatencyStats Latency distribution within this group.

AnalyticsSummary

Headline numbers for a window. One request backs the overview screen.

Field Type Description
from timestamp required Start of the window.
to timestamp required End of the window.
trace_count u64 required Runs started in the window.
span_count u64 required Steps recorded in the window.
error_count u64 required Runs that failed.
error_rate f64 required Failures as a fraction of runs, between 0 and 1.
latency LatencyStats required Run duration distribution.
input_tokens u64 required Input tokens consumed.
output_tokens u64 required Output tokens produced.
total_cost f64 required Total spend in the window.
currency string required Currency of total_cost.
groups GroupedStat[] Per-group rows, when group_by was supplied.

CostBucket

Spend in one time bucket for one group.

Field Type Description
timestamp timestamp required Start of the bucket.
key string required The group this row belongs to.
total_cost f64 required Spend in this bucket.
input_tokens u64 Input tokens in this bucket.
output_tokens u64 Output tokens in this bucket.

CostBreakdown

Where the money went.

Field Type Description
from timestamp required Start of the window.
to timestamp required End of the window.
currency string required Currency of every figure in this response.
total_cost f64 required Total spend across the window.
groups GroupedStat[] required Totals per group, largest first.
series CostBucket[] A time series, when bucket was supplied.

DeleteResult

What a deletion removed.

Field Type Description
deleted u32 required How many records were removed, across every signal.

HealthStatus

Whether the server is alive and able to serve.

Field Type Description
status string required ok or degraded.
version string required The mcpd version serving this request.
spec_version string required The data-model spec version this build implements. A client can refuse to talk to an incompatible server.
uptime_seconds u64 required How long this process has been running.
storage string Which storage backend is configured.
detail string What is degraded, when something is.
signup bool True when this daemon offers open sign-up: anyone may create an account at POST /v1/api/account/signup and log in at POST /v1/api/account/login. Absent when it does not, which is the default. The interface reads this to decide whether to show its sign-up and log-in screens.

ScorerInfo

One scorer mcpd can run, as described to a caller.

Field Type Description
id string required Stable identifier, recorded on every score it produces.
description string required What it measures, in one line.
needs_config bool required Whether it needs settings to mean anything. A scorer that needs an expected answer cannot run without one.

ScorerConfig

Settings shared by the scorers that need them. Every field is optional; a scorer that needs one and does not get it declines instead of guessing.

Field Type Description
needle string Text the answer must contain, for contains.
expected string The expected answer, for exact_match.
max_ms f64 A budget in milliseconds, for latency_budget.
max_cost f64 A budget in currency units, for cost_budget.
max_tool_calls u32 The most tool calls a healthy run should make, for tool_efficiency.
span_kind SpanKind The kind of span that must be present in the run, for span_present.
span_name string The span name that must be present in the run, for span_present. Matched exactly against Span.name.
tool_name string The tool that must have run and ended ok, for tool_succeeded. Matched exactly against the name of a span whose kind is tool_call, client_tool_call, provider_tool_call or mcp_tool_call.
case_sensitive bool required Case sensitivity for text comparisons.

ScoreRunRequest

A request to run scorers over runs that are already stored.

Scoring reads stored runs rather than running inline, so an evaluation can never add latency to the request it is judging, and a scorer that fails can never fail a user’s request.

Field Type Description
project string Which project’s runs to score. Defaults to the server’s default project.
trace_ids string[] required Score exactly these runs. Takes precedence over the window.
from timestamp Otherwise, score runs started after this time.
limit u32 Most runs to score in one request, newest first, when trace_ids is empty. Unset, it is 100 for deterministic scorers, and for a pass with a judged scorer it is as many runs as fit in the request deadline. A judged pass that names more runs than fit, through limit or trace_ids, is refused with the cap.
scorers string[] required Which scorers to run, by id. Empty means every scorer that needs no configuration.
config ScorerConfig Settings for the scorers that need them.
source ScoreSource What to record as the origin of these scores.
dry_run bool required Report what would happen and store nothing.

SkippedScorer

A scorer that declined to answer on one run.

Field Type Description
scorer_id string required Which scorer declined.
trace_id string required Which run it declined on.
reason string required Why. Never silent: a scorer that cannot answer is more interesting than one that returned a number.

ScoreRunResult

What one scoring pass did.

Field Type Description
traces_scored u32 required Runs looked at.
scores_written u32 required Scores produced and stored.
skipped SkippedScorer[] required Every scorer that declined, with its reason.
scores Score[] required The scores themselves, so the caller sees them without a second read.

ScorerDelta

How one scorer moved between two experiments.

Field Type Description
scorer_id string required Which scorer.
baseline_mean f64 Mean on the baseline, when it ran there.
baseline_n u64 required Number of baseline observations.
candidate_mean f64 Mean on the candidate.
candidate_n u64 required Number of candidate observations.
delta f64 Candidate minus baseline.
decidable bool required Whether the sample is large enough for the delta to mean anything. A deliberately blunt rule rather than a significance test: below twenty observations on either side, mcpd says the comparison is not decidable rather than dressing noise up as a finding.

ExperimentComparison

Two experiments, compared.

The point of an experiment is the comparison, so this reports the delta per scorer beside the sample sizes. A mean that moved on four runs is noise, and printing the counts next to it is what stops it being read as a result.

Field Type Description
baseline string required The baseline’s identifier.
candidate string required The candidate’s identifier.
scorers ScorerDelta[] required One row per scorer present on either side.
regressed bool required Whether any decidable scorer moved down.

CaptureRequest

A request to turn stored runs into dataset cases.

Field Type Description
trace_ids string[] required The runs to capture. Each becomes one item, keeping its trace id so anyone can go back and read what actually happened.
use_output_as_expected bool required Record each run’s own output as the expected answer. Leave this off for a run that went wrong: capturing a bad answer as the reference would enshrine the bug.

CaptureResult

What a capture added to a dataset.

Field Type Description
captured u32 required Items added.
requested u32 required Runs asked for. A gap means a run was not found.

ItemsAdded

How many cases a dataset gained.

Field Type Description
added u32 required Items added.

ErasureResult

What an erasure request removed, counted per signal. The subject is not echoed back: it has been erased, and repeating it in a response that may be logged on the way out would write it down again.

Field Type Description
project string required The project the erasure ran in. No other project was read or touched.
traces u32 required How many runs were removed.
spans u32 required How many spans were removed, with the prompts and completions they carried.
logs u32 required How many log lines were removed.
metrics u32 required How many metric points were removed.
scores u32 required How many scores were removed.
feedback u32 required How many pieces of feedback were removed.
deleted u32 required The total across every signal, so a caller that wants one number does not add them up.
runs_erased u32 How many durable runs were removed from the run store, each with its step attempts, event log, deadlines, waits, the events it consumed and any span still queued for the trace store. A run is matched on the user_id in its correlation, as it was started with it, and removed with the whole tree of subworkflow runs it belongs to. Not part of deleted, which counts the trace store. Absent when this daemon does not serve runs.
runs_in_flight u32 How many runs attributed to this person were left untouched because a run in their tree has not ended. Removing a live run would corrupt the workflow it is part of, so these are counted rather than skipped in silence, and note names the root runs. Cancel them, or let them end, then send the erasure again. Absent when this daemon does not serve runs.
aspect_notes_erased u32 How many aspect notes were removed, matched by the run they describe and by the person stamped on them. Not part of deleted. Absent only from a store that holds no notes.
dataset_items_erased u32 How many dataset cases captured from an erased run were emptied in place. They are not deleted, because a case was kept on purpose, so they are not part of deleted.
datasets string[] required The datasets those emptied cases sit in, so the reply can say where they are.
deletes_pending bool True when the storage backend applies the deletes after this reply is sent, as ClickHouse does, so the counts say what matched rather than what has already left the disk.
unmatchable_identifiers u32 How many stored identifiers this request could not have matched, because they were written in the other form, hashed or plain, from the one given. A number above zero means the counts may not be the whole answer.
note string Why the numbers above may not be the whole answer, in sentences an operator can repeat. Absent when they are the whole answer.

DefinitionDocument

A definition as you post it: the graph, and the id and version to store it under. The body is validated at write time against spec/definition.schema.json; a body the walker cannot run is refused with a 400 naming the field, and unknown fields are refused, so a guard that is spelled wrong is an error and not a run that skips its guard.

Field Type Description
id string required The definition’s identifier within its project. Matches ^[a-z][a-z0-9_]*$.
kind DefinitionKind What kind of definition. Absent means workflow, the only kind version 1 serves.
version u32 The version to store the body under. Absent asks the daemon to choose: the version whose stored body is identical, or one past the highest stored. A named version that is free is taken; one held by an identical body is confirmed; one held by a different body is a 409, because a stored version never changes.
body json required The document itself: input_schema, limits, steps and output. The definition schema names every field.

DefinitionVersion

Where a posted definition landed.

Field Type Description
id string required The definition’s identifier.
version u32 required The version the body is stored under.
assigned bool required True when the daemon chose the number because the body was new. False when the body already existed at this version, or you named the version yourself.

RunCursor

The frontier of a run: which position in the definition’s steps it has reached, and what sits there.

Field Type Description
index u32 required The index into steps, or their count at the end of the graph.
construct ConstructKind required What is at that position.
step_key string The step this position belongs to, when the construct has one. A fan-out names its parent step; the branch and item keys are on the attempts.

RunTimer

A deadline the daemon holds for a run.

Field Type Description
run_id string required Which run.
step_key string The step the deadline belongs to. Absent for a run-level deadline.
kind TimerKind required Which deadline.
due_at timestamp required When it fires.

RunWait

A suspension waiting for an event.

Field Type Description
run_id string required Which run.
step_key string required The wait_event step that is waiting.
event string required The event name an inbound event must match.
key string required The rendered key that picks this run out of every run waiting on that event.
expires_at timestamp required When waiting stops and the node’s on_timeout applies.

RunDetail

One run and everything the daemon holds for it.

Field Type Description
run Run required The run.
attempts StepAttempt[] required Every attempt, in the order they were written.
timers RunTimer[] required The deadlines currently held. Empty once the run has ended.
waits RunWait[] required The suspensions currently open. Empty once the run has ended.

RunListResponse

A page of runs.

Field Type Description
runs Run[] required The matching runs, newest first.
page Page required Cursor and totals.

RunEventPage

A page of a run’s event log.

Field Type Description
events RunEvent[] required The rows after the requested sequence number, in order.
has_more bool required Whether rows beyond the last one here existed when the page was read. A run that is still moving will write more.

One edge of a cause chain: an ancestor step, and the template path through which the step below it read that ancestor’s output. An ancestor read through two paths appears twice, once per path.

Field Type Description
step_key string required The ancestor step, keyed the way the journal keys it: refund/issue, classify[3], poll[7]/classify.
status AttemptStatus required Where the ancestor’s latest attempt ended.
attempts u32 required How many attempts were made at the ancestor. More than one on a step that ended succeeded is worth reading: it got there, but not the first time.
template_path string required The path as the definition writes it inside ${...}, such as steps.classify.output.kind. The definition declares this edge, so the chain is read rather than inferred from timing.
consumed_by string required The step key that read this ancestor through template_path. On a direct edge it is the step the answer is about; deeper in the chain it is another ancestor.
depth u32 required How far this ancestor sits from the step the answer is about. 1 is read directly, 2 is read by something the step read directly, and so on.
silent_producer bool required True when this ancestor ended succeeded and every step between it and the failed step ended succeeded too. Nothing on that path is marked failed, so no status code points at it and the value it produced is the suspect. It is not the same as status being succeeded: an ancestor sitting behind a step that did fail is not a silent producer, because that failure already explains the run.
value_kind ValueKind The JSON type template_path resolved to on the ancestor’s stored output. The value itself is never returned, only its shape. Absent when there is no output to read, which is the case for a step that never ran and for one that failed.
value_empty bool True when that value was null, an empty string, an empty array or an empty object. A step that ended succeeded and handed the step below it nothing is the failure a status code cannot catch, and this is the field that shows it. Absent for the same reason value_kind is.

StepCause

Why one step of a run failed, answered by walking backwards along the data dependencies its definition declares. The daemon reads the run journal and the pinned definition version, both of which it has to hold to render ${...} templates at all, so this answer is available with span payload capture off and with no model, prompt or completion involved.

Field Type Description
run_id string required The run this answer is about.
step_key string required The step this answer is about, as it was asked for.
status AttemptStatus required Where that step’s latest attempt ended.
attempts u32 required How many attempts were made at it.
error StepError Why the latest attempt failed, when it did. Absent on a step that did not fail, because the endpoint answers for any step, not only a failed one.
chain CauseLink[] required The ancestors whose output this step consumed, directly or transitively. Ordered by depth ascending, then by the order the paths appear in the definition, so two calls against one run return the same list in the same order. Empty when the step reads nothing but the run input.
truncated bool required True when the walk stopped at max_depth with ancestors left unvisited, so chain is a prefix of the answer rather than the whole of it.

StartRunRequest

A request to start a run.

Field Type Description
project string Which project. A selector: refused with 403 on a mismatch with the key’s grant, filled in for a single-project key, and a 400 when a multi-project key names none.
definition_id string required Which definition to run.
version u32 Which version. Absent pins the highest stored version at this moment, and the run records it.
input json The run’s input. Validated against the definition’s input_schema when it declares one; a failure is a 400.
correlation Correlation Context to store on the run and copy onto every span the daemon writes for it. project and run_id inside it are ignored: the run’s own values win.
idempotency_key string Makes the start idempotent per project for 24 hours: a repost returns the same run and starts nothing, and two simultaneous posts of one key produce one run.

ClaimRequest

A worker asking for work.

Field Type Description
project string Which project. A selector: refused with 403 on a mismatch with the key’s grant, filled in for a single-project key, and a 400 when a multi-project key names none.
worker_id string required Who is asking. Recorded on the attempt as lease_owner and in the run’s event log. Nothing else about the worker is stored.
max u32 required The most leases to hand back, 1 to 100.
wait_ms u32 required How long to hold the request open waiting for work, 0 to 60000 milliseconds. The daemon holds a claim for at most four fifths of its request timeout, 24000 at the default 30 seconds, so a longer wait is answered early rather than cut off with a 503. An empty answer means nothing became runnable in time: reissue the claim.
run_id string Restrict the claim to one run, which is how a script drives its own run inline.

StepLease

A step handed to a worker. Every later call about the attempt is addressed by lease_id.

Field Type Description
lease_id string required The address of every later call about this attempt.
run_id string required Which run.
project string required Which project the run belongs to.
definition_id string required Which definition the run is executing.
definition_version u32 required Which version. Fail the lease with class unservable if you do not serve it.
step_key string required The step to run. A worker that does not recognise it fails the lease with class unservable rather than guessing.
kind StepKind required Whether this is a plain task or an agent turn.
attempt u32 required Which attempt this is. The fence: a call about this lease is refused once a later attempt exists.
effect Effect required Under at_most_once, post started and wait for its 200 before running the body.
expires_at timestamp required When the lease lapses. Heartbeat every heartbeat_ms to keep it.
deadline_ms u64 The step’s own timeout, when the node declares one. It bounds the attempt whatever the heartbeats say.
idempotency_key string required A key stable across every attempt of this step: the SHA-256 of run_id:step_key. Pass it to any downstream call that can deduplicate.
input json The node’s input template rendered for this attempt, or the run’s input when the node declares none.
prior json required The ended attempts of earlier steps, keyed by step id: { status, output?, error? } for the latest attempt of each, or { status: skipped }. Only ended attempts appear.
state json The run’s state object as it stands.
correlation Correlation required The run’s correlation, with run_id set. Put it on every span you record inside this step.
trace_id string required The run’s trace. Set it on every span you record inside this step.
span_id string required The span the daemon has already opened for this attempt. Set it as parent_span_id on your own spans, so a model call or a tool call nests under the step.
parent_span_id string required The run’s root span, which this attempt’s span hangs off.

ClaimResponse

What a claim answered.

Field Type Description
leases StepLease[] required Zero to max leases. Empty when nothing became runnable within wait_ms.

LeaseState

What started and heartbeat answer.

Field Type Description
expires_at timestamp required The lease’s new expiry.
reclaimed bool required True when this call found the attempt orphaned and restored it to the state it came from.

StepUsage

Token usage a worker reports on complete. The daemon prices it from its price book and charges the attempt, the run and the root of the run tree.

Field Type Description
input_tokens u64 Total input tokens.
output_tokens u64 Total output tokens.
total_tokens u64 Input plus output, when the provider reports a total that differs from the sum.
input_details InputTokenDetails How the input tokens were billed.
output_details OutputTokenDetails How the output tokens were billed.
model string The model that produced the usage, which the price book needs to price it.
provider string The provider that served the model.

StepResult

What a worker posts to complete a step.

Field Type Description
output json The step’s output. Later steps read it as steps.<id>.output.
state json An object to merge shallowly into the run’s state.
usage StepUsage Token usage to price and charge.

StepError

Why a step failed, as the worker reports it.

Field Type Description
class StepErrorClass required What kind of failure. Decides whether the daemon retries.
message string required What went wrong, for the person reading the run.
details json Structured detail. Stored on the attempt.
retry_after_ms u64 The least the daemon should wait before the next attempt, when the worker knows: a rate limit’s reset, say. Honoured when it is larger than the backoff. At most 315360000000 (3650 days), the bound on every duration in a definition; a larger value is a 400 naming this field.

StepFailure

What a worker posts to fail a step.

Field Type Description
error StepError required Why it failed.

StepAck

What complete and fail answer.

Field Type Description
run_status RunStatus required Where the run is after this call.
next string required claim when more work is runnable now, idle otherwise.
attempts_left u32 After a fail: how many attempts the node’s retry budget still allows.

StepConflict

The body of a 409 from a worker call. reason says what is true and what the worker should do next.

Field Type Description
error string required Always conflict.
reason ConflictReason required Which conflict.
message string required The reason, in words.
attempt u32 required The attempt the lease addressed.
attempt_status AttemptStatus Where that attempt is, when it still exists. Under already_applied this says how the step ended.
run_status RunStatus required Where the run is.

PublishEventRequest

An event for whichever run is waiting for it.

Field Type Description
project string Which project. A selector: refused with 403 on a mismatch with the key’s grant, filled in for a single-project key, and a 400 when a multi-project key names none.
event string required The event name, matched against open waits.
key string required Matched against the rendered key of each waiting run. Delivered to the matching run whose wait expires soonest.
payload json Becomes the waiting step’s output.
event_id string Your own id for this event. A supplied id seen before answers duplicate and writes nothing, whether or not a run is waiting. Absent, the daemon derives one from project, event, key and payload, and a derived id counts as a duplicate only when no run is waiting, because two identical approvals for two runs on one key are two events. Send your own id from any handler that can be retried.

EventReceipt

What publishing an event did.

Field Type Description
outcome EventOutcome required Delivered, parked or duplicate.
event_id string required The id used, supplied or derived.

SignalRequest

An event for one run, addressed by run id rather than by key. The daemon reads the run’s open wait on event, takes its key, and routes the event as a published one.

Field Type Description
event string required The event name. The run must hold an open wait on it.
payload json Becomes the waiting step’s output.
event_id string Your own id for this event, deduplicated as on a published event.

WorkerRegistration

A worker announcing the definitions it serves.

Field Type Description
project string Which project. A selector: refused with 403 on a mismatch with the key’s grant, filled in for a single-project key, and a 400 when a multi-project key names none.
worker_id string required Who is registering. Not stored: dispatch reads the runnable set, never a worker table.
definitions DefinitionDocument[] required Each is stored under the versioning rule, exactly as if posted to the definitions endpoint.

WorkerRegistered

What registering answered.

Field Type Description
worker_id string required The id you registered with.
registered DefinitionVersion[] required Where each definition landed, in the order posted.
lease_ttl_ms u64 required How long a lease lasts without a heartbeat.
heartbeat_ms u64 required How often to heartbeat a held lease: a third of lease_ttl_ms.

Monitor

A saved condition the daemon evaluates on a schedule. Monitors are configured in the daemon’s configuration file, not through the API.

Field Type Description
id string required Stable identifier.
name string required What a person should read when it trips.
project string required Which project it watches.
watch MonitorWatch required What it measures.
scorer_id string Which scorer, when watch is score_mean.
compare MonitorCompare required Which direction trips it.
threshold f64 required The threshold.
window_seconds u32 required How far back each evaluation looks.
min_samples u64 required The fewest runs the window must contain before the monitor will trip. Without it a single failed run in a quiet hour is a 100% error rate, and the first thing anyone does with an alert that cries wolf is mute it.
webhook string Where to POST when it trips or clears. Set as a full URL in the config file; the API returns only its host, because a webhook URL is often its own credential.
enabled bool required Whether it is evaluated at all.

MonitorReading

One evaluation of a monitor.

Field Type Description
monitor_id string required Which monitor.
at timestamp required When it was evaluated.
value f64 The measured value, when there was enough data to measure one.
samples u64 required How many runs the window held.
condition MonitorCondition required The resulting condition.
detail string required What a person should read.

MonitorState

A monitor and the condition it is latched in.

Field Type Description
monitor Monitor required The monitor.
condition MonitorCondition required Its current condition.
last MonitorReading The most recent reading. Absent before the first evaluation.
tripped_at timestamp When it first tripped, while it stays tripped.

Slo

A service level objective: the fraction of runs that must succeed over a window. A monitor asks whether a number is over a line right now. An objective asks whether the promise is going to be missed and how fast the error budget is burning, over two windows at once, so a two minute blip and a slow leak get the different answers they need. Objectives are configured in the daemon’s configuration file, not through the API.

Field Type Description
id string required Stable identifier.
name string required What a person should read.
project string required Which project it covers.
objective f64 required The fraction of runs that must succeed, between 0 and 1. 0.995 is ninety-nine and a half percent of runs.
window_days u32 required How long the promise is measured over.
min_samples u64 required The fewest runs a burn window must hold before it may trip.
webhook string Where to POST on a transition. Set as a full URL in the config file; the API returns only its host, because a webhook URL is often its own credential.
enabled bool required Whether it is evaluated at all.

SloReading

One evaluation of an objective.

Field Type Description
slo_id string required Which objective.
at timestamp required When it was evaluated.
success_rate f64 The observed success rate over the whole window.
budget_remaining f64 The fraction of the error budget still unspent. Negative once it is overspent, which says by how much the promise was missed.
burn_fast f64 The burn rate over the one hour window. A rate of 1 spends the whole budget exactly over the whole window.
burn_slow f64 The burn rate over the six hour window. Both windows must exceed their trip rate together before the objective trips, which is what stops a brief spike paging anyone.
samples u64 required Runs in the whole window.
condition MonitorCondition required The resulting condition.
detail string required What a person should read.

SloState

An objective and the condition it is latched in.

Field Type Description
slo Slo required The objective.
condition MonitorCondition required Its current condition.
last SloReading The most recent reading. Absent before the first evaluation.
tripped_at timestamp When it first tripped, while it stays tripped.

StoreCapabilities

What the storage backend behind this daemon can and cannot do. Three backends implement one storage trait and they are not equal: a reader on ClickHouse who searches for CAFE with an acute and finds nothing has no way to learn that this backend folds ASCII case and no more. Read this once at connect time and offer only what the deployment answers. Every field is named after the capability an endpoint names when it refuses with 501, so the two can be compared without translation.

Field Type Description
backend string required Which backend this describes: sqlite, postgres or clickhouse. The same string /healthz reports as storage.
text_search TextSearch required What the q parameter matches.
unicode_case_folding bool required Whether q folds case for letters outside ASCII. False means a search for CAFE with an acute does not find the lowercase form, which a reader otherwise meets as an empty result they cannot explain.
immediate_deletes bool required Whether a delete is on disk when the call returns. False means the backend accepted the delete and applies it afterwards, so a count says what matched rather than what has already gone.
discovery bool required Whether GET /v1/api/discovery answers. False means a filter box has to be filled from what is already on screen.
discovery_label_values bool required Whether the metric_label_value facet is answerable, not only the keys. Separate because the values are the expensive half: a key set is small and bounded, and a value set is not.
delta_polling bool required Whether GET /v1/api/deltas answers, so a list refreshes from a cursor rather than by running the whole query again.
per_signal_retention bool required Whether retention can carry a different window per signal. False means one window covers all five.
declarative_retention bool required Whether the window can be handed to the database engine, so nothing has to run a delete on a timer.
partitioning Partitioning required How the signal tables are cut on their anchor time.
partition_drop_retention bool required Whether a retention pass can drop a whole partition instead of deleting rows one at a time.
idempotent_schema_init bool required Whether a start with nothing to apply issues no schema statement at all. False means every process replays the schema on every boot, which is harmless on one node and is metadata churn across a replicated deployment.
theme_analysis bool required Whether this deployment answers the Intelligence endpoints. False means the store holds no note or theme tables and every one of those routes refuses with 501 naming this capability, so a client decides once whether to offer the feature rather than showing a reader an empty page they cannot account for. Every backend that answers gives the same themes: the clustering runs in the daemon over stored notes and not in SQL, so there is nothing here for a backend to do differently.

DiscoveredValue

One value a filter dimension takes in a project.

Field Type Description
value string required The value, exactly as it is stored, so it can be sent straight back as a filter with no further mapping.
last_seen timestamp required When mcpd last received a signal carrying it. This is arrival time and not the signal’s own timestamp, so a backfill of last year’s data does not make a value look current. Carried so a caller can rank down or grey out a value nothing has reported for a month. Values are ordered by value and not by this: a list somebody reads alphabetically is stable between polls, and one ordered by recency reshuffles under them as data arrives.

DiscoveryResponse

The values one filter dimension takes, for a filter box. Answered from a distinct-value list the daemon maintains as signals arrive, not by scanning the signal tables, because the question is asked on every keystroke and the tables it would otherwise scan are the largest thing the deployment holds.

Field Type Description
facet Facet required Which dimension was asked for.
key string The label key the values belong to, echoed back for metric_label_value. Absent for every other facet.
values DiscoveredValue[] required The values, ordered by value, ascending.

DeltaResponse

What has arrived since a cursor. A poll is not a page: paging walks a fixed result set backwards in the sort you chose, and polling walks forwards in arrival order and never ends, so the two cursors are different things and are deliberately not interchangeable. Rows come back oldest arrival first, so applying them in order to a list you already hold gives the same result as having listed afresh.

Field Type Description
signal DeltaSignal required Which signal these rows are. Exactly one of the three arrays below is present, and it is this one.
cursor string required Where the next poll resumes. Always present, and unchanged from the request when nothing came back: moving it forward on an empty poll is what would drop a row that lands a moment later with an earlier arrival time. Treat it as meaningless, and send back the one you were given.
has_more bool required True when the page filled and more is waiting, so a caller draining a backlog polls again at once rather than waiting for its next tick.
traces Trace[] The runs that arrived, when signal is traces.
spans Span[] The steps that arrived, when signal is spans.
logs LogRecord[] The log lines that arrived, when signal is logs.

AspectLimits

The bounds a clustering pass runs under. They are published because each one changes what a reader is looking at: a floor that has not been reached means no themes at all rather than an empty result, and a ceiling that has been reached means every share is a share of what was read. They are copied from the divergence watch, which is the one windowed statistic mcpd already ships. pass_armed is the first of them to read: the other three describe what a pass would do, and it says whether one runs.

Field Type Description
min_notes_to_cluster u64 required How many notes one aspect on one entity needs before a pass runs at all, 50 by default. Below it the aspect reads collecting and no snapshot is written.
max_notes_per_pass u64 required The most notes one pass reads, 20,000 by default, taken newest first. A window holding more is clustered from the newest of them and its snapshot is marked capped.
page_size u32 required How many notes the pass reads at a time while it fills a window, 500 by default. It bounds what the pass holds, not what it covers.
max_aspects u32 required How many aspects one project may define, the four built-ins included. It is configuration and is published here so a client that lets an operator add an aspect knows when to stop instead of finding out from a refusal.
pass_armed bool required Whether this deployment runs the clustering pass at all. False means no snapshot will ever be written however many notes arrive, so every aspect stays collecting or processing for ever and no theme route will have anything to answer with. It is configuration and not a fault, and it is not the same thing as the 501 the theme routes give: that says the storage backend cannot hold the tables, which nobody can switch on, while this says nobody has switched the pass on yet. It is published here rather than on every progress row because it is one fact about the deployment, and a client reads this route once to learn what the aspects are called.

AspectCatalogue

Every aspect a project describes traces along, with the bounds the clustering pass runs under. The four built-ins are rows in the same table as anything you define, so there is no fallback list inside a client that can quietly disagree with this one.

Field Type Description
aspects Aspect[] required The aspects, in sort_order. Disabled aspects are included, because notes already recorded under one stay readable and a name that vanished from this list would make them look like a mistake.
limits AspectLimits required The bounds a pass runs under in this deployment.

EntityIndexRow

One entity that has aspect notes, with the roll-up a list needs to be worth reading. A row appears as soon as the first note is recorded, before any theme exists, because an empty screen with a count moving on it is a different thing from an empty screen and a reader has to be able to tell them apart.

Field Type Description
entity_kind EntityKind required The kind of thing this row is about.
entity_name string required Its name.
trace_count u64 required How many of its traces carry at least one note. This is not the entity’s trace count: writing a note lags ingestion, so a busy agent can show far fewer here.
note_count u64 required How many notes are recorded about it.
enabled_aspects u32 required How many aspects are switched on for this entity’s project.
ready_aspects u32 required How many of those have themes to read. The two numbers read as 2 of 4 and are the honest answer to whether this row is worth opening yet.
readiness AspectReadiness required The row’s overall state, which is the least advanced of its enabled aspects, so a row says processing while any one of them is still short of a snapshot. It never reports the best case.
last_note_at timestamp required When the newest note about this entity was recorded, so a reader can tell a list that is filling from one that stopped.
latest_snapshot_at timestamp The cutoff of this entity’s newest snapshot. Absent until a pass has written one, which is the ordinary state of a row that is still collecting.

EntityIndexResponse

A page of the entities that have aspect notes.

Field Type Description
entities EntityIndexRow[] required The matching entities, most recently written to first.
page Page required Cursor and totals.

AspectProgressRow

How far one aspect has got for one entity. Counted from stored notes, so it is what has happened and not a projection of what will.

Field Type Description
aspect string required Which aspect this row is about.
readiness AspectReadiness required The state this aspect is in for this entity.
note_count u64 required How many notes are recorded along it.
trace_count u64 required How many distinct traces those notes were written about.
notes_needed u64 required How many more notes this aspect needs before a pass will run. Zero once the floor is reached, which is what turns a wait into a number a reader can watch.
last_note_at timestamp When the newest note along this aspect was recorded. Absent when there are none.

AspectProgress

What the daemon has recorded for one entity, aspect by aspect. This is the answer to why a themes screen is empty, and it separates the three cases that look identical on screen: nothing has been recorded, enough has been recorded and no pass has run yet, or there are themes to read and the reader is looking at the wrong aspect.

Field Type Description
entity_kind EntityKind required The kind of entity this describes.
entity_name string required Its name.
readiness AspectReadiness required The entity’s overall state, which is the least advanced of its enabled aspects.
trace_count u64 required How many of this entity’s traces carry at least one note.
aspects AspectProgressRow[] required One row per enabled aspect, in catalogue order.
latest_snapshot ThemeSnapshot The newest snapshot for this entity, when a pass has written one. Absent while every aspect is still collecting.

SnapshotLandmark

One snapshot in a timeline, with the reason it was chosen.

Field Type Description
snapshot ThemeSnapshot required The snapshot.
reason SnapshotLandmarkReason Why this snapshot is in the selection. Present for presentation=landmarks only; an inventory returns consecutive snapshots and chooses nothing, so there is nothing to report.

SnapshotListResponse

A selection of one entity’s snapshots, oldest first, so a timeline can be drawn without reordering anything.

Field Type Description
snapshots SnapshotLandmark[] required The selected snapshots, oldest first.
total u64 required How many snapshots the entity has in the requested range. This is the number the selection was drawn from and not the number returned, so a timeline can say what it is standing in for.
page Page required Cursor and totals.

ThemeNode

One node of one stage of a flow: a theme, the stage’s noise, or the overflow the stage folded away.

Field Type Description
node_id string required Identifies this node inside this flow, and is what a link names at either end. It is not a theme id: the noise and overflow nodes have one of these and no theme id.
kind ThemeNodeKind required Whether this node is a theme, the noise or the overflow.
theme_id string The theme this node stands for. Present only when kind is theme, which is what makes the other two impossible to drill into by accident.
label string required What to show for this node: a theme’s own label, or the words for noise or overflow.
trace_count u64 required How many traces this node accounts for.
coverage f64 required This node’s share of the stage’s traces, between 0 and 1. A stage’s nodes cover all of it, so its shares add to 1.

ThemeStage

One aspect’s column in a flow. Stages come back in the order the aspects were asked for, and links only ever join neighbouring stages, so the order decides which co-occurrences are visible and is not a display preference.

Field Type Description
aspect string required Which aspect this stage is.
trace_count u64 required How many traces this stage accounts for.
nodes ThemeNode[] required The stage’s nodes, largest first, with the noise and overflow nodes last.

A ribbon between one node of a stage and one node of the next. It says the two describe the same traces and nothing more: a link between a goal theme and an outcome theme means they were written about the same runs, not that one caused the other.

Field Type Description
source_node_id string required The node this link leaves, in the earlier stage.
target_node_id string required The node it reaches, in the next stage.
trace_count u64 required How many traces are assigned to both ends.
source_share f64 required That count as a fraction of the source node’s traces, between 0 and 1.
target_share f64 required That count as a fraction of the target node’s traces, between 0 and 1. The two shares differ whenever the nodes are different sizes, and reading only one of them is how a thin ribbon gets mistaken for a weak relationship.

ThemeFlow

One snapshot read across two to four aspects at once: a stage per aspect, and the links between neighbouring stages. Association, never causation.

Field Type Description
snapshot ThemeSnapshot required The snapshot this flow was computed from, so every count on the page can be read against the window it came from.
stages ThemeStage[] required One stage per aspect, in the order they were asked for.
links ThemeLink[] required The links between neighbouring stages. There are none between stages that are not neighbours, which is why the order of the aspects matters.

ThemeDetail

One theme in one snapshot, or the answer that this snapshot did not hold it.

Field Type Description
snapshot ThemeSnapshot required The snapshot that was read, with its own counts, so a caller can write 28 of 70 traces in this window without a second request.
theme Theme The theme, when this snapshot holds it. Absent is an answer and not an error: the theme did not exist in that window, which is what scrubbing a timeline back past a theme’s birth looks like. A theme id this entity has never had is a 404 instead.

ThemeExample

One note from a theme, with the run it was written about. The two ids are different things: note_id identifies the sentence and trace_id identifies the run it describes.

Field Type Description
note_id string required The note this example is.
trace_id string required The trace the note was written about.
text string required The sentence, after redaction. This is the wording that put the trace in this theme, so a reader can see why it landed here rather than take it on trust.
similarity u32 required How much this note’s wording overlaps the theme’s label, counted out of 64. It is a count of shared sketch positions and not a percentage, and it is an integer on purpose: no part of the clustering uses floating-point arithmetic, so two replicas cannot order the same notes differently.
trace_started_at timestamp required When the run began, so examples can be read against the period they came from rather than against when the note was written.

ThemeExampleListResponse

A page of the notes behind one theme.

Field Type Description
examples ThemeExample[] required The notes, most like the theme’s label first.
page Page required Cursor and totals.

ThemeHistoryPoint

One theme in one snapshot of its life. Every count belongs to that snapshot alone, and windows overlap, so a run of these is read as a series and never added up.

Field Type Description
snapshot_id string required The snapshot this point comes from.
cutoff_at timestamp required When that snapshot became the current state.
window_from timestamp required Start of that snapshot’s window, inclusive.
window_to timestamp required End of that snapshot’s window, exclusive.
state ThemeState required What had happened to the theme by this point.
note_count u64 required How many notes were in the theme here.
trace_count u64 required How many distinct traces those notes were written about.
coverage f64 required The theme’s share of the notes that snapshot read for this aspect, between 0 and 1. Coverage and raw count can move in opposite directions, and coverage is the one that says whether a theme is gaining ground.
split_from string[] required The themes this one came out of at this point. Empty unless state is split.
split_into string[] required The themes that came out of this one at this point. Both directions are stored, so a caller never has to read a second theme’s history to learn what happened to this one.
merged_from string[] required The themes that became this one at this point. Empty unless state is merge.
merged_into string[] required The theme this one became at this point, when it stopped being its own theme.

ThemeHistory

One theme across every stored snapshot that holds it, oldest first. This is the only theme response that names no single snapshot, because the series is the answer.

Field Type Description
theme_id string required The theme this history is of.
aspect string required The aspect it was clustered along.
label string required The theme’s label at its newest point. A label can change between snapshots, because the note most like the others can change while the theme holds together.
points ThemeHistoryPoint[] required The series, oldest first, so nothing has to be reordered before it is drawn or differenced.
page Page required Cursor and totals.

TraceAspectNotes

Every aspect note recorded about one trace, which is at most one per aspect. Read it beside the trace itself: a note is the sentence that put this run in each of its themes, so it is the shortest route from a theme back to what actually happened.

Field Type Description
trace_id string required The trace these notes are about.
notes AspectNote[] required The notes, in catalogue order. Empty when nothing has written one yet, which is ordinary for a recent trace.

AccountCredentials

An email address and a password, for sign-up and for log-in.

Field Type Description
email string required The account’s email address. Compared without regard to case, and with surrounding spaces removed.
password string required At least 10 characters and at most 1024 bytes. Stored only as an argon2id hash.

AccountSession

A logged-in session. Send token as Authorization: Bearer <token>.

Field Type Description
token string required The session token. It grants every scope on the account’s own project and nothing else. It is shown once: the daemon keeps only its SHA-256 digest.
expires_at timestamp required When the session stops working. Log in again after this.
email string required The account’s email address, as stored.
project string required The account’s project. Every signal an account key writes lands here, and every read is limited to it.

Account

The account a session belongs to.

Field Type Description
email string required The account’s email address, as stored.
project string required The account’s project, which is the only project its session and its keys reach.
created_at timestamp required When the account was created.

AccountKey

One API key an account holds. The secret is not part of it: the daemon keeps only a digest.

Field Type Description
id string required The key’s identifier, for revoking it. Not a secret.
scope KeyScope required What the key may do.
name string The label given when the key was created.
prefix string required The first characters of the secret, so you can tell which key a process holds without the daemon storing the secret.
created_at timestamp required When the key was created.

CreateAccountKeyRequest

A request for a new API key.

Field Type Description
scope KeyScope required What the key may do. One scope per key.
name string A label for the key, such as the service that will hold it. At most 100 characters.

CreatedAccountKey

A new API key, with its secret. This is the only time the secret is shown.

Field Type Description
key AccountKey required The key as it is listed from now on.
secret string required The key itself. Send it as Authorization: Bearer <secret>. Copy it now: the daemon keeps only its SHA-256 digest and cannot show it again.

Span

One unit of work. Spans are the backbone of mcpd: logs, metrics, scores and feedback all hang off a span or a trace.

Stored in the spans table.

Field Type Description
trace_id string required 128-bit trace id, 32 lowercase hex characters. OpenTelemetry-compatible.
span_id string required 64-bit span id, 16 lowercase hex characters. Unique within the trace.
parent_span_id string The parent span within this trace. Absent on the root.
external_parent_span_id string A parent from a foreign tracing system that mcpd did not create. Carried for correlation, never used for mcpd’s own tree.
name string required What this operation is called. Keep it low-cardinality: search_docs, not search_docs(query=hello).
kind SpanKind required What kind of operation this is.
status SpanStatus required The outcome.
start_time timestamp required When the operation began, in UTC with nanosecond precision.
end_time timestamp When it finished. Absent while the span is still running.
duration_ns u64 Derived from the timestamps at ingest, stored so the database never has to compute it to sort.
is_root bool required True when this span has no parent inside the trace. Materialised so the trace list never scans children.
is_event bool required True for a point in time rather than a duration. Event spans have no end.
entity_kind EntityKind The kind of thing that produced this span.
entity_id string Stable identifier of the producing entity.
entity_name string Display name of the producing entity.
input json What went in. For a model call this is the full prompt, and it is the single most valuable field mcpd stores.
output json What came out. For a model call this is the completion.
attributes json Kind-specific structured detail. See the attribute reference for what each kind carries.
metadata json Anything you want to attach. mcpd never interprets this.
usage TokenUsage Token counts, on model and embedding spans.
cost Cost Computed cost, on spans that consumed tokens.
model_parameters ModelParameters How the model was called: the sampling settings the caller chose. Absent on a span that made no model call, and on one whose producer did not record them.
error SpanError Failure detail, when status is error.
links SpanLink[] Causal references to other spans or traces.
tags string[] Free labels for filtering. Only meaningful on a root span, where they label the whole trace.
correlation Correlation required Which project, environment, session, thread and user this belongs to.
model string Model identifier, denormalised out of attributes so it can be indexed and grouped.
provider string Model provider, denormalised for the same reason.
received_at timestamp required When mcpd accepted this span. Differs from start_time for buffered or replayed data, and is what retention is measured against.
ingest_key string Producer-supplied idempotency key. A retried batch with the same key is accepted once.

Primary key: trace_id, span_id

Indexes

  • correlation.project, start_time: The trace list, newest first, scoped to a project.
  • correlation.project, is_root, start_time: Root-only projection for the trace list.
  • correlation.session_id: Every trace in a conversation.
  • model, start_time: Cost and latency grouped by model.
  • status, start_time: Failures, newest first.

Trace

A whole run, projected from its spans. mcpd materialises this at ingest so the list view is one indexed read rather than an aggregation over every span. One per project and trace id: a trace id crosses service boundaries, so two projects that call each other each hold their own run under it, built from their own spans.

Stored in the traces table.

Field Type Description
trace_id string required The trace this summarises.
root_span_id string The root span, when one has arrived.
name string required The root span’s name.
kind SpanKind required The root span’s kind.
status SpanStatus required error when any span in the trace failed, otherwise the root’s status.
start_time timestamp required Earliest start across the trace.
end_time timestamp Latest end across the trace. Absent while anything is still running.
duration_ns u64 Wall-clock span of the whole trace.
span_count u32 required How many spans have arrived.
error_count u32 required How many of them failed.
usage TokenUsage Token usage summed across every model span in the trace.
cost Cost Cost summed across the trace. This is the number a bill is reconciled against.
model_count u32 required How many model calls the run made.
tool_count u32 required How many tool calls the run made.
tags string[] Tags from the root span.
correlation Correlation required Context inherited from the root span.
score_summary json Mean score per scorer id, maintained as scores arrive, so the list can sort by quality without a join.
feedback_summary json Counts per feedback kind and value.
is_complete bool required True once the root span has ended and no span is still open.
received_at timestamp required When the first span of this trace arrived.

Primary key: project, trace_id

Indexes

  • correlation.project, start_time: The default list ordering.
  • correlation.project, status, start_time: Failed runs.
  • correlation.project, cost.total_cost: Most expensive runs.
  • correlation.project, duration_ns: Slowest runs.

LogRecord

A structured log line, correlated to the span that produced it. Logs are stored beside traces so ‘show me the logs for this run’ is one query, not a context switch to another tool.

Stored in the logs table.

Field Type Description
log_id string required Unique id for this record.
trace_id string The trace this line belongs to, when it was emitted inside one.
span_id string The span this line belongs to.
timestamp timestamp required When the line was emitted.
level LogLevel required Severity.
message string required The rendered message.
fields json Structured key/value context. Redacted like any payload.
logger string Which logger emitted it.
correlation Correlation required Project, environment, session and user.
received_at timestamp required When mcpd accepted it.

Primary key: log_id

Indexes

  • trace_id, timestamp: All logs for a run, in order.
  • correlation.project, level, timestamp: Severity-filtered log search.

MetricPoint

One numeric observation. mcpd aggregates these into time buckets on read, and keeps raw points only as long as the raw retention class allows.

Stored in the metrics table.

Field Type Description
metric_id string required Unique id for this observation.
name string required Metric name, such as mcpd.model.duration.
kind MetricKind required How it aggregates.
value f64 required The observed value.
unit string UCUM unit, such as ms, By or {token}.
timestamp timestamp required When it was observed.
labels json Dimensions. Passed through the cardinality filter, which rejects unbounded keys.
buckets json Histogram bucket boundaries and counts, on histogram points.
trace_id string Exemplar link: the trace that produced this observation.
span_id string Exemplar link: the span that produced it. This is what turns a spike on a chart into a specific run you can open.
correlation Correlation required Project and environment.
received_at timestamp required When mcpd accepted it.

Primary key: metric_id

Indexes

  • correlation.project, name, timestamp: Time series read path.

Score

A machine evaluation of a span or a trace.

Stored in the scores table.

Field Type Description
score_id string required Unique id.
trace_id string The scored trace.
span_id string The scored span, when the score is about one step rather than the whole run.
scorer_id string required Which scorer produced this, such as answer_relevance.
scorer_version string The scorer’s version. A score is only comparable to another score from the same version.
source ScoreSource required Live, offline, experiment or manual.
value f64 required The numeric result.
min f64 Lower bound of the scale. Without it, a bare number is not interpretable.
max f64 Upper bound of the scale.
label string A categorical result, when the scorer produces one alongside the number.
reason string Why. For an LLM judge this is the judge’s explanation, and it is what makes a score arguable rather than magic.
score_trace_id string The trace of the scoring run itself, so a suspicious score can be debugged like any other run.
cost Cost What producing this score cost. Judged evaluation is not free and mcpd refuses to hide that.
metadata json Anything else.
timestamp timestamp required When the score was produced.
correlation Correlation required Inherited from the target.

Primary key: score_id

Indexes

  • trace_id: Every score for a run.
  • correlation.project, scorer_id, timestamp: A scorer’s values over time.

Feedback

A human evaluation. Kept distinct from Score because the two answer different questions and must never be averaged together.

Stored in the feedback table.

Field Type Description
feedback_id string required Unique id.
trace_id string The trace being judged.
span_id string The span being judged.
kind FeedbackKind required Thumbs, rating, correction, tag or comment.
value json required The judgement. Its shape follows kind.
comment string Free prose the person added.
author_id string Who gave it. Hashed by default.
author_kind string Whether the author was an end user, a reviewer or an operator.
timestamp timestamp required When it was given.
metadata json Anything else.
correlation Correlation required Inherited from the target.

Primary key: feedback_id

Indexes

  • trace_id: Every judgement on a run.

Project

The tenancy boundary. Every signal belongs to exactly one project; retention, redaction, sampling, budgets and access are all configured per project.

Stored in the projects table.

Field Type Description
project_id string required Stable identifier, used in API paths and ingest keys.
name string required Display name.
created_at timestamp required When it was created.
retention_days u32 How long to keep signals. Absent means the server default.
sampling json The sampling policy for this project.
redaction json Extra redaction rules, applied on top of the server defaults.
budget json Cost budget and what happens when it is exceeded.
metadata json Anything else.

Primary key: project_id

Dataset

A fixed collection of inputs, with optional expected outputs, that an experiment runs against. Datasets are how a change is proved not to regress.

Stored in the datasets table.

Field Type Description
dataset_id string required Stable identifier.
name string required Display name.
description string What this dataset is for.
item_count u32 required How many items it holds.
created_at timestamp required When it was created.
correlation Correlation required Which project owns it.

Primary key: dataset_id

DatasetItem

One case in a dataset.

Stored in the dataset_items table.

Field Type Description
item_id string required Stable identifier.
dataset_id string required Which dataset this belongs to.
input json required What to feed the system under test.
expected json The reference answer, when there is one. Absent for open-ended cases judged by a scorer.
metadata json Anything else, such as a difficulty label or a source citation.
source_trace_id string The production trace this case was captured from, when it was. This is how a bug report becomes a permanent regression test.

Primary key: item_id

Experiment

One evaluation run of a system against a dataset. Comparing two experiments is how a change is accepted or rejected.

Stored in the experiments table.

Field Type Description
experiment_id string required Stable identifier.
name string required Display name.
dataset_id string required What it ran against.
status ExperimentStatus required Where it got to.
started_at timestamp When it began.
finished_at timestamp When it ended.
item_count u32 required Cases attempted.
summary json Aggregate score per scorer, with mean, standard deviation and count.
baseline_id string The experiment this one is compared against.
cost Cost What the whole run cost.
error string Why it failed, when it did.
metadata json The configuration under test: model, prompt version, git sha.
correlation Correlation required Which project owns it.

Primary key: experiment_id

AuditEvent

One recorded attempt to read stored payloads, or to delete them. Written by the handlers that hand prompts and completions back out of mcpd, so the question “who read this run” has an answer. The audit log is not trimmed by the retention pass: a log that deleted itself on the same schedule as the data it describes could not say who read the data that has since gone.

Stored in the audit_log table.

Field Type Description
audit_id string required Unique id.
timestamp timestamp required When the request was handled.
principal string required Which key acted, as a short hex digest of it. The key itself is never written here. anonymous when the request carried no key, which is what an unprotected daemon looks like.
subject string required The end identity a proxy asserted for this request. Always empty in this build. The field exists from the first release so that proxy identity can be added later without a migration.
action string required What was done, named as the endpoint id: get_trace, list_spans, delete_trace.
project string required The project the request was made against.
resource string The trace or dataset the request named, when it named one.
result AuditResult required Whether authorisation allowed the request or refused it.

Primary key: audit_id

Indexes

  • project, timestamp: One project’s audit log, newest first.

Definition

A stored definition. Immutable per version: a run pins the version it started against, and that version never changes underneath it.

Stored in the definitions table.

Field Type Description
project string required Which project owns it.
id string required Its identifier within the project.
version u32 required Its version. A run started without one pins the highest stored version at that moment.
kind DefinitionKind required What kind of definition it is.
body json required The document as stored, minus version.
created_at timestamp required When this version was stored.

Primary key: project, id, version

Run

One execution of one definition version. The daemon owns the run between steps: its journal, its deadlines, its waits, its cancel and its trace. Your own process owns everything inside a step.

Stored in the runs table.

Field Type Description
run_id string required Its identifier, minted at start.
project string required Which project owns it.
definition_id string required Which definition it runs.
definition_version u32 required Which version. Pinned at start and never re-resolved.
status RunStatus required Where it is.
cursor RunCursor required The frontier: where in the graph the run has reached.
input json What it was started with, after validation against the definition’s input_schema.
output json What the definition’s output template rendered when the run reached the end of the graph.
state json A JSON object merged shallowly on every complete that sends one. Later steps read it through the lease.
error string Why it failed, when it did: the failing step’s message, or the ceiling it spent.
correlation Correlation required The context stored at start and copied onto every span the daemon writes for the run, with run_id set to this run’s id.
trace_id string required The trace every span of this run belongs to. 32 lowercase hex characters, minted at start.
steps_used u32 required How many steps this run has completed. Counted against max_steps on the root of the run tree.
max_steps u32 The step ceiling from the definition’s limits, when it declares one. On a root it bounds the whole tree.
cost_usd f64 required What the run’s steps have cost so far, priced from the usage workers reported.
max_cost_usd f64 The spend ceiling from limits, when declared. A claim refused for a spent ceiling ends the run failed.
deadline_at timestamp When limits.run_timeout_ms passes. Absent when the definition declares no timeout, and once the run has ended.
idempotency_key string The key the start request carried, when it carried one. A repost of it within 24 hours returns this run.
root_run_id string required The root of the run tree, which ceilings are enforced against. Equal to run_id on a root.
parent_run_id string The run whose subworkflow step started this one.
parent_step_key string The step in the parent that this run is the body of.
depth u32 required How many parents lie between this run and the root. Zero on a root.
created_at timestamp required When it started.
updated_at timestamp required When it last moved.
ended_at timestamp When it reached a terminal status.

Primary key: run_id

StepAttempt

One attempt at one step of a run. A run’s journal is these rows, append only: a retry is a new row, never a rewrite of the old one.

Stored in the step_attempts table.

Field Type Description
run_id string required Which run.
step_key string required Which step, with any enclosing branch, item or iteration folded into the key: refund/issue, classify[3], poll[7]/classify.
attempt u32 required Which attempt this is. The fence: a lease for an earlier attempt is refused once a later one exists.
kind StepKind required What kind of step.
effect Effect required What a duplicated body would cost.
status AttemptStatus required Where the attempt is.
input json The rendered input handed to the worker, on a claimable step.
output json What the worker returned on complete, or the event payload on a resumed wait.
error StepError Why it failed, when it did.
usage StepUsage The token usage the worker reported on complete.
cost_usd f64 What the daemon priced that usage at.
span_id string required The span the daemon opened for this attempt. 16 lowercase hex characters, minted when the row was written. A worker’s own spans nest under it.
lease_owner string The worker_id holding the lease, while one does.
lease_until timestamp When the lease lapses without a heartbeat.
started_at timestamp When the worker posted started. Absent when it never did, which is what the reclaim rule reads.
orphaned_at timestamp When the lease lapsed.
reclaim_until timestamp When the reclaim window closes.
attempts_left u32 How many attempts the node’s retry budget still allows after this one.
ended_at timestamp When it reached a terminal status.
created_at timestamp required When the row was written.

Primary key: run_id, step_key, attempt

RunEvent

One row of a run’s event log. Written in the transaction that made the change it describes, with seq allocated from the run header, so a reader that has seen a row has seen every row below it.

Stored in the run_events table.

Field Type Description
run_id string required Which run.
seq u64 required Its position in this run’s stream, from zero and without gaps. Pass it as after to reattach.
kind RunEventKind required What happened.
payload json The detail, shaped by kind.
at timestamp required When it was written.

Primary key: run_id, seq

Aspect

An axis that traces are described along. An aspect is a definition and not a measurement: it carries the name a note is filed under, the words an operator reads on screen and, where a model writes the sentence, the instruction handed to whatever produces it. Four ship with the daemon as ordinary rows rather than as a list compiled into a client, so there is one source of truth for what exists: goal, what the user wanted; sentiment, the tone they expressed; behaviour, what the agent did about it; and outcome, how the interaction ended. A stored version never changes, so a note recorded a month ago still names the definition it was written against.

Stored in the aspects table.

Field Type Description
project string required Which project owns it. Aspects are project-scoped, like every other tenancy boundary here.
name string required The aspect’s name, lowercase with underscores, such as goal or refund_reason. This is what a note is filed under and what every query parameter names.
label string required The display name, such as Goal. Stored rather than derived from the name, so an aspect reads the same in every screen and every language of SDK.
description string required One line saying what this axis is about, shown beside the label so a reader does not have to infer the axis from its name.
instruction string The text handed to whatever writes a note along this aspect, at most 2000 bytes. The daemon stores it and hands it out; what reads it and writes the sentence sits outside the daemon. Absent when derived is true, because a derived aspect needs no instruction.
derived bool required Whether the daemon writes this aspect’s notes itself, with no model anywhere. outcome ships derived, written from the run’s status, the step error class, the silent producer flag on the cause report and the divergence watch, so a deployment with nothing configured still fills one aspect and can see the whole pipeline work before a model is wired to anything.
built_in bool required Whether the daemon seeded this aspect. A built-in is a row like any other and can be switched off, but it cannot be deleted, because notes already name it.
enabled bool required Whether notes are written along this aspect in this project. It is configuration and read-only here. Switching an aspect on affects notes recorded afterwards and never rewrites what is stored.
sort_order u32 required Where this aspect sits in every list and in the default column order, ascending. Stored, so the order an operator learns is the order they keep.
version u32 required This definition’s version. Editing an aspect stores a new version rather than changing this one, so analysis already recorded is never rewritten under a reader.
created_at timestamp required When this version was stored.

Primary key: project, name, version

Indexes

  • project, sort_order: The catalogue, in the order it is shown.

AspectNote

One sentence recorded against one trace along one aspect. A note proves one thing: that this sentence was recorded against that trace by the definition named in aspect, at the version in definition_version. It does not prove the sentence is true. Nothing here is checked against the trace it describes, so a note is evidence of what a definition said and not evidence of what happened, and the trace is what settles the difference. The daemon also keeps a small sketch of the wording, which is what clustering compares, and that sketch is deliberately not a field on this entity: it is a detail of how the pass is stored, a caller can do nothing with it, and publishing it would fix the hashing where it stands today. The text is payload. It passes through the same redactor as every other signal on the way in, retention sweeps it, and erasing a subject erases it.

Stored in the aspect_notes table.

Field Type Description
note_id string required Unique id of the note. This identifies the sentence and never the run: a note id and a trace id are different things and conflating them is the mistake this separate name exists to prevent.
trace_id string required The trace this sentence is about.
aspect string required Which aspect this note is filed under, matching Aspect.name.
text string required The sentence, after redaction. It is the only text the read API returns for a note, and it is what the clustering pass reads.
definition_version u32 required The version of the aspect definition this note was written against. Editing a definition mints a new version, so an older note keeps naming what was actually asked for at the time.
model string Which model wrote the sentence, when one did. Absent for a derived aspect, where the daemon wrote the note itself.
created_at timestamp required When the note was recorded. Writing a note lags the trace it describes, sometimes by a long way, so a trace can be visible for hours before it carries one.
correlation Correlation required Inherited from the trace, so a note is scoped, filtered, retained and erased exactly as its trace is.

Primary key: note_id

Indexes

  • trace_id: Every note about one run.
  • correlation.project, aspect, created_at: One aspect’s notes newest first, which is the order a clustering pass reads them in.

ThemeSnapshot

One clustering pass over one entity, covering a moving window of notes. A snapshot is what turns a clustering into a series: every theme belongs to exactly one, and every theme query names one. Windows overlap, so counts from two snapshots must never be added together. A trace whose note falls inside three windows is counted once in each of them, and summing the three counts it three times. Compare snapshots or read one; do not sum them. Reads never recompute either: a pass writes a snapshot on its own timer and every read is a lookup of what it wrote, so refreshing a screen shows the same numbers rather than a fresh clustering.

Stored in the theme_snapshots table.

Field Type Description
snapshot_id string required Opaque id, echoed back unchanged. Never build one by hand and never read meaning into its shape.
project string required Which project this pass covered.
entity_kind EntityKind required The kind of thing this pass covered.
entity_name string required The name of that thing. An entity is the pair of a kind and a name, which spans already carry, so nothing new is assigned to identify one.
aspects string[] required The aspects this pass covered, in catalogue order. An aspect with too few notes when the pass ran is absent, so a missing name means it is still collecting and not that anything failed.
ordinal u64 required Where this snapshot falls in the entity’s series, counting from one at the oldest snapshot still stored.
cutoff_at timestamp required When this snapshot became the current state. A timeline is ordered by this and not by the window bounds, because two passes can cover overlapping windows.
window_from timestamp required Start of the window of notes read, inclusive.
window_to timestamp required End of the window of notes read, exclusive.
trace_count u64 required How many distinct traces the notes read belong to.
note_count u64 required How many notes the pass actually read. Every coverage figure in this snapshot is a share of this number and never of the number of notes in the window, which is what makes capped worth reading.
capped bool required Whether the pass reached its ceiling of 20,000 notes and clustered the newest of them rather than all of them. True means the window holds more notes than were read, so the denominator of every share here is note_count and not the window.

Primary key: snapshot_id

Indexes

  • project, entity_kind, entity_name, cutoff_at: One entity’s series of passes, newest first.

Theme

A cluster of aspect notes that share their wording, in one snapshot along one aspect. A theme groups aspect notes that share their wording, which is why mcpd needs no model and no embedding to build one. It does not read the sentences, so two notes that mean the same thing in different words land in separate themes, and a theme’s label is the note most like the others rather than a summary written for it. The clustering is a pure function of the notes it reads, with no floating-point arithmetic in the comparison, so two replicas of one deployment reading the same notes write the same themes and cannot disagree about where a boundary falls.

Stored in the themes table.

Field Type Description
theme_id string required Opaque id, stable across snapshots, which is what makes a history possible. A theme keeps its id while its wording holds, so the same id in two snapshots is the same theme. Nothing is encoded in the shape of it.
snapshot_id string required The snapshot this theme belongs to.
aspect string required Which aspect it was clustered along. Clustering runs once per aspect, so one trace can sit in a different theme in each of them.
entity_kind EntityKind required The kind of entity this theme was built for.
entity_name string required The name of that entity.
label string required The theme’s label, which is the text of one of its own notes: the note most like the rest of them. Nothing wrote it for the screen, so it reads as one real sentence about one real trace rather than as a description of the group, and it can change between snapshots as the group does.
label_note_id string required The note the label is taken from.
label_trace_id string required The trace that note was written about, so a reader can open the run behind the label instead of taking the label’s word for it.
state ThemeState required What happened to this theme between the previous snapshot and this one.
note_count u64 required How many notes are in this theme.
trace_count u64 required How many distinct traces those notes were written about.
coverage f64 required This theme’s share of the notes the snapshot read for this aspect, between 0 and 1. The denominator is the snapshot’s note_count, so in a capped snapshot it is a share of what was read.

Primary key: snapshot_id, aspect, theme_id

Indexes

  • theme_id, aspect: One theme across every snapshot that holds it, which is what a history reads.