Files
kandev/docs/decisions/2026-07-30-session-owned-mcp-observability.md
T
2026-07-30 20:49:07 +01:00

120 lines
6.6 KiB
Markdown

# ADR-2026-07-30-session-owned-mcp-observability: Keep MCP Attachment Evidence Session Owned
**Status:** accepted
**Date:** 2026-07-30
**Area:** backend, frontend, protocol, security
## Context
Kandev configures MCP at several boundaries: agent profiles resolve servers,
executor policy can filter them, ACP adapters deliver supported transports,
passthrough strategies materialize CLI-specific configuration, and agentctl
hosts Kandev's task-aware MCP endpoint. The current toolbar collapses those
steps into a list derived from the profile, so “configured” is presented as if
it meant “attached.”
ACP `initialize` advertises transport capabilities and `session/new` accepts
`mcpServers`, but the protocol does not return a standard list of MCP servers
that connected successfully. A connection failure can also happen before the
target server observes any request. Raw ACP and stderr capture would improve
diagnosis but can contain prompts, files, tool arguments, URLs, headers,
environment values, and credentials, making unconditional release logging an
unacceptable privacy boundary.
Concurrent agents make global status invalid. A task can own several Kandev
sessions, and a single session can restart into several execution generations
or reset its provider session inside one execution. An old successful
connection must not make a new attachment attempt appear healthy.
## Decision
MCP attachment observability is owned by the Kandev session and execution:
- Every report is keyed by backend-owned `task_id`, `session_id`,
`execution_id`, and `attachment_attempt_id`, enriched with agent/profile
identity and provider ACP session identity when available. Every
`session/new`, `session/load`, `session/reset`, or passthrough process start
creates a new attempt before delivery.
- Agentctl's instance identity is the authoritative execution generation for
local MCP observations. The server injects task and session ownership from
that instance; agent-supplied IDs never select the report.
- Each MCP transport client gets an opaque connection ID. Shared connections
remain shared; Kandev does not infer internal subagent identity.
- Configuration resolution, filtering, delivery, session acceptance,
initialize, `tools/list`, tool use, explicit errors, disconnect, and
supersession are separate evidence events. The UI displays the strongest
observed evidence and never treats missing evidence as a connection failure.
- Kandev's in-agentctl MCP server uses transport/session hooks to observe
initialize, `tools/list`, calls, and disconnects. Third-party direct MCP
servers remain delivery-unverified unless an explicit agent error or future
observable proxy/provider contract exists.
- Release mode persists a bounded structured timeline for the current and two
previous attachment attempts. It contains stable reason codes and sanitized
summaries, not raw protocol frames or persistent stderr.
- Sanitized network targets retain only scheme and host with optional port.
Stdio targets retain only the executable basename. Headers, environment
values, arguments, credentials, prompts, files, tool inputs/results, full
URLs, and raw agent output are excluded.
- Recent agent stderr remains an explicit, on-demand, bounded, ephemeral
diagnostic. It is warned as potentially sensitive and is never included in
the default copied report.
- A user-initiated endpoint test runs only against a server already present in
the session's effective configuration and from the same executor. Its result
is presented as reachability evidence, not proof that the agent attached.
- The toolbar trigger stays visually neutral. Per-server colors and diagnostic
actions appear in a desktop hover/focus popover that can be pinned, or an
inset mobile drawer opened by a 44px touch target.
- `acpdbg` gains an opt-in sentinel MCP probe so agent integrations can be
tested against the real ACP handshake. Advertised capabilities, delivery,
initialize, tool listing, and tool use remain separate findings.
The observable behavior is specified in
[`docs/specs/mcp-session-observability/spec.md`](../specs/mcp-session-observability/spec.md).
## Consequences
Users can distinguish “Kandev sent this configuration” from “the agent loaded
these tools” for each concurrent session, including after restarts. Release
diagnostics remain useful without enabling high-risk raw frame logging.
The report is deliberately asymmetric: Kandev can automatically prove more
about its own in-session endpoint than about third-party servers. Some rows
will remain unverified even when the agent is working correctly. The UI and
public documentation must explain that limitation.
Lifecycle, agentctl, orchestrator persistence, boot hydration, WebSocket state,
and the chat toolbar must share one versioned report contract. Each new
attachment attempt supersedes current display evidence before it can deliver
configuration, including resets that reuse an execution. Passthrough agents
must emit honest resolution/materialization evidence; a missing passthrough
strategy is Filtered or Unavailable, not Active.
Endpoint tests and stderr reads become new session-keyed diagnostic entry
points. They must use the existing authorization guards, accept only stored
server names, remain bounded, and avoid turning arbitrary user input into
network or process execution.
## Alternatives Considered
- **Keep deriving status from profile configuration.** Rejected because it
creates false positives and cannot distinguish concurrent executions.
- **Parse a connected-server list from ACP.** Rejected because ACP defines no
such portable response; provider-specific metadata can be supplemental but
cannot be the product contract.
- **Mark a server failed when no connection appears after a timeout.** Rejected
because third-party connections may be unobservable or lazy, and absence is
not failure evidence.
- **Proxy every MCP server through Kandev.** Deferred because it changes
transport ownership, credentials, performance, and failure domains far
beyond an observability feature.
- **Enable raw ACP and stderr logging in release mode.** Rejected because the
diagnostic value does not justify persistent capture of user and credential
data.
- **Show status colors directly on every toolbar icon.** Rejected because many
concurrent sessions would create visual noise; the neutral trigger with
details on disclosure preserves density.
- **Key status only by task or ACP session ID.** Rejected because tasks can run
several agents and ACP IDs can rotate or collide across providers. Kandev's
session, execution, and attachment-attempt identities form the stable
ownership boundary.