The MCP audit trail must answer two different questions without forcing operators to choose between them:
-
"Why was this tool call allowed or denied?"
-
"What did the agent actually ask the tool to do?"
The existing log answers the first question well, but only partially answers the second. This design extends .cx/audit.log into a request-aware execution ledger without splitting policy review from operational review.
Goals
-
Preserve the current policy-audit value: capability, allow or deny state, policy name, and decision basis remain explicit.
-
Add request context that makes team review practical: session identity, tool arguments after redaction, agent rationale, and execution status.
-
Keep the log safe to retain and inspect: redact high-risk payloads by default and avoid turning the ledger into a replay mechanism.
-
Keep one registration path for all tools so coverage is uniform across read, observe, plan, and mutate capabilities.
Event Schema
Each line in .cx/audit.log remains one JSON object. The schema is extended rather than duplicated in a second file.
{
"schemaVersion": 2,
"timestamp": "2026-04-30T16:41:25.000Z",
"traceId": "notes_update:mutate:allowed:1746024085000",
"sessionId": "mcp-session-3d2c2f85",
"requestId": "req-000042",
"tool": "notes_update",
"capability": "mutate",
"decision": "allowed",
"reason": "Tool notes_update (capability: mutate) is allowed",
"policyName": "unrestricted",
"decisionBasis": ["tool_catalog", "policy_allow_list"],
"request": {
"agentReason": "Update the design note with the final redaction contract.",
"userGoal": "Document and implement request-aware audit logging.",
"args": {
"id": "20260430164125",
"title": "MCP Audit Request Tracing",
"body": {
"kind": "redacted_text",
"sha256": "8c7f...",
"length": 1240,
"preview": "MCP audit logging must capture..."
}
},
"redaction": {
"applied": true,
"rules": ["body_text", "large_freeform_text"]
}
},
"execution": {
"status": "succeeded",
"durationMs": 43
}
}
Required Fields
| Field | Meaning |
|---|---|
|
Audit schema version. The new request-aware ledger starts at |
|
UTC instant when the event is finalized. |
|
Stable per-event identifier kept for compatibility inside the repo’s review vocabulary. |
|
Stable id for the MCP server process lifetime. Groups related tool calls into one operator session. |
|
Monotonic per-session id that makes sequence reconstruction deterministic. |
|
Registered MCP tool name. |
|
Capability tier from the tool catalog. |
|
Policy verdict: |
|
Human-readable policy decision text. |
|
Resolved policy name such as |
|
Structured explanation of the verdict path. |
|
Request envelope with sanitized arguments and operator-review context. |
|
Execution outcome after the handler returns, throws, or times out. |
Request Envelope
The request object contains:
-
agentReason: required short free-text rationale supplied by the caller when available; otherwise"(not provided)". -
userGoal: optional short description of the current operator task when available from runtime context. -
args: sanitized tool arguments. -
redaction: whether any rule fired and which rules were applied.
The log must prefer structure over raw payload capture. If an argument is safe and already compact, store it directly. If it is sensitive or unbounded, replace it with a descriptor object.
When a client can supply richer intent, it should send these cx-specific MCP request _meta keys:
-
cx/agent-reason -
cx/user-goal
Execution Envelope
The execution object contains:
-
status:denied,succeeded,failed, ortimed_out -
durationMs: elapsed runtime when the handler started -
error: short sanitized message only forfailedortimed_out
Denied requests still emit an event. Their execution.status is denied, and no handler timing is recorded.
Redaction Rules
Redaction must be deterministic and schema-backed. The initial rules are:
| Rule | Behavior |
|---|---|
|
Replace large text values with |
|
Always redact note bodies, replacement payloads, generated docs content, and other multi-line authored text. |
|
Redact fields that contain prompt-like or instruction-like blobs even if they are short. |
|
Replace binary-like payloads or encoded content with |
|
Redact values when the key name suggests credentials, tokens, or secrets. |
Additional redaction constraints:
-
Keep direct scalar values for ids, booleans, counts, enum-like options, line numbers, and short paths.
-
Keep only a short preview for redacted text, and only when that preview is itself low risk.
-
Never log full note bodies, replacement content, extracted file contents, or generated documentation bodies.
-
Never log tool results in full. Execution metadata is enough for the audit ledger.
Registration And Wiring
The integration point is src/mcp/tools/register.ts.
The runtime flow becomes:
-
Build a request context as soon as the MCP tool handler receives normalized args.
-
Sanitize args with a shared redaction helper before any audit write.
-
Record the policy decision together with the request envelope.
-
Run the handler under the existing timeout wrapper.
-
Finalize the same event with
succeeded,failed, ortimed_out.
This keeps one audit event per tool call while making the final ledger reflect the whole lifecycle instead of just the gate decision.
Operator Commands
Operators consume the ledger through two complementary commands:
-
cx audit summaryfor aggregate trends, including execution-status counts and redaction-rule counts -
cx audit recentfor the latest sanitized request envelopes without opening raw JSONL
Implementation Notes
-
src/mcp/audit.tsowns the event schema, session id generation, request id sequencing, redaction helpers, and JSONL writes. -
src/mcp/enforce.tscontinues to own policy checks, but it should return the decision soregisterCxMcpToolcan log the full event lifecycle. -
src/mcp/tools/register.tsbecomes the orchestration point that combines request capture, policy decision, timing, handler execution, and final audit status. -
cx audit summaryandcx doctor mcpshould continue to summarize the ledger without printing raw request payloads.
Review Benefits
This design lets the team review:
-
what the agent asked for
-
why policy allowed or denied it
-
which redactions were applied
-
whether the request completed successfully
-
how a sequence of tool calls maps to one operator task
That closes the gap between policy telemetry and usable behavioral audit.