From 6fcc8fa220d0141f28389be52c8a3f4acc8b55ac Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 11:05:16 -0500 Subject: [PATCH 01/15] Add RFD for server-to-client logging Proposes a `log` notification method that allows agents to send diagnostic messages (errors, warnings, info, etc.) to clients. Key features: - Optional sessionId for session-scoped vs connection-wide messages - 8 severity levels (RFC 5424/MCP-compatible) - Optional structured data, error codes, and timestamps - Follows ACP naming conventions (no namespace, like initialize/authenticate) Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 195 +++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 docs/rfds/server-logging.mdx diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx new file mode 100644 index 0000000..0f368e7 --- /dev/null +++ b/docs/rfds/server-logging.mdx @@ -0,0 +1,195 @@ +--- +title: "Server-to-Client Logging" +--- + +Author(s): [@chazcb](https://github.com/chazcb) + +## Elevator pitch + +> What are you proposing to change? + +Add a `log` notification that allows agents to send diagnostic messages (errors, warnings, info, etc.) to clients. This notification supports both connection-wide and session-specific messages via an optional `sessionId` field. + +Currently, servers have no way to proactively notify clients about errors, warnings, or other diagnostic information outside of request-response cycles. This RFD proposes a simple, flexible logging mechanism that: + +- Works before, during, and after sessions exist +- Supports 8 severity levels (RFC 5424/MCP-compatible) +- Includes optional structured data and error codes +- Follows ACP's existing naming conventions + +## Status quo + +> How do things work today and what problems does this cause? Why would we change things? + +Today, ACP has several limitations around server-to-client diagnostic messaging: + +1. **JSON-RPC errors are response-only**: The JSON-RPC 2.0 specification only allows errors in response to method calls. Servers cannot proactively send error notifications. + +2. **SessionNotification requires a session**: The existing `session/update` notification requires a `sessionId`. This means servers cannot communicate errors that occur: + - Before any session is created + - During connection setup or MCP server initialization + - At the connection level (affecting all sessions) + +3. **No severity levels**: There's no standard way to indicate whether a message is an error, warning, informational, or debug-level. + +4. **stderr is informal**: The transport spec mentions agents MAY write to stderr for logging, but this is unstructured and clients may ignore it. + +### Real-world problems this causes + +- **MCP server connection failures**: When an agent fails to connect to an MCP server, there's no way to inform the client. The user just sees tools not working. +- **Rate limiting**: When a provider rate-limits requests, the server can only fail the current request. It cannot proactively warn about approaching limits or advise the client to wait. +- **Transient errors**: Connection timeouts, retries, and other transient issues are invisible to clients. +- **Debugging**: Developers have no visibility into agent behavior without external logging infrastructure. + +## What we propose to do about it + +> What are you proposing to improve the situation? + +Add a new `log` notification method (agent → client) with the following structure: + +```json +{ + "jsonrpc": "2.0", + "method": "log", + "params": { + "level": "error", + "sessionId": "abc-123", + "logger": "mcp.database", + "message": "Connection to MCP server 'database-tools' failed", + "code": -32001, + "timestamp": "2025-01-21T10:30:00Z", + "data": { + "server": "database-tools", + "error": "ECONNREFUSED", + "retryIn": 5 + }, + "_meta": {} + } +} +``` + +### Fields + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `level` | `LogLevel` | Yes | Severity level of the message | +| `message` | `string` | Yes | Human-readable log message | +| `sessionId` | `SessionId` | No | Session this message pertains to. Omit for connection-wide messages. | +| `logger` | `string` | No | Name of the logger/component emitting this message (e.g., "mcp.database", "auth") | +| `code` | `ErrorCode` | No | Structured error code (uses same space as JSON-RPC errors) | +| `timestamp` | `string` | No | ISO 8601 timestamp when the event occurred | +| `data` | `object` | No | Additional structured data | +| `_meta` | `object` | No | Extensibility metadata | + +### Log Levels (RFC 5424) + +The `LogLevel` enum follows RFC 5424 syslog severity levels, which are also used by MCP: + +| Level | Description | +|-------|-------------| +| `debug` | Detailed debugging information | +| `info` | General informational messages | +| `notice` | Normal but significant events | +| `warning` | Warning conditions | +| `error` | Error conditions | +| `critical` | Critical conditions | +| `alert` | Action must be taken immediately | +| `emergency` | System is unusable | + +### Why `log` (no namespace)? + +The method name follows ACP's convention for connection-level operations: + +| Pattern | Examples | +|---------|----------| +| No namespace (connection-level) | `initialize`, `authenticate`, **`log`** | +| Resource namespace | `session/*`, `fs/*`, `terminal/*` | +| Protocol-level | `$/cancel_request` | + +We chose `log` over alternatives: +- `log/message` - Creates a namespace with only one method +- `notifications/message` - ACP doesn't use the `notifications/` pattern +- `SessionUpdate::Log` - Can't handle connection-wide messages + +## Shiny future + +> How will things will play out once this feature exists? + +Once this feature exists: + +1. **Better error visibility**: Clients can display connection errors, rate limit warnings, and other diagnostic information to users. IDEs could show a status indicator or notification panel for agent health. + +2. **Proactive warnings**: Servers can warn about approaching limits (token budget, rate limits) before they become errors, allowing clients to adapt. + +3. **Debugging support**: Developers can see what's happening inside agents without external logging infrastructure. The `logger` field allows filtering by component. + +4. **Session-scoped context**: Errors related to specific sessions can be attributed correctly, while connection-wide issues (like MCP server failures) are clearly distinguished. + +5. **Structured error handling**: The optional `code` field allows clients to programmatically handle specific error types (retry on rate limit, prompt for auth on auth errors, etc.). + +## Frequently asked questions + +> What questions have arisen over the course of authoring this document or during subsequent discussions? + +### Why not add a variant to `SessionUpdate`? + +`SessionUpdate` variants are delivered via `session/update`, which requires a `sessionId`. This means: + +1. You cannot send messages before any session exists +2. You cannot send connection-wide messages that affect all sessions +3. Semantically, log messages are different from session state updates (chunks, tool calls, mode changes) + +A separate `log` notification cleanly handles all scoping scenarios. + +### Why use RFC 5424 log levels instead of simpler error/warning/info? + +RFC 5424 levels are a well-established standard used by syslog, many logging libraries, and MCP. Using the same levels: + +- Provides familiar semantics for developers +- Enables compatibility with MCP logging +- Supports future use cases (debug logging, critical alerts) + +Clients can always map these to simpler categories in their UI. + +### How should clients handle log messages? + +Clients have flexibility in how they present log messages: + +- **IDE status bar**: Show counts of errors/warnings with hover details +- **Notification toasts**: Pop up for critical/alert/emergency levels +- **Log panel**: Dedicated panel showing filterable log history +- **Ignore**: For levels below a configured threshold + +The `logger` field can help clients filter/categorize messages. + +### Can agents flood clients with log messages? + +Yes, like any notification. Clients should: + +- Rate-limit UI updates +- Drop old messages if buffer is full +- Consider filtering by level + +Agents should be reasonable about log frequency. + +### What alternative approaches did you consider, and why did you settle on this one? + +1. **Tunneling over `SessionUpdate`**: Rejected because it can't handle connection-wide messages and mixes concerns. + +2. **JSON-RPC error responses**: Not possible - JSON-RPC only allows errors in response to requests. + +3. **Out-of-band telemetry (like agent-telemetry-export RFD)**: Good for observability backends but doesn't solve in-band client notification needs. + +4. **stderr logging**: Already exists informally but is unstructured, has no levels, and clients may ignore it. + +5. **New capability negotiation**: Considered requiring a capability, but logging is basic infrastructure that all clients should handle (even if just ignoring it). + +The proposed `log` notification is the simplest solution that: +- Works at all lifecycle stages +- Supports both session-scoped and connection-wide messages +- Uses standard log levels +- Fits ACP's existing patterns + +## Revision history + +- **2025-01-21**: Initial draft From 7565112f8cc5b7b0884c441703186b867d4d31f7 Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 11:18:08 -0500 Subject: [PATCH 02/15] Update RFD: transport-agnostic + protocol comparison - Fix transport-agnostic language (stderr is stdio-specific) - Add comparison table with MCP, LSP, A2A, AG-UI - Clarify key design choices vs other protocols Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 0f368e7..7c6c119 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -32,7 +32,7 @@ Today, ACP has several limitations around server-to-client diagnostic messaging: 3. **No severity levels**: There's no standard way to indicate whether a message is an error, warning, informational, or debug-level. -4. **stderr is informal**: The transport spec mentions agents MAY write to stderr for logging, but this is unstructured and clients may ignore it. +4. **Transport-agnostic gap**: ACP supports multiple transports (stdio, WebSocket, HTTP). While the stdio transport allows informal stderr logging, other transports have no equivalent. A protocol-level logging mechanism ensures consistent behavior across all transports. ### Real-world problems this causes @@ -180,7 +180,7 @@ Agents should be reasonable about log frequency. 3. **Out-of-band telemetry (like agent-telemetry-export RFD)**: Good for observability backends but doesn't solve in-band client notification needs. -4. **stderr logging**: Already exists informally but is unstructured, has no levels, and clients may ignore it. +4. **Transport-specific logging (e.g., stderr)**: Only works for stdio transport. ACP is transport-agnostic, so we need a protocol-level solution that works over WebSocket, HTTP, and any future transports. 5. **New capability negotiation**: Considered requiring a capability, but logging is basic infrastructure that all clients should handle (even if just ignoring it). @@ -189,6 +189,23 @@ The proposed `log` notification is the simplest solution that: - Supports both session-scoped and connection-wide messages - Uses standard log levels - Fits ACP's existing patterns +- Works identically across all transports + +### How does this compare to other protocols? + +| Protocol | Method | Levels | Scoping | Notes | +|----------|--------|--------|---------|-------| +| **ACP (proposed)** | `log` | RFC 5424 (8 levels) | Optional `sessionId` | Connection-wide or session-scoped | +| **MCP** | `notifications/message` | RFC 5424 (8 levels) | None (server-wide) | Requires `logging` capability; has `logging/setLevel` request | +| **LSP** | `window/logMessage` | 5 levels (Error→Debug) | None | Separate `window/showMessage` for UI display | +| **A2A** | None (via `TaskStatus.message`) | None | Task-scoped | Errors via binding-specific mechanisms (JSON-RPC, gRPC, HTTP Problem Details) | +| **AG-UI** | `RunError` event | None | Run-scoped | Event-driven; uses `StepStarted`/`StepFinished` for progress | + +**Key design choices:** + +- **RFC 5424 levels**: Aligns with MCP for ecosystem consistency. More expressive than LSP's 5 levels. +- **Optional `sessionId`**: Unlike MCP (server-wide only) or A2A/AG-UI (task/run-scoped only), ACP supports both connection-wide AND session-scoped messages. +- **Single method**: Unlike LSP which separates `logMessage` (background) from `showMessage` (UI), ACP uses one method with levels. Clients decide presentation. ## Revision history From c09a6cadc8c88ff93514e6cbae5f147efdc2f80f Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 11:18:45 -0500 Subject: [PATCH 03/15] Add links to protocol references in comparison table Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 7c6c119..1f037c7 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -196,10 +196,10 @@ The proposed `log` notification is the simplest solution that: | Protocol | Method | Levels | Scoping | Notes | |----------|--------|--------|---------|-------| | **ACP (proposed)** | `log` | RFC 5424 (8 levels) | Optional `sessionId` | Connection-wide or session-scoped | -| **MCP** | `notifications/message` | RFC 5424 (8 levels) | None (server-wide) | Requires `logging` capability; has `logging/setLevel` request | -| **LSP** | `window/logMessage` | 5 levels (Error→Debug) | None | Separate `window/showMessage` for UI display | -| **A2A** | None (via `TaskStatus.message`) | None | Task-scoped | Errors via binding-specific mechanisms (JSON-RPC, gRPC, HTTP Problem Details) | -| **AG-UI** | `RunError` event | None | Run-scoped | Event-driven; uses `StepStarted`/`StepFinished` for progress | +| **[MCP](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging)** | `notifications/message` | RFC 5424 (8 levels) | None (server-wide) | Requires `logging` capability; has `logging/setLevel` request | +| **[LSP](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_logMessage)** | `window/logMessage` | 5 levels (Error→Debug) | None | Separate `window/showMessage` for UI display | +| **[A2A](https://a2a-protocol.org/latest/specification/)** | None (via `TaskStatus.message`) | None | Task-scoped | Errors via binding-specific mechanisms (JSON-RPC, gRPC, HTTP Problem Details) | +| **[AG-UI](https://docs.ag-ui.com/concepts/events)** | `RunError` event | None | Run-scoped | Event-driven; uses `StepStarted`/`StepFinished` for progress | **Key design choices:** From 230cf35c5c66492aaf86707603e1914f10d4b11a Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 11:19:38 -0500 Subject: [PATCH 04/15] Add FAQ: relationship to OTEL Telemetry Export RFD Explains that `log` notifications (in-band, user-facing) and OTEL telemetry export (out-of-band, observability) are complementary. Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 1f037c7..21890f6 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -131,6 +131,32 @@ Once this feature exists: > What questions have arisen over the course of authoring this document or during subsequent discussions? +### How does this relate to the [Agent Telemetry Export RFD](./agent-telemetry-export)? + +They are **complementary**, serving different purposes: + +| Aspect | `log` notification (this RFD) | OTEL Telemetry Export | +|--------|------------------------------|----------------------| +| **Channel** | In-band (ACP protocol messages) | Out-of-band (HTTP to OTLP collector) | +| **Purpose** | Real-time user-facing notifications | Observability/debugging infrastructure | +| **Audience** | End users via client UI | Developers, ops teams, observability backends | +| **Transport** | Works on all ACP transports | Requires HTTP (stdio subprocess focus) | +| **Volume** | Low (errors, warnings, key events) | High (traces, spans, metrics, debug logs) | + +**When to use which:** + +- **`log` notification**: User-relevant events that should appear in the client UI + - "Rate limit exceeded - waiting 60s" → User sees toast/status bar + - "MCP server disconnected" → User knows why tools stopped working + +- **OTEL telemetry**: Developer/ops diagnostics for debugging and monitoring + - Detailed span traces with timing + - Token usage metrics + - High-volume debug logs + - Integration with Datadog, Grafana, etc. + +Agents may emit both: a `log` notification for user-visible errors AND detailed OTEL telemetry for debugging the same issue. + ### Why not add a variant to `SessionUpdate`? `SessionUpdate` variants are delivered via `session/update`, which requires a `sessionId`. This means: From ede77c9f857083c67eecc9dfdf9bbb1f196dfd4a Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 11:29:07 -0500 Subject: [PATCH 05/15] Expand FAQ: why separate from session/update, when to use log vs errors - Connection-level messages are essential (pre-session errors) - Separation of concerns (logs != conversation state) - Can't require capability negotiation (pre-initialize errors) - Clarify: JSON-RPC errors for request failures, log for out-of-band Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 21890f6..c65bbb0 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -159,13 +159,38 @@ Agents may emit both: a `log` notification for user-visible errors AND detailed ### Why not add a variant to `SessionUpdate`? -`SessionUpdate` variants are delivered via `session/update`, which requires a `sessionId`. This means: +There are several reasons to keep logging separate from `session/update`: -1. You cannot send messages before any session exists -2. You cannot send connection-wide messages that affect all sessions -3. Semantically, log messages are different from session state updates (chunks, tool calls, mode changes) +1. **Connection-level messages are essential**: Errors can occur before any session exists (during initialization, MCP server connection, authentication). A session-scoped mechanism simply cannot handle these cases. -A separate `log` notification cleanly handles all scoping scenarios. +2. **Separation of concerns**: Log messages are conceptually different from conversation state. `SessionUpdate` represents the actual conversation flow (user messages, agent responses, tool calls, mode changes). Mixing diagnostic notifications into that stream forces clients to filter out messages they may not want to display in the conversation UI. A separate channel lets clients handle logs independently—show them in a status bar, a separate panel, or ignore them entirely. + +3. **Opt-in presentation**: Not all clients care about every log message. By keeping logs separate, clients can choose their level of engagement without polluting the conversation thread. + +### Why not require capability negotiation for logging? + +We considered requiring a `logging` capability (like MCP does), but rejected it because: + +1. **Pre-initialize errors**: Connection-level errors can occur *before* `initialize` completes. If the agent can't send logs until capabilities are negotiated, critical startup errors would be lost. + +2. **Basic infrastructure**: Logging is fundamental enough that all clients should be able to receive it (even if they choose to ignore it). Making it capability-gated adds friction without clear benefit. + +Clients that don't want to display logs can simply ignore `log` notifications. + +### When should agents use `log` vs JSON-RPC error responses? + +**JSON-RPC errors should still be used when possible.** The `log` notification is specifically for out-of-band situations where there's no request to respond to: + +| Situation | Use | +|-----------|-----| +| Request fails (invalid params, auth required, etc.) | JSON-RPC error response | +| Connection to upstream service lost | `log` notification | +| Rate limit hit during request processing | JSON-RPC error response | +| Approaching rate limit (proactive warning) | `log` notification | +| MCP server disconnected | `log` notification | +| Server shutting down soon | `log` notification | + +The rule of thumb: if there's a pending request that caused the error, respond with a JSON-RPC error. If the error/warning is unsolicited (no request triggered it), use `log`. ### Why use RFC 5424 log levels instead of simpler error/warning/info? From 4b731e366d1425f73b05e5c0c07579f13af3e8bf Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 11:30:05 -0500 Subject: [PATCH 06/15] Add FAQ: backward compatibility (non-breaking change) JSON-RPC 2.0 notifications are fire-and-forget - clients safely ignore unknown methods. No version negotiation required. Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index c65bbb0..0a4fed7 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -192,6 +192,17 @@ Clients that don't want to display logs can simply ignore `log` notifications. The rule of thumb: if there's a pending request that caused the error, respond with a JSON-RPC error. If the error/warning is unsolicited (no request triggered it), use `log`. +### Is this a breaking change? + +**No.** Per JSON-RPC 2.0, notifications are fire-and-forget—the sender does not expect a response. If a client receives a notification for a method it doesn't recognize, it simply ignores it. This is standard JSON-RPC behavior. + +This means: +- Existing clients will safely ignore `log` notifications from updated agents +- Updated clients will work with older agents that don't send `log` notifications +- No version negotiation or capability exchange is required + +The proposal is fully backward-compatible. + ### Why use RFC 5424 log levels instead of simpler error/warning/info? RFC 5424 levels are a well-established standard used by syslog, many logging libraries, and MCP. Using the same levels: From 6809b539a30afb1115ab6d85ff7516c7d51e478f Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 11:42:59 -0500 Subject: [PATCH 07/15] Require capability negotiation + clarify why not prompt() errors Key insight: JSON-RPC errors terminate the run, log notifications don't. Changes: - Add `logging` capability requirement (non-breaking, clean opt-in) - Clarify the core question: "should the run stop?" - Non-fatal issues, warnings, degraded functionality shouldn't abort - Connection-wide issues, out-of-band timing, alternative patterns Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 74 +++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 0a4fed7..e47e88d 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -12,8 +12,9 @@ Add a `log` notification that allows agents to send diagnostic messages (errors, Currently, servers have no way to proactively notify clients about errors, warnings, or other diagnostic information outside of request-response cycles. This RFD proposes a simple, flexible logging mechanism that: -- Works before, during, and after sessions exist -- Supports 8 severity levels (RFC 5424/MCP-compatible) +- Supports both connection-wide and session-specific messages via optional `sessionId` +- Uses 8 severity levels (RFC 5424/MCP-compatible) +- Requires capability negotiation for clean opt-in (no breaking changes) - Includes optional structured data and error codes - Follows ACP's existing naming conventions @@ -81,6 +82,26 @@ Add a new `log` notification method (agent → client) with the following struct | `data` | `object` | No | Additional structured data | | `_meta` | `object` | No | Extensibility metadata | +### Capability Declaration + +Clients that want to receive `log` notifications MUST declare the `logging` capability during `initialize`: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "protocolVersion": "0.1", + "clientCapabilities": { + "logging": {} + } + } +} +``` + +Agents MUST NOT send `log` notifications to clients that did not declare this capability. + ### Log Levels (RFC 5424) The `LogLevel` enum follows RFC 5424 syslog severity levels, which are also used by MCP: @@ -167,15 +188,43 @@ There are several reasons to keep logging separate from `session/update`: 3. **Opt-in presentation**: Not all clients care about every log message. By keeping logs separate, clients can choose their level of engagement without polluting the conversation thread. -### Why not require capability negotiation for logging? +### Should logging require capability negotiation? + +**Yes.** Agents MUST only send `log` notifications to clients that declare support via a `logging` capability in `ClientCapabilities` during `initialize`. + +This works because: + +1. **Initialize handles startup errors**: The `initialize` method is a request/response—if something is broken during startup, the agent responds with a JSON-RPC error. There's no "pre-initialize" gap where we need unsolicited notifications. + +2. **Clean opt-in**: Clients explicitly declare they want log notifications. Older clients that don't support `log` simply don't receive them—no SDK changes needed, no breaking changes. + +3. **Follows MCP pattern**: MCP requires a `logging` capability for the same reasons. + +**Flow:** +1. Transport connects +2. Client calls `initialize()` with `clientCapabilities: { logging: {} }` +3. If startup fails → Agent responds with JSON-RPC error +4. If startup succeeds → Agent knows client supports `log`, can send notifications + +Clients that don't declare `logging` capability won't receive `log` notifications, but can still receive JSON-RPC errors in response to their method calls. + +### Why not just return errors on `prompt()` responses? + +The key question is: **should the run stop?** + +A JSON-RPC error response terminates the request—the run is over. But many situations warrant notifying the client *without* aborting: + +1. **Non-fatal issues during long runs**: A prompt turn can run for minutes. If one MCP server disconnects but others are fine, the agent might continue with degraded functionality. The client should know about the issue, but the run shouldn't stop. + +2. **Warnings and informational messages**: "Approaching rate limit" or "Using fallback model" are useful to surface, but definitely shouldn't abort the run. -We considered requiring a `logging` capability (like MCP does), but rejected it because: +3. **Connection-wide issues**: Some errors affect all sessions (server shutting down soon). There's no single prompt to attach these to. -1. **Pre-initialize errors**: Connection-level errors can occur *before* `initialize` completes. If the agent can't send logs until capabilities are negotiated, critical startup errors would be lost. +4. **Out-of-band timing**: Errors can occur between prompt turns, when no request is pending. -2. **Basic infrastructure**: Logging is fundamental enough that all clients should be able to receive it (even if they choose to ignore it). Making it capability-gated adds friction without clear benefit. +5. **Alternative interaction patterns**: Not all ACP implementations use `prompt()`. Some agents use custom extension methods like `_enqueue` (a notification for steering during a run). Since notifications don't have responses, there's no place to return a JSON-RPC error. -Clients that don't want to display logs can simply ignore `log` notifications. +The `log` notification provides an independent channel for diagnostic messages that don't terminate the current operation. ### When should agents use `log` vs JSON-RPC error responses? @@ -194,14 +243,13 @@ The rule of thumb: if there's a pending request that caused the error, respond w ### Is this a breaking change? -**No.** Per JSON-RPC 2.0, notifications are fire-and-forget—the sender does not expect a response. If a client receives a notification for a method it doesn't recognize, it simply ignores it. This is standard JSON-RPC behavior. +**No.** Because logging requires capability negotiation: -This means: -- Existing clients will safely ignore `log` notifications from updated agents -- Updated clients will work with older agents that don't send `log` notifications -- No version negotiation or capability exchange is required +- **Older clients** don't declare `logging` capability → agents don't send `log` notifications → no change in behavior +- **Older agents** don't send `log` notifications → updated clients just don't receive any → no change in behavior +- **Updated clients + updated agents** → client declares `logging` capability → agent sends `log` notifications → new feature works -The proposal is fully backward-compatible. +No SDK changes are required for backward compatibility. Clients opt-in by declaring the capability. ### Why use RFC 5424 log levels instead of simpler error/warning/info? From 2e2268dfed4ab247ed18a34e571b57433735d0ac Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 12:25:02 -0500 Subject: [PATCH 08/15] Clean up and finalize RFD for server-to-client logging Major revision: - Require capability negotiation (non-breaking, opt-in) - Lead with key insight: JSON-RPC errors terminate, log doesn't - Consolidate FAQ from 12 to 8 focused questions - Add "why not separate channel" FAQ (transport-agnostic, etc.) - Fix connection-wide example (before session/new, not multi-session) - Remove redundant/contradictory text - 176 lines (down from 323) Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 311 ++++++++++------------------------- 1 file changed, 88 insertions(+), 223 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index e47e88d..ae6b13d 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -8,314 +8,179 @@ Author(s): [@chazcb](https://github.com/chazcb) > What are you proposing to change? -Add a `log` notification that allows agents to send diagnostic messages (errors, warnings, info, etc.) to clients. This notification supports both connection-wide and session-specific messages via an optional `sessionId` field. +Add a `log` notification that allows agents to send diagnostic messages to clients without terminating operations. This enables real-time visibility into agent health, warnings, and errors. -Currently, servers have no way to proactively notify clients about errors, warnings, or other diagnostic information outside of request-response cycles. This RFD proposes a simple, flexible logging mechanism that: - -- Supports both connection-wide and session-specific messages via optional `sessionId` -- Uses 8 severity levels (RFC 5424/MCP-compatible) -- Requires capability negotiation for clean opt-in (no breaking changes) -- Includes optional structured data and error codes -- Follows ACP's existing naming conventions +**Key insight**: JSON-RPC error responses terminate the request. But many situations (MCP server disconnected, approaching rate limit, using fallback model) warrant notifying the client *without* stopping the run. ## Status quo -> How do things work today and what problems does this cause? Why would we change things? - -Today, ACP has several limitations around server-to-client diagnostic messaging: +> How do things work today and what problems does this cause? -1. **JSON-RPC errors are response-only**: The JSON-RPC 2.0 specification only allows errors in response to method calls. Servers cannot proactively send error notifications. +ACP has no mechanism for agents to proactively notify clients about diagnostic information: -2. **SessionNotification requires a session**: The existing `session/update` notification requires a `sessionId`. This means servers cannot communicate errors that occur: - - Before any session is created - - During connection setup or MCP server initialization - - At the connection level (affecting all sessions) +1. **JSON-RPC errors terminate requests**: If an agent returns an error, the run stops. There's no way to report non-fatal issues. -3. **No severity levels**: There's no standard way to indicate whether a message is an error, warning, informational, or debug-level. +2. **`session/update` requires a session**: Connection-wide errors (MCP server failures, server shutting down) can't be communicated because there's no session to attach them to. -4. **Transport-agnostic gap**: ACP supports multiple transports (stdio, WebSocket, HTTP). While the stdio transport allows informal stderr logging, other transports have no equivalent. A protocol-level logging mechanism ensures consistent behavior across all transports. +3. **Errors during long runs are invisible**: A prompt turn can run for minutes. If something goes wrong mid-run but the agent can recover, the client has no visibility. -### Real-world problems this causes +4. **No standard severity levels**: No way to distinguish errors from warnings from informational messages. -- **MCP server connection failures**: When an agent fails to connect to an MCP server, there's no way to inform the client. The user just sees tools not working. -- **Rate limiting**: When a provider rate-limits requests, the server can only fail the current request. It cannot proactively warn about approaching limits or advise the client to wait. -- **Transient errors**: Connection timeouts, retries, and other transient issues are invisible to clients. -- **Debugging**: Developers have no visibility into agent behavior without external logging infrastructure. +**Real-world examples:** +- MCP server disconnects, but other tools still work → client should know, but run continues +- Approaching rate limit → warn the user, don't abort +- Using fallback model → informational, definitely don't abort +- Server shutting down in 5 minutes → warn all sessions ## What we propose to do about it > What are you proposing to improve the situation? -Add a new `log` notification method (agent → client) with the following structure: +Add a `log` notification (agent → client) that requires capability negotiation: ```json { "jsonrpc": "2.0", "method": "log", "params": { - "level": "error", + "level": "warning", + "message": "MCP server 'database-tools' disconnected, retrying...", "sessionId": "abc-123", - "logger": "mcp.database", - "message": "Connection to MCP server 'database-tools' failed", + "logger": "mcp", "code": -32001, "timestamp": "2025-01-21T10:30:00Z", - "data": { - "server": "database-tools", - "error": "ECONNREFUSED", - "retryIn": 5 - }, - "_meta": {} + "data": { "server": "database-tools", "retryIn": 5 } } } ``` -### Fields - -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `level` | `LogLevel` | Yes | Severity level of the message | -| `message` | `string` | Yes | Human-readable log message | -| `sessionId` | `SessionId` | No | Session this message pertains to. Omit for connection-wide messages. | -| `logger` | `string` | No | Name of the logger/component emitting this message (e.g., "mcp.database", "auth") | -| `code` | `ErrorCode` | No | Structured error code (uses same space as JSON-RPC errors) | -| `timestamp` | `string` | No | ISO 8601 timestamp when the event occurred | -| `data` | `object` | No | Additional structured data | -| `_meta` | `object` | No | Extensibility metadata | - ### Capability Declaration -Clients that want to receive `log` notifications MUST declare the `logging` capability during `initialize`: +Clients opt-in by declaring `logging` capability during `initialize`: ```json { - "jsonrpc": "2.0", - "id": 1, "method": "initialize", "params": { - "protocolVersion": "0.1", - "clientCapabilities": { - "logging": {} - } + "clientCapabilities": { "logging": {} } } } ``` -Agents MUST NOT send `log` notifications to clients that did not declare this capability. - -### Log Levels (RFC 5424) - -The `LogLevel` enum follows RFC 5424 syslog severity levels, which are also used by MCP: +Agents MUST NOT send `log` notifications to clients that didn't declare this capability. This ensures backward compatibility—older clients simply don't receive notifications they can't handle. -| Level | Description | -|-------|-------------| -| `debug` | Detailed debugging information | -| `info` | General informational messages | -| `notice` | Normal but significant events | -| `warning` | Warning conditions | -| `error` | Error conditions | -| `critical` | Critical conditions | -| `alert` | Action must be taken immediately | -| `emergency` | System is unusable | - -### Why `log` (no namespace)? +### Fields -The method name follows ACP's convention for connection-level operations: +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `level` | `LogLevel` | Yes | RFC 5424 severity: `debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency` | +| `message` | `string` | Yes | Human-readable message | +| `sessionId` | `SessionId` | No | Omit for connection-wide messages | +| `logger` | `string` | No | Component name (e.g., "mcp", "auth") | +| `code` | `ErrorCode` | No | Structured error code | +| `timestamp` | `string` | No | ISO 8601 timestamp | +| `data` | `object` | No | Additional structured data | +| `_meta` | `object` | No | Extensibility metadata | -| Pattern | Examples | -|---------|----------| -| No namespace (connection-level) | `initialize`, `authenticate`, **`log`** | -| Resource namespace | `session/*`, `fs/*`, `terminal/*` | -| Protocol-level | `$/cancel_request` | +### Method Naming -We chose `log` over alternatives: -- `log/message` - Creates a namespace with only one method -- `notifications/message` - ACP doesn't use the `notifications/` pattern -- `SessionUpdate::Log` - Can't handle connection-wide messages +The method name `log` follows ACP's convention for connection-level operations (like `initialize`, `authenticate`) rather than using a namespace like `log/message`. ## Shiny future -> How will things will play out once this feature exists? - -Once this feature exists: - -1. **Better error visibility**: Clients can display connection errors, rate limit warnings, and other diagnostic information to users. IDEs could show a status indicator or notification panel for agent health. - -2. **Proactive warnings**: Servers can warn about approaching limits (token budget, rate limits) before they become errors, allowing clients to adapt. - -3. **Debugging support**: Developers can see what's happening inside agents without external logging infrastructure. The `logger` field allows filtering by component. +> How will things play out once this feature exists? -4. **Session-scoped context**: Errors related to specific sessions can be attributed correctly, while connection-wide issues (like MCP server failures) are clearly distinguished. - -5. **Structured error handling**: The optional `code` field allows clients to programmatically handle specific error types (retry on rate limit, prompt for auth on auth errors, etc.). +1. **Real-time health visibility**: Clients can show agent status—errors in a panel, warnings in a status bar +2. **Non-fatal error reporting**: Agents can report issues without aborting runs +3. **Proactive warnings**: "Approaching rate limit" before it becomes an error +4. **Debugging**: Developers see what's happening without external infrastructure ## Frequently asked questions -> What questions have arisen over the course of authoring this document or during subsequent discussions? - -### How does this relate to the [Agent Telemetry Export RFD](./agent-telemetry-export)? - -They are **complementary**, serving different purposes: - -| Aspect | `log` notification (this RFD) | OTEL Telemetry Export | -|--------|------------------------------|----------------------| -| **Channel** | In-band (ACP protocol messages) | Out-of-band (HTTP to OTLP collector) | -| **Purpose** | Real-time user-facing notifications | Observability/debugging infrastructure | -| **Audience** | End users via client UI | Developers, ops teams, observability backends | -| **Transport** | Works on all ACP transports | Requires HTTP (stdio subprocess focus) | -| **Volume** | Low (errors, warnings, key events) | High (traces, spans, metrics, debug logs) | - -**When to use which:** - -- **`log` notification**: User-relevant events that should appear in the client UI - - "Rate limit exceeded - waiting 60s" → User sees toast/status bar - - "MCP server disconnected" → User knows why tools stopped working +### Why not use `session/update`? -- **OTEL telemetry**: Developer/ops diagnostics for debugging and monitoring - - Detailed span traces with timing - - Token usage metrics - - High-volume debug logs - - Integration with Datadog, Grafana, etc. +Several reasons: -Agents may emit both: a `log` notification for user-visible errors AND detailed OTEL telemetry for debugging the same issue. +1. **Separation of concerns**: `SessionUpdate` represents conversation state (messages, tool calls, mode changes). Logs are diagnostic metadata, not conversation content. Mixing them forces clients to filter out logs they don't want in the conversation UI. -### Why not add a variant to `SessionUpdate`? +2. **Breaking change**: Adding a log variant to `SessionUpdate` would send logs to existing clients that don't expect them. A separate capability-gated notification is opt-in and non-breaking. -There are several reasons to keep logging separate from `session/update`: +3. **Opt-in presentation**: Not all clients care about logs. A separate channel lets clients ignore them entirely without polluting the conversation stream. -1. **Connection-level messages are essential**: Errors can occur before any session exists (during initialization, MCP server connection, authentication). A session-scoped mechanism simply cannot handle these cases. +4. **Connection-wide messages**: Errors can occur before any session exists (e.g., MCP server failing to connect after `initialize` but before `session/new`). `session/update` requires a `sessionId`, so it simply can't be used in these cases. -2. **Separation of concerns**: Log messages are conceptually different from conversation state. `SessionUpdate` represents the actual conversation flow (user messages, agent responses, tool calls, mode changes). Mixing diagnostic notifications into that stream forces clients to filter out messages they may not want to display in the conversation UI. A separate channel lets clients handle logs independently—show them in a status bar, a separate panel, or ignore them entirely. +### Why not use a separate channel (stderr, separate SSE endpoint, etc.)? -3. **Opt-in presentation**: Not all clients care about every log message. By keeping logs separate, clients can choose their level of engagement without polluting the conversation thread. +1. **Transport-agnostic**: ACP works over stdio, WebSocket, HTTP, etc. A side channel like stderr only exists for stdio. A protocol-level solution works across all transports. -### Should logging require capability negotiation? +2. **Already connected**: Clients already have an ACP connection. Why require a separate channel just for logs? -**Yes.** Agents MUST only send `log` notifications to clients that declare support via a `logging` capability in `ClientCapabilities` during `initialize`. +3. **Session context**: The `sessionId` field ties logs to specific sessions. A separate channel would need to reinvent this. -This works because: +4. **Capability negotiation**: ACP already has capability negotiation. A separate channel would need its own opt-in mechanism. -1. **Initialize handles startup errors**: The `initialize` method is a request/response—if something is broken during startup, the agent responds with a JSON-RPC error. There's no "pre-initialize" gap where we need unsolicited notifications. +5. **OTEL handles heavy telemetry**: The [Agent Telemetry Export RFD](./agent-telemetry-export) covers high-volume, developer-focused observability. `log` is for low-volume, user-facing messages—different use case, belongs in-band. -2. **Clean opt-in**: Clients explicitly declare they want log notifications. Older clients that don't support `log` simply don't receive them—no SDK changes needed, no breaking changes. - -3. **Follows MCP pattern**: MCP requires a `logging` capability for the same reasons. - -**Flow:** -1. Transport connects -2. Client calls `initialize()` with `clientCapabilities: { logging: {} }` -3. If startup fails → Agent responds with JSON-RPC error -4. If startup succeeds → Agent knows client supports `log`, can send notifications - -Clients that don't declare `logging` capability won't receive `log` notifications, but can still receive JSON-RPC errors in response to their method calls. - -### Why not just return errors on `prompt()` responses? +### Why not return errors on `prompt()`? The key question is: **should the run stop?** -A JSON-RPC error response terminates the request—the run is over. But many situations warrant notifying the client *without* aborting: - -1. **Non-fatal issues during long runs**: A prompt turn can run for minutes. If one MCP server disconnects but others are fine, the agent might continue with degraded functionality. The client should know about the issue, but the run shouldn't stop. - -2. **Warnings and informational messages**: "Approaching rate limit" or "Using fallback model" are useful to surface, but definitely shouldn't abort the run. - -3. **Connection-wide issues**: Some errors affect all sessions (server shutting down soon). There's no single prompt to attach these to. - -4. **Out-of-band timing**: Errors can occur between prompt turns, when no request is pending. +- JSON-RPC error responses terminate the request—the run is over +- `log` notifications are independent—the run continues -5. **Alternative interaction patterns**: Not all ACP implementations use `prompt()`. Some agents use custom extension methods like `_enqueue` (a notification for steering during a run). Since notifications don't have responses, there's no place to return a JSON-RPC error. +Many situations (MCP server disconnected but others work, approaching rate limit, using fallback model) warrant notifying the client without aborting. -The `log` notification provides an independent channel for diagnostic messages that don't terminate the current operation. +Also: not all agents use `prompt()`. Some use extension methods like `_enqueue` (a notification). Notifications have no response, so there's nowhere to return an error. -### When should agents use `log` vs JSON-RPC error responses? +### How does this relate to the [Agent Telemetry Export RFD](./agent-telemetry-export)? -**JSON-RPC errors should still be used when possible.** The `log` notification is specifically for out-of-band situations where there's no request to respond to: +They're complementary: -| Situation | Use | -|-----------|-----| -| Request fails (invalid params, auth required, etc.) | JSON-RPC error response | -| Connection to upstream service lost | `log` notification | -| Rate limit hit during request processing | JSON-RPC error response | -| Approaching rate limit (proactive warning) | `log` notification | -| MCP server disconnected | `log` notification | -| Server shutting down soon | `log` notification | +| | `log` notification | OTEL Telemetry | +|-|-------------------|----------------| +| **Channel** | In-band (ACP) | Out-of-band (HTTP) | +| **Audience** | End users | Developers/ops | +| **Volume** | Low (key events) | High (traces, metrics) | -The rule of thumb: if there's a pending request that caused the error, respond with a JSON-RPC error. If the error/warning is unsolicited (no request triggered it), use `log`. +Use `log` for user-facing messages. Use OTEL for observability infrastructure. ### Is this a breaking change? -**No.** Because logging requires capability negotiation: +**No.** Capability negotiation makes it opt-in: +- Older clients don't declare capability → don't receive notifications +- Older agents don't send notifications → nothing changes -- **Older clients** don't declare `logging` capability → agents don't send `log` notifications → no change in behavior -- **Older agents** don't send `log` notifications → updated clients just don't receive any → no change in behavior -- **Updated clients + updated agents** → client declares `logging` capability → agent sends `log` notifications → new feature works +### When should agents use `log` vs JSON-RPC errors? -No SDK changes are required for backward compatibility. Clients opt-in by declaring the capability. +| Situation | Use | +|-----------|-----| +| Request fails | JSON-RPC error (terminates request) | +| Non-fatal issue during run | `log` notification (run continues) | +| Proactive warning | `log` notification | +| Connection-wide issue | `log` notification | -### Why use RFC 5424 log levels instead of simpler error/warning/info? +Rule: If there's a pending request that should fail, use JSON-RPC error. Otherwise, use `log`. -RFC 5424 levels are a well-established standard used by syslog, many logging libraries, and MCP. Using the same levels: +### Why RFC 5424 log levels instead of just error/warning/info? -- Provides familiar semantics for developers -- Enables compatibility with MCP logging +RFC 5424 (syslog) levels are used by MCP, most logging libraries, and observability tools. Using the same 8 levels: +- Provides familiar semantics +- Enables MCP compatibility - Supports future use cases (debug logging, critical alerts) -Clients can always map these to simpler categories in their UI. - -### How should clients handle log messages? - -Clients have flexibility in how they present log messages: - -- **IDE status bar**: Show counts of errors/warnings with hover details -- **Notification toasts**: Pop up for critical/alert/emergency levels -- **Log panel**: Dedicated panel showing filterable log history -- **Ignore**: For levels below a configured threshold - -The `logger` field can help clients filter/categorize messages. - -### Can agents flood clients with log messages? - -Yes, like any notification. Clients should: - -- Rate-limit UI updates -- Drop old messages if buffer is full -- Consider filtering by level - -Agents should be reasonable about log frequency. - -### What alternative approaches did you consider, and why did you settle on this one? - -1. **Tunneling over `SessionUpdate`**: Rejected because it can't handle connection-wide messages and mixes concerns. - -2. **JSON-RPC error responses**: Not possible - JSON-RPC only allows errors in response to requests. - -3. **Out-of-band telemetry (like agent-telemetry-export RFD)**: Good for observability backends but doesn't solve in-band client notification needs. - -4. **Transport-specific logging (e.g., stderr)**: Only works for stdio transport. ACP is transport-agnostic, so we need a protocol-level solution that works over WebSocket, HTTP, and any future transports. - -5. **New capability negotiation**: Considered requiring a capability, but logging is basic infrastructure that all clients should handle (even if just ignoring it). - -The proposed `log` notification is the simplest solution that: -- Works at all lifecycle stages -- Supports both session-scoped and connection-wide messages -- Uses standard log levels -- Fits ACP's existing patterns -- Works identically across all transports +Clients can always map to simpler categories in their UI. ### How does this compare to other protocols? -| Protocol | Method | Levels | Scoping | Notes | -|----------|--------|--------|---------|-------| -| **ACP (proposed)** | `log` | RFC 5424 (8 levels) | Optional `sessionId` | Connection-wide or session-scoped | -| **[MCP](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging)** | `notifications/message` | RFC 5424 (8 levels) | None (server-wide) | Requires `logging` capability; has `logging/setLevel` request | -| **[LSP](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_logMessage)** | `window/logMessage` | 5 levels (Error→Debug) | None | Separate `window/showMessage` for UI display | -| **[A2A](https://a2a-protocol.org/latest/specification/)** | None (via `TaskStatus.message`) | None | Task-scoped | Errors via binding-specific mechanisms (JSON-RPC, gRPC, HTTP Problem Details) | -| **[AG-UI](https://docs.ag-ui.com/concepts/events)** | `RunError` event | None | Run-scoped | Event-driven; uses `StepStarted`/`StepFinished` for progress | - -**Key design choices:** +| Protocol | Method | Levels | Scoping | +|----------|--------|--------|---------| +| **ACP (proposed)** | `log` | RFC 5424 (8) | Optional `sessionId` | +| **[MCP](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging)** | `notifications/message` | RFC 5424 (8) | Server-wide only | +| **[LSP](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_logMessage)** | `window/logMessage` | 5 levels | None | +| **[A2A](https://a2a-protocol.org/latest/specification/)** | None | None | Task-scoped | +| **[AG-UI](https://docs.ag-ui.com/concepts/events)** | `RunError` event | None | Run-scoped | -- **RFC 5424 levels**: Aligns with MCP for ecosystem consistency. More expressive than LSP's 5 levels. -- **Optional `sessionId`**: Unlike MCP (server-wide only) or A2A/AG-UI (task/run-scoped only), ACP supports both connection-wide AND session-scoped messages. -- **Single method**: Unlike LSP which separates `logMessage` (background) from `showMessage` (UI), ACP uses one method with levels. Clients decide presentation. +ACP's optional `sessionId` is unique—supports both connection-wide AND session-scoped messages. ## Revision history From 0625898aa694a32ccd971683a0d5be3fc82604cb Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 12:31:42 -0500 Subject: [PATCH 09/15] fix: use backing model/service examples instead of MCP disconnection MCP server disconnection is a session-level concern, not a good example for run-time log notifications. Better examples are backing model rate limiting and backing service disconnection. Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index ae6b13d..be0c74e 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -8,9 +8,9 @@ Author(s): [@chazcb](https://github.com/chazcb) > What are you proposing to change? -Add a `log` notification that allows agents to send diagnostic messages to clients without terminating operations. This enables real-time visibility into agent health, warnings, and errors. +Add a `log` notification that allows agents to send diagnostic messages to clients without terminating operations. -**Key insight**: JSON-RPC error responses terminate the request. But many situations (MCP server disconnected, approaching rate limit, using fallback model) warrant notifying the client *without* stopping the run. +JSON-RPC error responses terminate the request—the run is over. But many situations (approaching rate limit, using fallback model, retrying a failed request) warrant notifying the client *without* stopping the run. This proposal adds that capability. ## Status quo @@ -27,9 +27,9 @@ ACP has no mechanism for agents to proactively notify clients about diagnostic i 4. **No standard severity levels**: No way to distinguish errors from warnings from informational messages. **Real-world examples:** -- MCP server disconnects, but other tools still work → client should know, but run continues -- Approaching rate limit → warn the user, don't abort -- Using fallback model → informational, definitely don't abort +- Backing model rate limited → warn the user, retry in progress +- Backing service disconnected → informational, reconnecting +- Using fallback model → informational, run continues - Server shutting down in 5 minutes → warn all sessions ## What we propose to do about it @@ -44,12 +44,12 @@ Add a `log` notification (agent → client) that requires capability negotiation "method": "log", "params": { "level": "warning", - "message": "MCP server 'database-tools' disconnected, retrying...", + "message": "Backing model rate limited, retrying in 5 seconds...", "sessionId": "abc-123", - "logger": "mcp", + "logger": "model", "code": -32001, "timestamp": "2025-01-21T10:30:00Z", - "data": { "server": "database-tools", "retryIn": 5 } + "data": { "model": "claude-3", "retryIn": 5 } } } ``` @@ -128,7 +128,7 @@ The key question is: **should the run stop?** - JSON-RPC error responses terminate the request—the run is over - `log` notifications are independent—the run continues -Many situations (MCP server disconnected but others work, approaching rate limit, using fallback model) warrant notifying the client without aborting. +Many situations (backing model rate limited, approaching context limit, using fallback model) warrant notifying the client without aborting. Also: not all agents use `prompt()`. Some use extension methods like `_enqueue` (a notification). Notifications have no response, so there's nowhere to return an error. From cce5266a099045461d81ecae720729acb0b57a0c Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 12:33:25 -0500 Subject: [PATCH 10/15] fix: remove MCP server failure examples from connection-wide errors Use generic examples (server shutting down, configuration problems) instead of MCP-specific examples for connection-wide error scenarios. Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index be0c74e..4799c98 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -20,7 +20,7 @@ ACP has no mechanism for agents to proactively notify clients about diagnostic i 1. **JSON-RPC errors terminate requests**: If an agent returns an error, the run stops. There's no way to report non-fatal issues. -2. **`session/update` requires a session**: Connection-wide errors (MCP server failures, server shutting down) can't be communicated because there's no session to attach them to. +2. **`session/update` requires a session**: Connection-wide issues (server shutting down, configuration problems) can't be communicated because there's no session to attach them to. 3. **Errors during long runs are invisible**: A prompt turn can run for minutes. If something goes wrong mid-run but the agent can recover, the client has no visibility. @@ -107,7 +107,7 @@ Several reasons: 3. **Opt-in presentation**: Not all clients care about logs. A separate channel lets clients ignore them entirely without polluting the conversation stream. -4. **Connection-wide messages**: Errors can occur before any session exists (e.g., MCP server failing to connect after `initialize` but before `session/new`). `session/update` requires a `sessionId`, so it simply can't be used in these cases. +4. **Connection-wide messages**: Errors can occur before any session exists (e.g., after `initialize` but before `session/new`). `session/update` requires a `sessionId`, so it simply can't be used in these cases. ### Why not use a separate channel (stderr, separate SSE endpoint, etc.)? From ac9c755eb054ce509a8aa59a1425d4193f1c4d57 Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 12:34:35 -0500 Subject: [PATCH 11/15] docs: add FAQ for why not use agent text messages for diagnostics Addresses the alternative of sending diagnostic info as fake agent_message_chunk content. Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 4799c98..7a7d779 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -109,6 +109,18 @@ Several reasons: 4. **Connection-wide messages**: Errors can occur before any session exists (e.g., after `initialize` but before `session/new`). `session/update` requires a `sessionId`, so it simply can't be used in these cases. +### Why not send diagnostics as agent text messages? + +Sending diagnostic info as `agent_message_chunk` would conflate conversation content with status messages: + +1. **Wrong semantics**: Agent messages are conversation history. Diagnostics are ephemeral status. "Rate limit warning from 2 hours ago" shouldn't appear when you reload a session. + +2. **No severity levels**: Text chunks can't express error vs warning vs info. + +3. **Filtering burden**: Clients would need to parse messages to separate "real" responses from diagnostics. How? Magic prefix? `_meta` field? Both are hacky. + +4. **Still requires sessionId**: Doesn't solve connection-wide messages. + ### Why not use a separate channel (stderr, separate SSE endpoint, etc.)? 1. **Transport-agnostic**: ACP works over stdio, WebSocket, HTTP, etc. A side channel like stderr only exists for stdio. A protocol-level solution works across all transports. From 55cd96f16e2ad0435d2e6899c363978e40cca4df Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 12:55:39 -0500 Subject: [PATCH 12/15] docs: clarify log notifications are purely informational - Add "purely informational" to elevator pitch - Add explicit statement that clients MAY display but MUST NOT take automated action - Remove top-level code field (error codes don't make sense for info/debug messages) - Clarify data field is opaque, for display/debugging only Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 7a7d779..1ac2201 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -8,9 +8,9 @@ Author(s): [@chazcb](https://github.com/chazcb) > What are you proposing to change? -Add a `log` notification that allows agents to send diagnostic messages to clients without terminating operations. +Add a `log` notification that allows agents to send purely informational diagnostic messages to clients without terminating operations. -JSON-RPC error responses terminate the request—the run is over. But many situations (approaching rate limit, using fallback model, retrying a failed request) warrant notifying the client *without* stopping the run. This proposal adds that capability. +JSON-RPC error responses terminate the request—the run is over. But many situations (approaching rate limit, using fallback model, retrying a failed request) warrant informing the client *without* stopping the run. This proposal adds that capability. ## Status quo @@ -47,7 +47,6 @@ Add a `log` notification (agent → client) that requires capability negotiation "message": "Backing model rate limited, retrying in 5 seconds...", "sessionId": "abc-123", "logger": "model", - "code": -32001, "timestamp": "2025-01-21T10:30:00Z", "data": { "model": "claude-3", "retryIn": 5 } } @@ -69,6 +68,8 @@ Clients opt-in by declaring `logging` capability during `initialize`: Agents MUST NOT send `log` notifications to clients that didn't declare this capability. This ensures backward compatibility—older clients simply don't receive notifications they can't handle. +Log notifications are purely informational. Clients MAY display them to users but MUST NOT take automated action based on their contents. + ### Fields | Field | Type | Required | Description | @@ -76,10 +77,9 @@ Agents MUST NOT send `log` notifications to clients that didn't declare this cap | `level` | `LogLevel` | Yes | RFC 5424 severity: `debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency` | | `message` | `string` | Yes | Human-readable message | | `sessionId` | `SessionId` | No | Omit for connection-wide messages | -| `logger` | `string` | No | Component name (e.g., "mcp", "auth") | -| `code` | `ErrorCode` | No | Structured error code | +| `logger` | `string` | No | Component name (e.g., "model", "auth") | | `timestamp` | `string` | No | ISO 8601 timestamp | -| `data` | `object` | No | Additional structured data | +| `data` | `object` | No | Additional context for display/debugging (opaque to clients) | | `_meta` | `object` | No | Extensibility metadata | ### Method Naming From 5945eed42c17c85208185166b008c4448b80bc71 Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 13:06:53 -0500 Subject: [PATCH 13/15] docs: simplify elevator pitch to one-liner The "why" is already covered in Status quo (gaps/problems) and FAQ (why not alternatives). Elevator pitch should be punchy. Co-Authored-By: Claude --- docs/rfds/server-logging.mdx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 1ac2201..1926593 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -8,9 +8,7 @@ Author(s): [@chazcb](https://github.com/chazcb) > What are you proposing to change? -Add a `log` notification that allows agents to send purely informational diagnostic messages to clients without terminating operations. - -JSON-RPC error responses terminate the request—the run is over. But many situations (approaching rate limit, using fallback model, retrying a failed request) warrant informing the client *without* stopping the run. This proposal adds that capability. +Add a `log` notification that allows agents to send diagnostic messages to clients. ## Status quo From cabe11474ed1852ce2df346534cd41c9395e6543 Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 14:40:14 -0500 Subject: [PATCH 14/15] Update server-logging.mdx --- docs/rfds/server-logging.mdx | 183 ++++++++++++++--------------------- 1 file changed, 70 insertions(+), 113 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 1926593..01eed28 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -1,5 +1,5 @@ --- -title: "Server-to-Client Logging" +title: "Agent-to-Client Logging" --- Author(s): [@chazcb](https://github.com/chazcb) @@ -8,189 +8,146 @@ Author(s): [@chazcb](https://github.com/chazcb) > What are you proposing to change? -Add a `log` notification that allows agents to send diagnostic messages to clients. +Introduce a capability-gated `log` notification (agent → client) so agents can share diagnostic messages without polluting conversation history. ## Status quo -> How do things work today and what problems does this cause? +> How do things work today and what problems does this cause? Why would we change things? -ACP has no mechanism for agents to proactively notify clients about diagnostic information: +Today, agents have limited ways to inform clients about status that might impact their experience. The two options are: -1. **JSON-RPC errors terminate requests**: If an agent returns an error, the run stops. There's no way to report non-fatal issues. +1. **JSON-RPC errors**: Terminate the request immediately with an informative error message the client can display to the user +2. **`session/update`**: Update conversation history with diagnostic information in the `agent_message_chunk` or other chat history notification -2. **`session/update` requires a session**: Connection-wide issues (server shutting down, configuration problems) can't be communicated because there's no session to attach them to. +But neither option works when: -3. **Errors during long runs are invisible**: A prompt turn can run for minutes. If something goes wrong mid-run but the agent can recover, the client has no visibility. +- There's no active JSON RPC request to attach an error response to +- We don't want to fail the request (e.g., retries, rate limiting, fallback selection) +- There's no session yet (diagnostics after `initialize` but before `session/start`) +- We don't want to put diagnostics in chat history, or to force clients to filter non-chat content, or to fake chat content just to send diagnostic logs, etc. -4. **No standard severity levels**: No way to distinguish errors from warnings from informational messages. - -**Real-world examples:** -- Backing model rate limited → warn the user, retry in progress -- Backing service disconnected → informational, reconnecting -- Using fallback model → informational, run continues -- Server shutting down in 5 minutes → warn all sessions +Without a way to surface these situations, users can be left confused when their ACP connection or session seem to stall or behave unexpectedly. ## What we propose to do about it > What are you proposing to improve the situation? -Add a `log` notification (agent → client) that requires capability negotiation: +Add a `log` JSON-RPC notification that is explicitly capability-gated. Clients opt in via `clientCapabilities.logging`; agents only send logs to clients that declare the capability. Clients can optionally specify a minimum log level. ```json { - "jsonrpc": "2.0", - "method": "log", + "method": "initialize", "params": { - "level": "warning", - "message": "Backing model rate limited, retrying in 5 seconds...", - "sessionId": "abc-123", - "logger": "model", - "timestamp": "2025-01-21T10:30:00Z", - "data": { "model": "claude-3", "retryIn": 5 } + "clientCapabilities": { + "logging": { + "level": "warning" + } + } } } ``` -### Capability Declaration +If `level` is omitted, agents should default to `info`. Agents MUST NOT send logs below the client's requested level. -Clients opt-in by declaring `logging` capability during `initialize`: +### Method ```json { - "method": "initialize", + "jsonrpc": "2.0", + "method": "log", "params": { - "clientCapabilities": { "logging": {} } + "level": "warning", + "message": "Backing model rate limited, retrying in 5 seconds...", + "sessionId": "abc-123", + "logger": "model", + "timestamp": "2025-01-21T10:30:00Z", + "data": { "model": "claude-3", "retryIn": 5 } } } ``` -Agents MUST NOT send `log` notifications to clients that didn't declare this capability. This ensures backward compatibility—older clients simply don't receive notifications they can't handle. - -Log notifications are purely informational. Clients MAY display them to users but MUST NOT take automated action based on their contents. - ### Fields | Field | Type | Required | Description | |-------|------|----------|-------------| | `level` | `LogLevel` | Yes | RFC 5424 severity: `debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency` | -| `message` | `string` | Yes | Human-readable message | +| `message` | `string` | Yes | Human-readable summary safe for display | | `sessionId` | `SessionId` | No | Omit for connection-wide messages | | `logger` | `string` | No | Component name (e.g., "model", "auth") | -| `timestamp` | `string` | No | ISO 8601 timestamp | -| `data` | `object` | No | Additional context for display/debugging (opaque to clients) | +| `timestamp` | `string` | No | ISO 8601 timestamp if provided | +| `data` | `object` | No | Opaque context (clients must not depend on structure) | | `_meta` | `object` | No | Extensibility metadata | -### Method Naming +### Semantics + +- **Capability-gated**: Agents MUST NOT send `log` notification to clients that did not declare `clientCapabilities.logging`. +- **Level filtering**: Agents MUST NOT send logs below the client's requested level (default: `info`). +- **Informational only**: Clients MAY display logs but MUST NOT treat them as protocol-affecting or control flow signals. +- **Best-effort delivery**: Logs are not reliable transport and are not replayed on reconnect. +- **Session optional**: `sessionId` is optional; omitted logs are connection-wide. +- **Manageable volume**: Implementations should keep volume low and user-relevant. -The method name `log` follows ACP's convention for connection-level operations (like `initialize`, `authenticate`) rather than using a namespace like `log/message`. +### Method naming + +`log` follows ACP’s convention for connection-level operations (e.g., `initialize`, `authenticate`) rather than introducing a new namespace. ## Shiny future > How will things play out once this feature exists? -1. **Real-time health visibility**: Clients can show agent status—errors in a panel, warnings in a status bar -2. **Non-fatal error reporting**: Agents can report issues without aborting runs -3. **Proactive warnings**: "Approaching rate limit" before it becomes an error -4. **Debugging**: Developers see what's happening without external infrastructure +- **Clear connection feedback**: Clients can surface warnings (rate limits, retries, fallbacks) so users understand what's happening. +- **No more mysterious stalls**: Users see why things are slow (retries, rate limits) rather than assuming a hang. +- **Better developer experience**: Diagnostics are visible without requiring OTEL or external logging. +- **No compatibility risk**: Capability gating means legacy clients are unaffected. -## Frequently asked questions +## Implementation details and plan -### Why not use `session/update`? +> Tell me more about your implementation. What is your detailed implementation plan? -Several reasons: +1. **Schema**: Add a `LogLevel` enum and a `LogNotification` params schema with the fields above. +2. **Capabilities**: Add `clientCapabilities.logging` with optional `level` field for minimum severity filtering. +3. **Protocol**: Add `log` to method tables and route it through notification handling. +4. **Docs**: Update protocol docs and examples to show capability negotiation and sample logs. -1. **Separation of concerns**: `SessionUpdate` represents conversation state (messages, tool calls, mode changes). Logs are diagnostic metadata, not conversation content. Mixing them forces clients to filter out logs they don't want in the conversation UI. +## Frequently asked questions -2. **Breaking change**: Adding a log variant to `SessionUpdate` would send logs to existing clients that don't expect them. A separate capability-gated notification is opt-in and non-breaking. +> What questions have arisen over the course of authoring this document or during subsequent discussions? -3. **Opt-in presentation**: Not all clients care about logs. A separate channel lets clients ignore them entirely without polluting the conversation stream. +### Why not use `session/update`? -4. **Connection-wide messages**: Errors can occur before any session exists (e.g., after `initialize` but before `session/new`). `session/update` requires a `sessionId`, so it simply can't be used in these cases. +`session/update` represents conversation state. Logs are diagnostic metadata and should not appear in chat history or require clients to filter out non-conversation content. `session/update` also can't represent connection-wide issues because it requires `sessionId`. ### Why not send diagnostics as agent text messages? -Sending diagnostic info as `agent_message_chunk` would conflate conversation content with status messages: - -1. **Wrong semantics**: Agent messages are conversation history. Diagnostics are ephemeral status. "Rate limit warning from 2 hours ago" shouldn't appear when you reload a session. - -2. **No severity levels**: Text chunks can't express error vs warning vs info. - -3. **Filtering burden**: Clients would need to parse messages to separate "real" responses from diagnostics. How? Magic prefix? `_meta` field? Both are hacky. - -4. **Still requires sessionId**: Doesn't solve connection-wide messages. - -### Why not use a separate channel (stderr, separate SSE endpoint, etc.)? - -1. **Transport-agnostic**: ACP works over stdio, WebSocket, HTTP, etc. A side channel like stderr only exists for stdio. A protocol-level solution works across all transports. +Agent messages are persistent conversation content. Logs are ephemeral status and should not be reloaded or forked with the session. Agent text also lacks severity levels and would require ad-hoc parsing to separate real answers from diagnostics. -2. **Already connected**: Clients already have an ACP connection. Why require a separate channel just for logs? +### Why not use a separate channel (stderr, SSE side channel, etc.)? -3. **Session context**: The `sessionId` field ties logs to specific sessions. A separate channel would need to reinvent this. - -4. **Capability negotiation**: ACP already has capability negotiation. A separate channel would need its own opt-in mechanism. - -5. **OTEL handles heavy telemetry**: The [Agent Telemetry Export RFD](./agent-telemetry-export) covers high-volume, developer-focused observability. `log` is for low-volume, user-facing messages—different use case, belongs in-band. +ACP is transport-agnostic. A protocol-level log works uniformly across stdio, WebSocket, and HTTP, reuses capability negotiation, and allows optional session scoping without inventing a parallel channel. ### Why not return errors on `prompt()`? -The key question is: **should the run stop?** - -- JSON-RPC error responses terminate the request—the run is over -- `log` notifications are independent—the run continues +JSON-RPC errors terminate the request. Many conditions (rate limiting, retries, fallback selection) are non-fatal and should not end the run. Logs allow notification without aborting. -Many situations (backing model rate limited, approaching context limit, using fallback model) warrant notifying the client without aborting. +### Are logs ordered relative to other notifications? -Also: not all agents use `prompt()`. Some use extension methods like `_enqueue` (a notification). Notifications have no response, so there's nowhere to return an error. +No strict guarantees. Implementations may keep logs ordered with other notifications for readability, but clients must treat them as best-effort informational messages. -### How does this relate to the [Agent Telemetry Export RFD](./agent-telemetry-export)? +### Are logs replayed after reconnect? -They're complementary: +No. Logs are not part of session state and are not replayed. -| | `log` notification | OTEL Telemetry | -|-|-------------------|----------------| -| **Channel** | In-band (ACP) | Out-of-band (HTTP) | -| **Audience** | End users | Developers/ops | -| **Volume** | Low (key events) | High (traces, metrics) | +### How does this relate to Agent Telemetry Export? -Use `log` for user-facing messages. Use OTEL for observability infrastructure. +They are complementary: `log` is low-volume, user-facing diagnostics in-band; OTEL, as currently proposed, is for high-volume, developer/ops telemetry out-of-band. See `/docs/rfds/agent-telemetry-export`. ### Is this a breaking change? -**No.** Capability negotiation makes it opt-in: -- Older clients don't declare capability → don't receive notifications -- Older agents don't send notifications → nothing changes - -### When should agents use `log` vs JSON-RPC errors? - -| Situation | Use | -|-----------|-----| -| Request fails | JSON-RPC error (terminates request) | -| Non-fatal issue during run | `log` notification (run continues) | -| Proactive warning | `log` notification | -| Connection-wide issue | `log` notification | - -Rule: If there's a pending request that should fail, use JSON-RPC error. Otherwise, use `log`. - -### Why RFC 5424 log levels instead of just error/warning/info? - -RFC 5424 (syslog) levels are used by MCP, most logging libraries, and observability tools. Using the same 8 levels: -- Provides familiar semantics -- Enables MCP compatibility -- Supports future use cases (debug logging, critical alerts) - -Clients can always map to simpler categories in their UI. - -### How does this compare to other protocols? +No. It is opt-in via capability negotiation; older clients won't receive notifications they don't understand. -| Protocol | Method | Levels | Scoping | -|----------|--------|--------|---------| -| **ACP (proposed)** | `log` | RFC 5424 (8) | Optional `sessionId` | -| **[MCP](https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/logging)** | `notifications/message` | RFC 5424 (8) | Server-wide only | -| **[LSP](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_logMessage)** | `window/logMessage` | 5 levels | None | -| **[A2A](https://a2a-protocol.org/latest/specification/)** | None | None | Task-scoped | -| **[AG-UI](https://docs.ag-ui.com/concepts/events)** | `RunError` event | None | Run-scoped | +### Why RFC 5424 log levels instead of error/warning/info? -ACP's optional `sessionId` is unique—supports both connection-wide AND session-scoped messages. +RFC 5424 is widely used and aligns with MCP and common logging libraries. Clients can map to simpler categories in their UI. ## Revision history From ada9f5ef5da82c0736410486b8f868669c3576ef Mon Sep 17 00:00:00 2001 From: Charles Covey-Brandt Date: Wed, 21 Jan 2026 15:03:28 -0500 Subject: [PATCH 15/15] Update server-logging.mdx --- docs/rfds/server-logging.mdx | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/rfds/server-logging.mdx b/docs/rfds/server-logging.mdx index 01eed28..72b979f 100644 --- a/docs/rfds/server-logging.mdx +++ b/docs/rfds/server-logging.mdx @@ -23,7 +23,7 @@ But neither option works when: - There's no active JSON RPC request to attach an error response to - We don't want to fail the request (e.g., retries, rate limiting, fallback selection) -- There's no session yet (diagnostics after `initialize` but before `session/start`) +- There's no session yet (diagnostics after `initialize` but before `session/new`) - We don't want to put diagnostics in chat history, or to force clients to filter non-chat content, or to fake chat content just to send diagnostic logs, etc. Without a way to surface these situations, users can be left confused when their ACP connection or session seem to stall or behave unexpectedly. @@ -89,7 +89,31 @@ If `level` is omitted, agents should default to `info`. Agents MUST NOT send log ### Method naming -`log` follows ACP’s convention for connection-level operations (e.g., `initialize`, `authenticate`) rather than introducing a new namespace. +`log` follows ACP's convention for connection-level operations (e.g., `initialize`, `authenticate`) rather than introducing a new namespace. + +## Alternatives considered + +### Add a notification type to `session/update` + +Extend `session/update` with a new notification type for diagnostics. This keeps diagnostics within the existing session machinery but has drawbacks: it requires a session (can't send connection-wide diagnostics), risks polluting chat history unless clients explicitly filter, and overloads `session/update` with non-conversation concerns. Additionally, ACP specifies that session history is replayed on `session/load`, but diagnostic logs are transient and shouldn't be replayed—they're not part of the conversation. + +### Structured `status` notification + +Instead of general-purpose logging, define a more structured `status` notification explicitly for lightweight status info—similar to Claude Code's interim status messages ("Thinking...", "Searching files..."). This would be scoped to either the current session or the agent/connection level. + +**Tradeoffs**: More constrained semantics could be clearer for clients, but less flexible. Logging with severity levels is a well-understood pattern; inventing a new "status" abstraction may not add value over `log` with `level: info`. + +### Explicit progress or heartbeat notification + +Define a `progress` or `heartbeat` notification specifically for long-running operations, with structured fields like `percentComplete`, `estimatedTimeRemaining`, etc. + +**Tradeoffs**: Progress is better suited to `session/update` since it's about task state. Heartbeats could be useful but solve a different problem (connection liveness) than diagnostics. A `log` notification can express "retrying in 5s" without requiring structured progress semantics. + +### Transport-level mechanisms + +Use HTTP headers, WebSocket ping payloads, or other transport-level channels for status. + +**Tradeoffs**: ACP is transport-agnostic. Relying on transport-specific mechanisms would fragment implementations and lose capability negotiation. ## Shiny future