Skip to content

Records

Sinks receive batches that mix two dataclasses, ToolCallRecord and EventRecord. Tell them apart with isinstance or record.record_type ("tool_call" or "event"). Both have to_row() (column values) and to_dict() (JSON-friendly, timestamp as ISO 8601).

from fastmcp_feedback.instrumentation import EventRecord, ToolCallRecord

One tool call, built after the tool returns or raises.

FieldTypeDescription
idstrRandom UUID, chosen before the tool runs so events recorded inside it can carry it. Primary key of ffb_tool_calls.
toolstrTool name as called.
started_atdatetimeUTC, when the middleware saw the call.
duration_msfloatWall time through the middleware chain below this one, including the tool.
okbooloutcome == "ok".
outcomestr"ok", "soft_error" (a result the classifier flagged) or "error" (the tool raised).
modestr"meta" or "full"; "meta" for tools in meta_only_tools.
error_typestr | NoneException class name (the original cause, not FastMCP’s ToolError wrapper), or "SoftError".
error_messagestr | NoneRedacted, cut to max_error_chars. For soft errors, the classifier’s string, such as status=failed: disk full.
session_idstr | NoneThe session key: HTTP mcp-session-id, a per-process key on stdio, else FastMCP’s ctx.session_id.
request_idstr | NoneThe MCP request id.
client_idstr | NoneFrom the identity resolver, else FastMCP’s ctx.client_id.
user_substr | NoneFrom the identity resolver.
caller_kindstr | NoneFrom the identity resolver.
identitydictOther identity resolver keys, redacted.
server_versionstr | NoneFrom the enricher, else the middleware’s server_version.
args_sizeint | NoneBytes of the JSON-encoded arguments, before redaction.
result_sizeint | NoneBytes of the JSON-encoded result payload, before redaction. None when the tool raised.
argsAnyRedacted arguments in full mode, else None.
resultAnyRedacted result payload in full mode, else None. The payload is the structured content when present, otherwise the content items.
extradictEnricher output, redacted.
sample_ratefloat | NoneThe tool’s rate on sampled-in ok rows; None for everything recorded unconditionally.

One event from record_event or record_llm_call.

FieldTypeDescription
idstrRandom UUID. Primary key of ffb_events.
kindstrDotted name, at most 128 characters.
occurred_atdatetimeUTC, when record_event was called.
keystr | NoneJoin key, at most 255 characters.
call_idstr | NoneThe running call’s id when recorded inside a tool call, or the call_id passed in.
session_idstr | NoneThe running call’s session key, inside a call.
user_substr | NoneAs passed to record_event. The identity resolver is not consulted.
caller_kindstr | NoneNot set by record_event; present for schema symmetry.
client_idstr | NoneNot set by record_event; present for schema symmetry.
server_versionstr | NoneThe middleware’s server_version.
attrsdictRedacted and made JSON-safe: NaN and infinities become strings, datetimes ISO strings, unknown types str(value).
KindRecorded bykeyattrs
feedback.submittedsubmit_feedback with instrumentation=feedback idtype, title, description, submitter
llm.callrecord_llm_callas passedmodel, ok, and provider, duration_ms, input_tokens, output_tokens, error, error_type, prompt, completion when present

Returned by capture_recent_calls, one per linked call.

FieldTypeDescription
call_idstrThe call’s id.
rulestr"session" or "user_window": why it matched.
toolstrTool name.
started_atdatetimeUTC.
okboolThe call’s ok.
positionintOrder within the link set, from 0, oldest first.

JsonLinesSink writes {"record": record_type, **record.to_dict()} per record. A call line:

{"record": "tool_call", "tool": "render", "started_at": "2026-10-02T03:44:16.471791+00:00",
"duration_ms": 0.51, "ok": false, "mode": "full", "outcome": "soft_error",
"id": "25568541-cf28-472c-9537-3151d7d21be7", "error_type": "SoftError",
"error_message": "status=failed: out of memory", "session_id": "fd6283b1-7dd9-411c-b3fb-838a9c38330a",
"request_id": "4", "client_id": null, "user_sub": "dev", "caller_kind": null,
"identity": {"team": "render"}, "server_version": null, "args_size": 16, "result_size": 46,
"args": {"scene": "big"}, "result": {"status": "failed", "error": "out of memory"},
"extra": {}, "sample_rate": null}