[GH-ISSUE #5447] [Bug] Session discovery falsely requires open_binary for independent Cutter and debugger tools #3307

Closed
opened 2026-10-31 00:39:00 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @coygeek on GitHub (Oct 20, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5447

Before submitting

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

Area

Provider or capability discovery

Observed behavior

With no binary open, binary_session {} reports list_cutter_sessions unavailable with reason: "target_required" and remediation Call open_binary with a supported local target. In the same production stdio MCP connection, list_cutter_sessions {} succeeds without opening a binary; a subsequent binary_session {} still reports open: false.

The independent verifier also observed the same false target prerequisite for cutter_command, start_gdb_session, gdb_console, gdb_session_status, close_gdb_session, start_rizin_debug_session, rizin_debug_command, rizin_debug_session_status, and close_rizin_debug_session. Direct debugger starts reached their Linux-only host restriction; the seven session-dependent calls reached unknown-session validation. These error probes demonstrate handler reachability, not successful live-session use.

Reproduction

From the built source checkout with its dependencies present, run the following. This exact public reproduction was independently executed and exited 0. It starts REA with isolated configuration and an explicitly absent Cutter bridge directory, then checks discovery and dispatch in one connection without calling open_binary.

node --input-type=module <<'JS'
import { Client } from '@modelcontextprotocol/client';
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
import { mkdtemp, mkdir } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
const probe = await mkdtemp(join(tmpdir(), 'rea-capability-'));
const env = { PATH: process.env.PATH, REA_LOG_LEVEL: 'silent' };
for (const name of ['HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR']) {
  env[name] = join(probe, name.toLowerCase());
  await mkdir(env[name], { recursive: true });
}
env.REA_CUTTER_BRIDGE_DIR = join(probe, 'absent-bridge');
const client = new Client({ name: 'capability-check', version: '1' });
const transport = new StdioClientTransport({ command: process.execPath, args: [resolve('scripts/rea.mjs'), '--mcp'], env, stderr: 'pipe' });
try {
  await client.connect(transport);
  const status = await client.callTool({ name: 'binary_session', arguments: {} });
  console.log('open:', status.structuredContent.result.open);
  console.log('availability:', JSON.stringify(status.structuredContent.result.tool_availability.groups.find(g => g.names.includes('list_cutter_sessions'))));
  const listed = await client.callTool({ name: 'list_cutter_sessions', arguments: {} });
  const { sessions, discovery_status, bridge_directory_security } = listed.structuredContent;
  console.log('discovery:', JSON.stringify({ isError: listed.isError ?? false, sessions, discovery_status, bridge_directory_security }));
  const after = await client.callTool({ name: 'binary_session', arguments: {} });
  console.log('open afterwards:', after.structuredContent.result.open);
} finally {
  await client.close();
  await transport.close();
}
JS

Expected behavior

docs/mcp-contracts.md:425-435 directs callers to use binary_session.result.tool_availability to choose callable operations for the current target, provider, host, and negotiated capabilities. src/contracts/cutterToolContracts.ts:65-74 accepts an empty discovery request, and src/server/registerCutterTools.ts:25-41 dispatches it without an active analysis target.

Cutter discovery should therefore be available in this state. Independent Cutter/GDB/Rizin session operations should advertise their actual host, input, and session constraints without inventing an open_binary prerequisite. This expectation for the remaining session operations is source-derived from their contracts and handlers; successful live-session execution was not tested. Rizin start still requires its own explicit target path and inferior TTY.

Impact and scope

P2: the documented operation-selection response supplies false readiness and misleading remediation for ten independently managed session operations. This can cause discovery-guided clients to skip a callable operation; downstream client selection behavior was not measured. A verified workaround for Cutter discovery is to invoke list_cutter_sessions directly. Debugger/session calls also reach their real validation directly, but successful live debugger/Cutter operations remain untested. No security boundary crossing or data loss was observed.

REA version

6.7.0; source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8.

Runtime and platform

Node.js v26.12.0; macOS (darwin), arm64. NOT_ENOUGH_INFO: the verifier did not record the exact macOS release. No live Cutter or debugger was required.

Target and artifact context

No active binary-analysis target; isolated HOME/XDG/cache/state directories; explicitly nonexistent Cutter bridge directory; no live bridge or debugger session.

Interface

MCP

Earliest failure stage

Transport or result projection

Relevant output or evidence

Independent production stdio MCP observations, with unrelated readiness metadata omitted:

open: false
availability: {"names":["list_cutter_sessions"],"available":false,"reason":"target_required","remediation":"Call open_binary with a supported local target."}
discovery: {"isError":false,"sessions":[],"discovery_status":"no_live_bridge_found","bridge_directory_security":"not_checked"}
open afterwards: false

A separate stdio probe checked all ten inventory entries and invoked all ten handlers. Both debugger starts returned capability_unavailable with a Linux-only host limitation; the seven existing-session operations returned invalid_request for an unknown UUID. address_name was a discriminating target-dependent control and also advertised target_required.

Evidence provenance: observed. Local verification: independently reproduced through node scripts/rea.mjs --mcp. Reproduction completeness: complete for the discovery/dispatch mismatch; no successful live-session claim is made.

Typed outcome or recovery expected

With no active binary, list_cutter_sessions should remain successful and its availability should agree. The independent session operations should no longer report target_required or recommend open_binary solely because the analysis session is closed. Preserve genuine host/session/input restrictions. address_name should continue to report target_required without an active target. These are proposed restoration checks, not a tested patch.

Analysis or suspected cause

Source-derived reasoning: src/application/CapabilityInventory.ts:193-198,675-687 includes native-provider in the active-target gate, which covers these ten contracts despite their independent dispatch/session ownership. The general host check runs before the target check (:371-375), but it did not project the debugger managers' Linux-only restriction in the observed state. This report does not claim that the target gate precedes every host check.

Additional context

Prepared with AI assistance and independently reproduced by a separate verifier. Upstream main was fetched before submission; the only later change to the cited files adds unrelated native-function-matching documentation and does not change this gate. Searches covered open/closed issues and PRs using list_cutter_sessions, target_required, and CapabilityInventory, plus session/readiness combinations. #1166 and PR #1176 concern the opposite mismatch for target-dependent inspection tools; #4687 concerns missing composition for trace_call_path; #4227 tracks a broader lifecycle audit. None of those inspected reports describes this target-free-session prerequisite mismatch.

NOT_ENOUGH_INFO: last known good version, first affected version, Linux live-session behavior, and downstream client occurrence frequency were not established.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5447 ### 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 Provider or capability discovery ### Observed behavior With no binary open, `binary_session {}` reports `list_cutter_sessions` unavailable with `reason: "target_required"` and remediation `Call open_binary with a supported local target.` In the same production stdio MCP connection, `list_cutter_sessions {}` succeeds without opening a binary; a subsequent `binary_session {}` still reports `open: false`. The independent verifier also observed the same false target prerequisite for `cutter_command`, `start_gdb_session`, `gdb_console`, `gdb_session_status`, `close_gdb_session`, `start_rizin_debug_session`, `rizin_debug_command`, `rizin_debug_session_status`, and `close_rizin_debug_session`. Direct debugger starts reached their Linux-only host restriction; the seven session-dependent calls reached unknown-session validation. These error probes demonstrate handler reachability, not successful live-session use. ### Reproduction From the built source checkout with its dependencies present, run the following. This exact public reproduction was independently executed and exited 0. It starts REA with isolated configuration and an explicitly absent Cutter bridge directory, then checks discovery and dispatch in one connection without calling `open_binary`. ```sh node --input-type=module <<'JS' import { Client } from '@modelcontextprotocol/client'; import { StdioClientTransport } from '@modelcontextprotocol/client/stdio'; import { mkdtemp, mkdir } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; const probe = await mkdtemp(join(tmpdir(), 'rea-capability-')); const env = { PATH: process.env.PATH, REA_LOG_LEVEL: 'silent' }; for (const name of ['HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR']) { env[name] = join(probe, name.toLowerCase()); await mkdir(env[name], { recursive: true }); } env.REA_CUTTER_BRIDGE_DIR = join(probe, 'absent-bridge'); const client = new Client({ name: 'capability-check', version: '1' }); const transport = new StdioClientTransport({ command: process.execPath, args: [resolve('scripts/rea.mjs'), '--mcp'], env, stderr: 'pipe' }); try { await client.connect(transport); const status = await client.callTool({ name: 'binary_session', arguments: {} }); console.log('open:', status.structuredContent.result.open); console.log('availability:', JSON.stringify(status.structuredContent.result.tool_availability.groups.find(g => g.names.includes('list_cutter_sessions')))); const listed = await client.callTool({ name: 'list_cutter_sessions', arguments: {} }); const { sessions, discovery_status, bridge_directory_security } = listed.structuredContent; console.log('discovery:', JSON.stringify({ isError: listed.isError ?? false, sessions, discovery_status, bridge_directory_security })); const after = await client.callTool({ name: 'binary_session', arguments: {} }); console.log('open afterwards:', after.structuredContent.result.open); } finally { await client.close(); await transport.close(); } JS ``` ### Expected behavior `docs/mcp-contracts.md:425-435` directs callers to use `binary_session.result.tool_availability` to choose callable operations for the current target, provider, host, and negotiated capabilities. `src/contracts/cutterToolContracts.ts:65-74` accepts an empty discovery request, and `src/server/registerCutterTools.ts:25-41` dispatches it without an active analysis target. Cutter discovery should therefore be available in this state. Independent Cutter/GDB/Rizin session operations should advertise their actual host, input, and session constraints without inventing an `open_binary` prerequisite. This expectation for the remaining session operations is source-derived from their contracts and handlers; successful live-session execution was not tested. Rizin start still requires its own explicit target path and inferior TTY. ### Impact and scope P2: the documented operation-selection response supplies false readiness and misleading remediation for ten independently managed session operations. This can cause discovery-guided clients to skip a callable operation; downstream client selection behavior was not measured. A verified workaround for Cutter discovery is to invoke `list_cutter_sessions` directly. Debugger/session calls also reach their real validation directly, but successful live debugger/Cutter operations remain untested. No security boundary crossing or data loss was observed. ### REA version 6.7.0; source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8`. ### Runtime and platform Node.js v26.12.0; macOS (`darwin`), arm64. NOT_ENOUGH_INFO: the verifier did not record the exact macOS release. No live Cutter or debugger was required. ### Target and artifact context No active binary-analysis target; isolated HOME/XDG/cache/state directories; explicitly nonexistent Cutter bridge directory; no live bridge or debugger session. ### Interface MCP ### Earliest failure stage Transport or result projection ### Relevant output or evidence Independent production stdio MCP observations, with unrelated readiness metadata omitted: ```text open: false availability: {"names":["list_cutter_sessions"],"available":false,"reason":"target_required","remediation":"Call open_binary with a supported local target."} discovery: {"isError":false,"sessions":[],"discovery_status":"no_live_bridge_found","bridge_directory_security":"not_checked"} open afterwards: false ``` A separate stdio probe checked all ten inventory entries and invoked all ten handlers. Both debugger starts returned `capability_unavailable` with a Linux-only host limitation; the seven existing-session operations returned `invalid_request` for an unknown UUID. `address_name` was a discriminating target-dependent control and also advertised `target_required`. Evidence provenance: observed. Local verification: independently reproduced through `node scripts/rea.mjs --mcp`. Reproduction completeness: complete for the discovery/dispatch mismatch; no successful live-session claim is made. ### Typed outcome or recovery expected With no active binary, `list_cutter_sessions` should remain successful and its availability should agree. The independent session operations should no longer report `target_required` or recommend `open_binary` solely because the analysis session is closed. Preserve genuine host/session/input restrictions. `address_name` should continue to report `target_required` without an active target. These are proposed restoration checks, not a tested patch. ### Analysis or suspected cause Source-derived reasoning: `src/application/CapabilityInventory.ts:193-198,675-687` includes `native-provider` in the active-target gate, which covers these ten contracts despite their independent dispatch/session ownership. The general host check runs before the target check (`:371-375`), but it did not project the debugger managers' Linux-only restriction in the observed state. This report does not claim that the target gate precedes every host check. ### Additional context Prepared with AI assistance and independently reproduced by a separate verifier. Upstream main was fetched before submission; the only later change to the cited files adds unrelated native-function-matching documentation and does not change this gate. Searches covered open/closed issues and PRs using `list_cutter_sessions`, `target_required`, and `CapabilityInventory`, plus session/readiness combinations. #1166 and PR #1176 concern the opposite mismatch for target-dependent inspection tools; #4687 concerns missing composition for `trace_call_path`; #4227 tracks a broader lifecycle audit. None of those inspected reports describes this target-free-session prerequisite mismatch. NOT_ENOUGH_INFO: last known good version, first affected version, Linux live-session behavior, and downstream client occurrence frequency were not established.
Author
Owner

@coygeek commented on GitHub (Oct 20, 2026):

Additional trigger for the same shared native-provider target gate: the documented targetless objdump architectures operation. Verified with REA 6.7.0 at 9d8e7803326a382cbbb3ffdb900f5e93e5442d68 (Node.js v26.12.0, macOS arm64), using isolated HOME/XDG/TMPDIR and REA_OBJDUMP_COMMAND pointing to an executable fixture that answers -i with BFD header file version (GNU Binutils) 2.42\nelf64-x86-64\nelf32-i386\n. After initializing node scripts/rea.mjs mcp, send {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"binary_session","arguments":{}}}, then {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"inspect_with_objdump","arguments":{"operation":"architectures"}}} in the same connection. The first reports open:false and inspect_with_objdump in a group with available:false, reason:"target_required", remediation:"Call open_binary with a supported local target."; the second succeeds without open_binary, returning subject:null, command_arguments:["-i"], exit_code:0, completion_status:"complete", and the fixture listing. node scripts/rea.mjs list-objdump-architectures --json also succeeds without a target. Expected: availability should expose the architectures operation as callable while preserving genuine artifact-input/provider requirements, consistent with docs/reverse-engineering-tools.md and src/contracts/reverseEngineeringToolContracts.ts. This tests discovery/dispatch consistency, not a real GNU objdump installation. Current main changes cited files; verified at the baseline above, main not re-run.

<!-- gh-comment-id:6303744490 --> @coygeek commented on GitHub (Oct 20, 2026): Additional trigger for the same shared `native-provider` target gate: the documented targetless objdump architectures operation. Verified with REA 6.7.0 at `9d8e7803326a382cbbb3ffdb900f5e93e5442d68` (Node.js v26.12.0, macOS arm64), using isolated HOME/XDG/TMPDIR and `REA_OBJDUMP_COMMAND` pointing to an executable fixture that answers `-i` with `BFD header file version (GNU Binutils) 2.42\nelf64-x86-64\nelf32-i386\n`. After initializing `node scripts/rea.mjs mcp`, send `{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"binary_session","arguments":{}}}`, then `{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"inspect_with_objdump","arguments":{"operation":"architectures"}}}` in the same connection. The first reports `open:false` and `inspect_with_objdump` in a group with `available:false`, `reason:"target_required"`, `remediation:"Call open_binary with a supported local target."`; the second succeeds without `open_binary`, returning `subject:null`, `command_arguments:["-i"]`, `exit_code:0`, `completion_status:"complete"`, and the fixture listing. `node scripts/rea.mjs list-objdump-architectures --json` also succeeds without a target. Expected: availability should expose the architectures operation as callable while preserving genuine artifact-input/provider requirements, consistent with `docs/reverse-engineering-tools.md` and `src/contracts/reverseEngineeringToolContracts.ts`. This tests discovery/dispatch consistency, not a real GNU objdump installation. Current main changes cited files; verified at the baseline above, main not re-run.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked the reported fix on main at d87e73f2db.

CapabilityInventory reads requiresActiveTarget from each named contract rather than applying native-provider as a blanket target gate. The regression enumerates all ten independently owned session operations and inspect_with_objdump, requires available without a binary, and keeps inspect_macho target_required. This also addresses the targetless architectures trigger in comments.

Implementation: CapabilityInventory.ts, CapabilityInventory.ts.

The relevant regression checks in CapabilityInventory.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380729440 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. CapabilityInventory reads requiresActiveTarget from each named contract rather than applying native-provider as a blanket target gate. The regression enumerates all ten independently owned session operations and inspect_with_objdump, requires available without a binary, and keeps inspect_macho target_required. This also addresses the targetless architectures trigger in comments. Implementation: [CapabilityInventory.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/CapabilityInventory.ts#L265), [CapabilityInventory.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/CapabilityInventory.ts#L749). The relevant regression checks in [CapabilityInventory.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/CapabilityInventory.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
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#3307
No description provided.