initializdocs
DeveloperForge runtimeMCP

MCP — Audit Events

Event types emitted by the MCP subsystem; their fields and reason codes.

Every MCP audit event is NDJSON to stderr alongside the existing Forge audit stream. No event ever carries argument or result bytes — only sizes, durations, server/tool names, and stable reason codes. The grep-test TestMCPTool_Audit_NeverLogsBytes pins this invariant.

Event matrix

EventWhenFields
mcp_server_startedA server reaches Readyname, transport, tool_count
mcp_server_failedA server reaches terminal Failedname, phase, reason
mcp_server_degradedTransport error mid-call; entering backoffname, attempt, backoff_ms
mcp_tool_callBefore every tools/callserver, tool, args_size
mcp_tool_resultAfter every tools/callserver, tool, duration_ms, result_size, ok, reason?
mcp_tool_conflictRegistry rejects a tool nameincoming_name, error
mcp_token_refreshEvery OAuth refresh attemptserver, ok, reason
mcp_auth_requiredA delegated (auth.type: user) call parked awaiting consent (#330)server, subject, deadline, timeout_ms
mcp_auth_resolvedThe parked call's consent arrived; it resumed (#330)server, subject, wait_ms
mcp_auth_timeoutNo consent within the window; the call fails no_token (#330)server, subject, wait_ms, decision

Every event also carries the standard top-level fields: ts, event, correlation_id (when scoped to a request).

The three mcp_auth_* consent-gate events are emitted once and attributed to the parked invocation — they carry that call's correlation_id / task_id / seq even though the resume happens out of band (#366). See Delegated consent for the full gate lifecycle.

Reason codes

mcp_tool_result.reason (only when ok=false)

ReasonCause
unavailable5xx / network error / DNS / TLS / timeout
protocol4xx, malformed JSON-RPC frame, JSON-RPC error response
revokedOAuth refresh denied (invalid_grant, expired_token)
canceledCaller cancelled ctx (deadline exceeded or explicit cancel)
tool_errorMCP server set isError: true in CallToolResult
unknownAnything else — investigate

mcp_server_failed.phase

PhaseMeaning
connectHTTP dial / TCP refused / DNS / TLS
initializeMCP initialize handshake; includes version mismatch
discovertools/list failed or returned a malformed input schema
runtimeAnything else (transport error after Ready)

mcp_server_failed.reason

ReasonMeaning
backoff_exhausted5 reconnect attempts failed (1s/2s/4s/8s/16s schedule)
version_mismatchServer's protocolVersion2025-06-18 (pinned)

mcp_token_refresh.reason

ReasonMeaning
refreshedNew access token persisted
refresh_deniedAuth server returned invalid_grant / expired_token
transportNetwork / 5xx talking to the token endpoint
store_errorFailed to persist the refreshed token

What to dashboard

For routine ops, three Grafana-style queries are enough:

  1. Per-server availability: count(mcp_server_started) - count(mcp_server_failed)
  2. Tool latency: histogram_quantile(0.95, mcp_tool_result.duration_ms by tool)
  3. OAuth refresh failures: count(mcp_token_refresh{ok="false"}) — page when non-zero.

What NOT to log

Do not log args or result text/data content. The audit stream intentionally omits these — Forge has no way to know whether a tool argument is PII, secrets, or operationally sensitive data, so it treats every byte as untrustworthy for logging.

On this page