Cowork desktop: MCP elicitation (2026-07-28 MRTR) dropped via print-mode branch, tool call hangs 180s

Status Open
Reported on v2.1.273
Maintainer reply None cached
Activity 2 comments · opened Sep 16, 2026

Elicitation (MCP 2026-07-28 MRTR) is dropped in print mode — capability is declared, InputRequiredResult is never serviced, tool call hangs for 180s

Component: MCP client used by the Claude Cowork desktop app
Version: claude-code/2.1.273 (remote_cowork)
Severity: High — makes any MCP server that gates writes behind elicitation unusable in Cowork

The core of it: the client takes a print-mode (non-interactive) branch on receiving an elicitation, even though the surface is the Claude Cowork desktop app with a live user sitting in front of it. A human is present and the app already renders tool-approval prompts — there is somewhere obvious to show the form. The session merely executes remotely; that is not a reason to treat it as headless.

---

Summary

The Claude Cowork desktop app declares "elicitation": {} in io.modelcontextprotocol/clientCapabilities and negotiates protocol revision 2026-07-28. When a server responds to tools/call with a spec-compliant InputRequiredResult carrying an elicitation/create (form mode) request, the client receives and parses it, logs

Elicitation request received in print mode: {"method":"elicitation/create", …}

…and then does nothing. No UI is rendered, no retry is issued, no decline/cancel is returned, and no error is surfaced. The originating tools/call stays pending until the transport timeout fires at 180 seconds, at which point the user sees a generic timeout that gives no hint that a confirmation was requested.

The server's InputRequiredResult arrives ~2 seconds after dispatch. The remaining ~178 seconds are entirely client-side stall.

Reproduced 3/3 in a single session.

---

Expected behaviour

Per the MRTR pattern spec, Client Requirements 1 and 2:

1. If a client receives an InputRequiredResult that contains the inputRequests field, the client MUST construct the requested inputs before retrying the original request. 2. If an InputRequiredResult contains the requestState field, the client MUST echo back the exact value of that field when retrying the original request.

So one of:

  1. Preferred — bridge the elicitation to the Cowork desktop UI. This is not a headless context: a user is sitting in the Cowork desktop app, and the app already renders tool-approval prompts, so there is an obvious place to show the form. Then retry the call with inputResponses + the echoed requestState under a new JSON-RPC id.
  2. Acceptable — if the surface genuinely cannot service elicitation, respond immediately with {"action": "cancel"} on the retry leg so the server can fall back, and fail the call fast with a diagnostic naming elicitation as the cause.
  3. Minimum — stop declaring the elicitation capability in modes that cannot honour it. Servers are required by MRTR server-requirement 7 to only send elicitation requests the client has declared support for; declaring support that is then silently dropped makes that guarantee meaningless and pushes the failure onto conformant servers.

Actual behaviour

Capability is declared → server correctly sends form-mode elicitation → client logs it under a print-mode branch → request is dropped → 180s hang → generic timeout.

---

Environment

| | |
|---|---|
| Surface | Claude Cowork desktop app (Windows), user interactively present |
| Client id (internal) | claude-code/2.1.273 (remote_cowork) — the Cowork desktop app's MCP client self-identifies as claude-code; the product surface is Cowork |
| Runtime | Node v26.3.0, Linux — session executes in an Anthropic cloud container |
| Transport | HTTP → https://api.anthropic.com/v2/ccr-sessions/<REDACTED>/mcp?mcp_server_id=<REDACTED>&mcp_url=<REDACTED>&toolbox_mcp_server_id=<REDACTED> |
| Transport timeout | timeoutMs: 180000 |
| Negotiated protocol | 2026-07-28 |
| Server | Remote MCP server built on the MCP C# SDK (InputRequiredException / MRTR) |
| Tool | add_agenda_item — a write gated behind a boolean confirmation |

---

Reproduction

  1. Connect an MCP server that negotiates 2026-07-28 and returns an InputRequiredResult with a form-mode elicitation/create from a tools/call handler.
  2. Call that tool from the Claude Cowork desktop app.
  3. Observe: no form, no retry, 180s hang, timeout error.

Minimal server behaviour needed to reproduce — return this from tools/call instead of a CallToolResult:

{
  "jsonrpc": "2.0",
  "id": 11,
  "result": {
    "resultType": "input_required",
    "inputRequests": {
      "confirmation": {
        "method": "elicitation/create",
        "params": {
          "mode": "form",
          "message": "Confirm this write?",
          "requestedSchema": {
            "type": "object",
            "properties": {
              "confirm": { "type": "boolean", "title": "Confirm", "default": false }
            },
            "required": ["confirm"]
          }
        }
      }
    },
    "requestState": "<opaque signed blob>"
  }
}

---

Evidence

1. Client request — capability is declared

{
  "method": "tools/call",
  "params": {
    "name": "add_agenda_item",
    "arguments": {
      "title": "Impact of MCP servers",
      "description": "<p>Discuss the impact of MCP servers …</p>",
      "eventId": "<REDACTED>"
    },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {
        "name": "claude-code",
        "title": "Claude Code",
        "version": "2.1.273"
      },
      "io.modelcontextprotocol/clientCapabilities": {
        "roots": { "listChanged": true },
        "elicitation": {}
      },
      "claudecode/toolUseId": "<REDACTED>",
      "progressToken": 11
    }
  },
  "jsonrpc": "2.0",
  "id": 11
}

Note "elicitation": {} — per the elicitation spec, an empty object is equivalent to declaring form mode support.

2. Server response — spec-compliant InputRequiredResult

{
  "jsonrpc": "2.0",
  "id": 11,
  "result": {
    "resultType": "input_required",
    "inputRequests": {
      "confirmation": {
        "method": "elicitation/create",
        "params": {
          "mode": "form",
          "message": "Add this item to the meeting's agenda?\nMeeting: <REDACTED>\nWhen: Sat 19 Sep 2026, 05:30 (UTC)\nItem: Impact of MCP servers\nDuration: 10 min\nAgenda: Existing draft agenda, not yet visible to attendees.",
          "requestedSchema": {
            "type": "object",
            "properties": {
              "confirm": { "type": "boolean", "title": "Add agenda item", "default": false }
            },
            "required": ["confirm"]
          }
        }
      }
    },
    "requestState": "<REDACTED — signed JWT-style blob>"
  }
}

Conformance check against MRTR server requirements: resultType set ✅ · inputRequests keyed uniquely, value is a valid ElicitRequest ✅ · requestState present and integrity-protected (req. 4) ✅ · at least one of inputRequests/requestState (req. 6) ✅ · sent on tools/call, a supported request ✅ · mode matches declared client capability (req. 7) ✅.

InputRequiredResult has no content field in the schema, so its absence is correct and is not the cause.

3. Client log — the drop

From ~/.cache/claude-cli-nodejs/<cwd-slug>/mcp-logs-<server>/<timestamp>.jsonl:

15:29:11.854  Calling MCP tool: add_agenda_item
15:29:13.862  Elicitation request received in print mode: {"method":"elicitation/create","params":{"mode":"form", …}}
15:29:41.877  Tool 'add_agenda_item' still running (30s elapsed)
15:30:11.877  Tool 'add_agenda_item' still running (60s elapsed)
15:30:41.879  Tool 'add_agenda_item' still running (90s elapsed)
15:31:11.941  Tool 'add_agenda_item' still running (120s elapsed)
15:31:41.939  Tool 'add_agenda_item' still running (150s elapsed)
15:32:11.855  Tool 'add_agenda_item' failed after 180s: MCP server "…" tool "add_agenda_item" timed out after 180s

The Elicitation request received in print mode line is the whole bug: the client got it, understood it, and had no branch to act on it.

4. All three occurrences

| # | Dispatched | Elicitation received | Outcome |
|---|---|---|---|
| 1 | 15:29:11.854 | 15:29:13.862 (+2.0s) | timed out 15:32:11.855 (180s) |
| 2 | 15:32:43.228 | 15:32:45.190 (+2.0s) | timed out 15:35:43.231 (180s) |
| 3 | 15:42:14.041 | 15:42:15.966 (+1.9s) | user-interrupted, still pending at 15:43:44 |

All times UTC, 2026-09-16.

5. Server-side trace context

W3C traceparent captured on the server for one of the hanging calls above:

00-b2acd673fcb084d36e29cc0d266aac7d-c20cbf725df2e5a5-01

| Field | Value |
|---|---|
| version | 00 |
| trace-id | b2acd673fcb084d36e29cc0d266aac7d |
| parent-id (span) | c20cbf725df2e5a5 |
| trace-flags | 01 (sampled) |

The server span shows the handler completing promptly — it emits the InputRequiredResult and returns. There is no corresponding retry request in the trace, confirming the client never issued the second leg. Traces for the remaining two calls can be supplied on request.

Note that this trace context does not appear anywhere in the Cowork MCP logs: the client neither logs nor propagates a server traceparent, so client↔server correlation currently has to be done by wall-clock timestamp. Surfacing the server's trace context in the client log alongside Calling MCP tool: … would make this class of issue substantially cheaper to diagnose, and is worth considering independently of the fix.

---

Impact

  • Any MCP server using 2026-07-28 elicitation to confirm a write is unusable in the Cowork desktop app. Every confirming write hangs three minutes and then fails.
  • The print-mode branch is taken on an interactive surface with a user present. This is not a headless run where dropping the request would be defensible — the person is in the Cowork desktop app, watching the tool call hang, and would have answered a one-checkbox form instantly.
  • The failure is silent and misattributed. The surfaced error is a transport timeout naming the server, so users reasonably conclude the server is broken. Diagnosing this took an extended session and only resolved on reading the client's own MCP log; the log line is the sole indication that a confirmation was ever requested.
  • It penalises conformant servers. A server that correctly honours MRTR server-requirement 7 — send elicitation only where the client declared support — is led into the hang by the client's own declaration. A non-conformant server that ignores capabilities and writes unconditionally works fine here.
  • Server authors have no signal to route around it. The C# SDK's IsMrtrSupported returns true, because 2026-07-28 genuinely is negotiated. Nothing in capability negotiation distinguishes this surface; only sniffing clientInfo.name would, which is not something server authors should be encouraged to build on.

Suggested fix

Priority order: (1) bridge elicitation to the Cowork approval UI; (2) failing that, return {"action":"cancel"} and surface a diagnostic that names elicitation; (3) at minimum, do not advertise the elicitation capability from a mode that drops it, and shorten the failure path so it does not consume the full 180s transport timeout.

References

---

Redaction note

Tenant GUID, Microsoft Graph event ID, team/channel IDs, session UUID, toolUseId, the requestState blob, and user name/email have been removed.

The traceparent in §5 is retained deliberately: trace and span IDs are random per-request identifiers that carry no tenant, user, or resource information, and they are what makes the server-side timeline verifiable. They are only resolvable against the Decisions backend, so they are included for correlation on request rather than as something Anthropic can look up directly.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗