[GH-ISSUE #5273] [Bug] MCP tool calls backgrounded by the client after 120 s fail with "missing required resultType" (protocol 2026-07-28) #3217

Open
opened 2026-10-31 00:37:58 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @kamy-labs on GitHub (Oct 19, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5273

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

MCP or agent integration

Observed behavior

When an REA MCP tool call runs longer than the client's auto-background threshold (Claude Code: 120 s), the client moves it to the background. When the call completes, the client rejects REA's result:

MCP server "rea" returned a malformed result that failed schema validation:
Invalid result for tools/call: missing required resultType — servers implementing
protocol revision 2026-07-28 MUST include it (the absent-means-complete bridge
applies only to earlier-revision servers)
  • The payload is dropped, so the caller sees neither the result nor REA's structured error.
  • The completed Ghidra analysis is not reused: the next provider query on the same open session re-analyses, is backgrounded again, and fails the same way.

Request

{ "tool": "procedure_pseudo_code", "arguments": { "procedure": "file-offset:0x1A3C09C" } }

Provider ghidra, 64 MB arm64 ELF shared library, first provider query after open_binary.

Calls that finish within 120 s succeed (same server, same session): open_binary, list_procedures, search_procedures, procedure_pseudo_code, close_binary on a thin arm64 Mach-O and a 1.3 MB arm64 ELF.

Reproduction

  1. Register REA as a stdio MCP server in Claude Code 2.1.292:
    npx -y rea-agents@6.7.0 mcp
    
    Env: GHIDRA_INSTALL_DIR, JAVA_HOME, REA_ANALYSIS_PROVIDER=ghidra.
    Unset (defaults): REA_GHIDRA_STARTUP_TIMEOUT_MS (330000), CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS (120000).
  2. open_binary with { "path": "<64 MB arm64 ELF .so>", "provider_id": "ghidra" }. Returns immediately.
  3. procedure_pseudo_code with { "procedure": "file-offset:0x1A3C09C" }.
  4. At 120 s Claude Code reports: "still running after 120s. It was moved to the background as task …"
  5. When the call completes, the background task fails with the resultType error.

Frequency: reproduced 2/2 times (task IDs kugc09qzp, kf8t3yoi7), the second as a retry on the same open session.
Not tested: other MCP clients.

Expected behavior

  • Every tools/call result, success or error, carries a valid resultType (e.g. "complete") when protocol 2026-07-28 is negotiated, including results for calls the client has backgrounded.
  • For this target and the default startup deadline, the caller should get REA's structured provider_timeout error, as the CLI returns (see evidence), not a client-side schema rejection.

Unverified expectation: I haven't found REA documentation that defines behaviour for client-backgrounded calls.

Impact and scope

Observed

  • Any REA MCP call that exceeds the client's auto-background threshold loses its result.
  • In practice this is the first provider query on any large native target with Ghidra, so large binaries can't be analysed over MCP in Claude Code with default settings.
  • Retries can't succeed: each one re-imports and re-analyses from scratch.

Not affected (observed)

  • No effect seen on evidence integrity or process isolation.
  • The session stays open: binary_session → open: true, analysis_activity: "not_observed".

Untested

  • Other MCP clients
  • Other providers (Hopper / IDA / Kuna)
  • Other long-running non-Ghidra tools
  • Linux and Windows hosts

REA version

6.7.0

Runtime and platform

Node v24.16.0; macOS 15.7.1 (24G231) arm64; Ghidra 12.1.4 headless, OpenJDK 21, arm64 native decompiler built from source; bundled @modelcontextprotocol/server 2.3.1; client Claude Code 2.1.292 (stdio)

Target and artifact context

ELF arm64 shared library, 64 MB (Unity IL2CPP libil2cpp.so from an Android split APK); first provider query triggers full Ghidra auto-analysis

Interface

MCP

Earliest failure stage

Transport or result projection

Relevant output or evidence

Command or MCP request actually run

  • MCP: open_binary {"provider_id":"ghidra"}, then procedure_pseudo_code {"procedure":"file-offset:0x1A3C09C"} (twice)
  • CLI comparison, outside MCP:
    rea decompile <same .so> --file-offset 0x1A3C09C --provider ghidra --snapshot <path> --format json
    

Relevant result, error, or artifact

MCP (both attempts), reported by the client when the background task completed:

Invalid result for tools/call: missing required resultType — servers implementing
protocol revision 2026-07-28 MUST include it

binary_session after the failure:

{ "open": true, "analysis_activity": { "status": "not_observed" } }

CLI, same target, exit 1 after about 6 min (no snapshot written):

{
  "code": "provider_timeout",
  "message": "Ghidra did not finish importing and analyzing the target within its 330000 ms startup deadline (REA_GHIDRA_STARTUP_TIMEOUT_MS)."
}

Missing evidence or remaining uncertainty

  • NOT_ENOUGH_INFO on the exact payload REA sent on the MCP path: the client dropped it, so it's unclear whether it was a provider_timeout error or a completed result. Given the CLI result, it was probably the timeout error.
  • No server-side log captured.

Typed outcome or recovery expected

  • A valid 2026-07-28 tools/call result.
  • On timeout: the same structured provider_timeout error the CLI returns, naming REA_GHIDRA_STARTUP_TIMEOUT_MS, so the caller knows to raise it.
  • Ideally the finished or partial analysis stays in the session so a retry doesn't re-import.

Analysis or suspected cause

Hypothesis (not source-verified)

The result REA sends after the client has backgrounded the call doesn't go through the path that adds resultType under protocol 2026-07-28. The bundled SDK 2.3.1 supports that revision and sets resultType: "complete" in some paths (e.g. subscription teardown), so the gap is likely in REA's result envelope for long-running calls. I haven't identified the code path.

Context

  • #1877 progress notifications don't stop a client from backgrounding the call. As #1720 notes: "the server cannot override an external caller's cap."
  • MCP Tasks support was deferred (#97, #390, #391), so there's no long-running result path that clients can consume.

Additional context

Related

  • #1720 / #1877: client deadline vs. Ghidra startup; progress notifications
  • #3509: long Ghidra analysis vs. client idle timeout
  • #3751: reuse an analysed Ghidra project (--snapshot doesn't avoid re-analysis)
  • PR #4439: stream and cache large Ghidra analysis
  • #97 / #390 / #391: MCP Tasks (deferred)
  • #974 / #4590: configurable and named startup deadline
  • Same client-side error with a different MCP server on backgrounded calls: claudeissues.com #88154

Proposed workaround (untested here)
Raise both:

  • CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS (or set 0)
  • REA_GHIDRA_STARTUP_TIMEOUT_MS

Neither alone looks sufficient for this target.

Regression window: NOT_ENOUGH_INFO (only 6.7.0 tested).

Originally created by @kamy-labs on GitHub (Oct 19, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5273 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area MCP or agent integration ### Observed behavior When an REA MCP tool call runs longer than the client's auto-background threshold (**Claude Code: 120 s**), the client moves it to the background. When the call completes, the client rejects REA's result: ``` MCP server "rea" returned a malformed result that failed schema validation: Invalid result for tools/call: missing required resultType — servers implementing protocol revision 2026-07-28 MUST include it (the absent-means-complete bridge applies only to earlier-revision servers) ``` - The payload is dropped, so the caller sees neither the result nor REA's structured error. - The completed Ghidra analysis is not reused: the next provider query on the same open session re-analyses, is backgrounded again, and fails the same way. **Request** ```json { "tool": "procedure_pseudo_code", "arguments": { "procedure": "file-offset:0x1A3C09C" } } ``` Provider `ghidra`, 64 MB arm64 ELF shared library, first provider query after `open_binary`. **Calls that finish within 120 s succeed** (same server, same session): `open_binary`, `list_procedures`, `search_procedures`, `procedure_pseudo_code`, `close_binary` on a thin arm64 Mach-O and a 1.3 MB arm64 ELF. ### Reproduction 1. Register REA as a stdio MCP server in Claude Code 2.1.292: ``` npx -y rea-agents@6.7.0 mcp ``` Env: `GHIDRA_INSTALL_DIR`, `JAVA_HOME`, `REA_ANALYSIS_PROVIDER=ghidra`. Unset (defaults): `REA_GHIDRA_STARTUP_TIMEOUT_MS` (330000), `CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS` (120000). 2. `open_binary` with `{ "path": "<64 MB arm64 ELF .so>", "provider_id": "ghidra" }`. Returns immediately. 3. `procedure_pseudo_code` with `{ "procedure": "file-offset:0x1A3C09C" }`. 4. At 120 s Claude Code reports: *"still running after 120s. It was moved to the background as task …"* 5. When the call completes, the background task fails with the `resultType` error. **Frequency:** reproduced 2/2 times (task IDs `kugc09qzp`, `kf8t3yoi7`), the second as a retry on the same open session. **Not tested:** other MCP clients. ### Expected behavior - Every `tools/call` result, **success or error**, carries a valid `resultType` (e.g. `"complete"`) when protocol **2026-07-28** is negotiated, including results for calls the client has backgrounded. - For this target and the default startup deadline, the caller should get REA's structured `provider_timeout` error, as the CLI returns (see evidence), not a client-side schema rejection. > Unverified expectation: I haven't found REA documentation that defines behaviour for client-backgrounded calls. ### Impact and scope **Observed** - Any REA MCP call that exceeds the client's auto-background threshold loses its result. - In practice this is the first provider query on any large native target with Ghidra, so **large binaries can't be analysed over MCP in Claude Code with default settings**. - Retries can't succeed: each one re-imports and re-analyses from scratch. **Not affected (observed)** - No effect seen on evidence integrity or process isolation. - The session stays open: `binary_session` → `open: true`, `analysis_activity: "not_observed"`. **Untested** - Other MCP clients - Other providers (Hopper / IDA / Kuna) - Other long-running non-Ghidra tools - Linux and Windows hosts ### REA version 6.7.0 ### Runtime and platform Node v24.16.0; macOS 15.7.1 (24G231) arm64; Ghidra 12.1.4 headless, OpenJDK 21, arm64 native decompiler built from source; bundled @modelcontextprotocol/server 2.3.1; client Claude Code 2.1.292 (stdio) ### Target and artifact context ELF arm64 shared library, 64 MB (Unity IL2CPP libil2cpp.so from an Android split APK); first provider query triggers full Ghidra auto-analysis ### Interface MCP ### Earliest failure stage Transport or result projection ### Relevant output or evidence **Command or MCP request actually run** - MCP: `open_binary {"provider_id":"ghidra"}`, then `procedure_pseudo_code {"procedure":"file-offset:0x1A3C09C"}` (twice) - CLI comparison, outside MCP: ``` rea decompile <same .so> --file-offset 0x1A3C09C --provider ghidra --snapshot <path> --format json ``` **Relevant result, error, or artifact** MCP (both attempts), reported by the client when the background task completed: ``` Invalid result for tools/call: missing required resultType — servers implementing protocol revision 2026-07-28 MUST include it ``` `binary_session` after the failure: ```json { "open": true, "analysis_activity": { "status": "not_observed" } } ``` CLI, same target, exit 1 after about 6 min (no snapshot written): ```json { "code": "provider_timeout", "message": "Ghidra did not finish importing and analyzing the target within its 330000 ms startup deadline (REA_GHIDRA_STARTUP_TIMEOUT_MS)." } ``` **Missing evidence or remaining uncertainty** - `NOT_ENOUGH_INFO` on the exact payload REA sent on the MCP path: the client dropped it, so it's unclear whether it was a `provider_timeout` error or a completed result. Given the CLI result, it was probably the timeout error. - No server-side log captured. ### Typed outcome or recovery expected - A valid 2026-07-28 `tools/call` result. - On timeout: the same structured `provider_timeout` error the CLI returns, naming `REA_GHIDRA_STARTUP_TIMEOUT_MS`, so the caller knows to raise it. - Ideally the finished or partial analysis stays in the session so a retry doesn't re-import. ### Analysis or suspected cause **Hypothesis (not source-verified)** The result REA sends after the client has backgrounded the call doesn't go through the path that adds `resultType` under protocol 2026-07-28. The bundled SDK 2.3.1 supports that revision and sets `resultType: "complete"` in some paths (e.g. subscription teardown), so the gap is likely in REA's result envelope for long-running calls. I haven't identified the code path. **Context** - #1877 progress notifications don't stop a client from backgrounding the call. As #1720 notes: *"the server cannot override an external caller's cap."* - MCP Tasks support was deferred (#97, #390, #391), so there's no long-running result path that clients can consume. ### Additional context **Related** - #1720 / #1877: client deadline vs. Ghidra startup; progress notifications - #3509: long Ghidra analysis vs. client idle timeout - #3751: reuse an analysed Ghidra project (`--snapshot` doesn't avoid re-analysis) - PR #4439: stream and cache large Ghidra analysis - #97 / #390 / #391: MCP Tasks (deferred) - #974 / #4590: configurable and named startup deadline - Same client-side error with a different MCP server on backgrounded calls: claudeissues.com #88154 **Proposed workaround (untested here)** Raise both: - `CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS` (or set `0`) - `REA_GHIDRA_STARTUP_TIMEOUT_MS` Neither alone looks sufficient for this target. **Regression window:** `NOT_ENOUGH_INFO` (only 6.7.0 tested).
Author
Owner

@akushonkamen commented on GitHub (Oct 19, 2026):

Claiming this — I'll work on the protocol-level fix: adding the required resultType to tools/call results (success and error paths) with revision negotiation, plus InMemoryTransport protocol tests.

<!-- gh-comment-id:6274370257 --> @akushonkamen commented on GitHub (Oct 19, 2026): Claiming this — I'll work on the protocol-level fix: adding the required `resultType` to `tools/call` results (success and error paths) with revision negotiation, plus InMemoryTransport protocol tests.
Author
Owner

@akushonkamen commented on GitHub (Oct 19, 2026):

Progress update — fix pushed to akushonkamen/rea branch fix/5273-mcp-resulttype (commit efd645e30), PR to follow once the current GitHub PR-creation restriction on this account clears.

Root cause (verified against @modelcontextprotocol/server 2.3.1, which REA pins):

  • Revision 2026-07-28 made resultType a REQUIRED member of every result; on that revision a client discards a tools/call result without it (the SDK's own decoder rejects with exactly missing required resultType), so both the payload and REA's structured error are lost.
  • The SDK only installs the server/discover handler when the supported protocol list names a modern revision; REA's EvidenceMcpServer passed no supportedProtocolVersions, so modern-era negotiation was unservable.
  • Even on a modern-era connection, REA's oversized-error recovery (ErrorEvidenceTransport.recoverError) replaces the SDK-encoded result with a freshly built one, dropping the resultType: "complete" stamp — a 2026-07-28 client then discards REA's structured error on precisely the long-analysis failures from this report.

Fix (commit efd645e30): advertise 2026-07-28 in supportedProtocolVersions so the SDK stamps every outbound result (success and isError alike), and preserve the stamp across the oversized-error replacement. Covered by new InMemoryTransport protocol tests (src/server/resultTypeProtocol.test.ts) asserting resultType on success, input-validation error, and oversized-error-recovery results — red before the fix, green after; adapters and mcp-boundary regressions show no new failures (the handful of local failures reproduce identically on a pristine checkout for lack of optional deps openpgp/tsx in this environment).

<!-- gh-comment-id:6274989386 --> @akushonkamen commented on GitHub (Oct 19, 2026): Progress update — fix pushed to `akushonkamen/rea` branch [fix/5273-mcp-resulttype](https://github.com/akushonkamen/rea/commits/fix/5273-mcp-resulttype) (commit efd645e30), PR to follow once the current GitHub PR-creation restriction on this account clears. Root cause (verified against `@modelcontextprotocol/server` 2.3.1, which REA pins): - Revision 2026-07-28 made `resultType` a REQUIRED member of every result; on that revision a client discards a `tools/call` result without it (the SDK's own decoder rejects with exactly `missing required resultType`), so both the payload and REA's structured error are lost. - The SDK only installs the `server/discover` handler when the supported protocol list names a modern revision; REA's `EvidenceMcpServer` passed no `supportedProtocolVersions`, so modern-era negotiation was unservable. - Even on a modern-era connection, REA's oversized-error recovery (`ErrorEvidenceTransport.recoverError`) replaces the SDK-encoded result with a freshly built one, dropping the `resultType: "complete"` stamp — a 2026-07-28 client then discards REA's structured error on precisely the long-analysis failures from this report. Fix (commit efd645e30): advertise `2026-07-28` in `supportedProtocolVersions` so the SDK stamps every outbound result (success and `isError` alike), and preserve the stamp across the oversized-error replacement. Covered by new `InMemoryTransport` protocol tests (`src/server/resultTypeProtocol.test.ts`) asserting `resultType` on success, input-validation error, and oversized-error-recovery results — red before the fix, green after; adapters and mcp-boundary regressions show no new failures (the handful of local failures reproduce identically on a pristine checkout for lack of optional deps `openpgp`/`tsx` in this environment).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3217
No description provided.