[GH-ISSUE #5730] [Bug] Linux-only tools report provider_missing instead of unsupported_host on macOS #3477

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

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

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

On macOS arm64, target-free binary_session {} reports five Linux-only tools as available: false, reason: "provider_missing". Their remediation already names Linux. In the same production stdio MCP connection, three representative calls return typed Linux-host refusals. The unavailable flag is correct, but its machine-readable reason confuses an unsupported host with a missing provider.

Reproduction

Run from a built source checkout on macOS with locked dependencies installed. This creates an isolated configuration and a synthetic hex fixture, queries discovery, and makes three calls on one connection. It opens no binary target and launches no GUI provider.

work_dir="$(mktemp -d)"
mkdir -p "$work_dir/home" "$work_dir/cfg" "$work_dir/data" "$work_dir/cache" "$work_dir/state" "$work_dir/tmp"
env -i PATH="$PATH" HOME="$work_dir/home" XDG_CONFIG_HOME="$work_dir/cfg" \
  XDG_DATA_HOME="$work_dir/data" XDG_CACHE_HOME="$work_dir/cache" \
  XDG_STATE_HOME="$work_dir/state" TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent \
  python3 - "$work_dir" <<'PY'
import json, os, pathlib, subprocess, sys
work=pathlib.Path(sys.argv[1]); work.mkdir(exist_ok=True)
for n in ('home','cfg','data','cache','state','tmp'): (work/n).mkdir(exist_ok=True)
(work/'runtime.hex').write_text('0x600160005260206000f3\n')
env={'PATH':os.environ['PATH'],'HOME':str(work/'home'),'XDG_CONFIG_HOME':str(work/'cfg'),'XDG_DATA_HOME':str(work/'data'),'XDG_CACHE_HOME':str(work/'cache'),'XDG_STATE_HOME':str(work/'state'),'TMPDIR':str(work/'tmp'),'REA_LOG_LEVEL':'silent'}
log=open(work/'stderr.log','w')
p=subprocess.Popen(['node','scripts/rea.mjs','mcp'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=log,env=env,text=True,bufsize=1)
frames=[]
def request(i,method,params):
 p.stdin.write(json.dumps({'jsonrpc':'2.0','id':i,'method':method,'params':params})+'\n');p.stdin.flush()
 while True:
  line=p.stdout.readline()
  if not line: raise RuntimeError('server stdout closed')
  msg=json.loads(line);frames.append(msg)
  if msg.get('id')==i:return msg
def call(i,name,args):
 reply=request(i,'tools/call',{'name':name,'arguments':args})
 r=reply['result']; body=r.get('structuredContent')
 if body is None:body=json.loads(next(c['text'] for c in r['content'] if c['type']=='text'))
 return body.get('result',body)
try:
 request(1,'initialize',{'protocolVersion':'2025-11-25','capabilities':{},'clientInfo':{'name':'independent-verifier','version':'1'}})
 p.stdin.write(json.dumps({'jsonrpc':'2.0','method':'notifications/initialized'})+'\n');p.stdin.flush()
 session=call(2,'binary_session',{})
 names={'inspect_signal_decoders','decode_signal_recording','inspect_binary_layout','inspect_recorded_crash','inspect_evm_interface'}
 for g in session['tool_availability']['groups']:
  if names.intersection(g['names']):print(json.dumps({'availability':sorted(names.intersection(g['names'])),'available':g['available'],'reason':g['reason'],'remediation':g['remediation']}))
 for i,(name,args) in enumerate([('inspect_signal_decoders',{}),('inspect_binary_layout',{'path':str(work/'runtime.hex')}),('inspect_evm_interface',{'path':str(work/'runtime.hex'),'encoding':'hex'})],start=3):print(json.dumps({'call':name,'result':call(i,name,args)}))
finally:
 p.stdin.close();p.wait(timeout=20);log.close()
 (work/'frames.json').write_text(json.dumps(frames,indent=2))
 print('server_exit',p.returncode)
PY

The independent verifier executed this sequence at the baseline below. Before submission, I built current main with npm run build:cached and reran the same Python probe with a fresh isolated directory. Both revisions returned all five incorrect reason values and the three host refusals; both servers exited 0 after stdin EOF.

Expected behavior

Keep these tools unavailable, but report reason: "unsupported_host" with their host requirements. Installing a provider on the same unsupported host cannot satisfy the execution guard.

At the baseline, docs/mcp-contracts.md:470-481 defines discovery for the current target, provider, host, and client capabilities, carrying each group's reason and remediation. src/contracts/toolOutputSchemaPrimitives.ts:373-380 separately represents unsupported_host and provider_missing. src/application/CapabilityInventory.ts:577-580,604-612 uses or preserves host reasons for other operations.

Impact and scope

P3: the observed defect is an inaccurate diagnostic reason. All five entries remain correctly unavailable, their human-readable Linux requirements remain present, and the three tested calls correctly refuse execution. No consequential client selection behavior, data mutation, or security impact was demonstrated. Callers can respect available: false and read the Linux remediation as a practical workaround. The other two tools' shared execution guards were inspected statically. Linux positive-provider behavior and other host architectures were not tested.

REA version

rea-agents 6.7.0 at verified baseline 9d8e7803326a382cbbb3ffdb900f5e93e5442d68. Rechecked after building origin/main d0067ba34a39ea47dd7e11bb2e6169bc0938cc3f, also 6.7.0: still reproduces. Intervening edits to cited files add unrelated resource-session documentation and Objective-C schemas; the cited availability branches and execution guards are unchanged.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1 arm64. Production stdio MCP, protocol 2025-11-25, replacement HOME/XDG/TMPDIR environment. The main build was also checked with the repository-pinned Node.js 24.18.0. Hopper/Ghidra are not involved.

Target and artifact context

No active binary target. A locally generated runtime.hex contains 0x600160005260206000f3. No chain endpoint, hardware, live process, or external provider command is needed to reach these host refusals.

Interface

MCP

Earliest failure stage

Setup or provider admission

Relevant output or evidence

Observed on both revisions, with unrelated discovery metadata omitted:

[decode_signal_recording, inspect_signal_decoders] available=false reason=provider_missing
[inspect_binary_layout, inspect_recorded_crash] available=false reason=provider_missing
[inspect_evm_interface] available=false reason=provider_missing

All three calls return code: "capability_unavailable", category: "unsupported_provider", retryable: false. Their messages identify the host requirement:

inspect_signal_decoders: Signal decoding requires Linux with util-linux prlimit and REA_SIGROK_COMMAND so expanded recordings are processed under an enforced address-space limit.
inspect_binary_layout: The initial real-verified pwntools artifact profile supports Linux x64 only.
inspect_evm_interface: Initial real-verified offline EVM interface profile supports Linux x64 only.
server_exit 0

Evidence provenance: observed; local verification: independently reproduced at baseline and replayed on main. Direct calls covered these three tools; discovery covered all five.

Typed outcome or recovery expected

Discovery should identify unsupported_host while preserving the existing typed execution refusals. Proposed restoration checks: repeat the macOS sequence and confirm all five reasons change while availability stays false. On supported hosts, preserve each provider's own prerequisites and distinguish missing tools from available capability. EVM is bundled; signal and pwntools operations require caller-supplied commands. These supported-host controls were not executed.

Analysis or suspected cause

Source-derived reasoning: src/server/createServer.ts:207-229 folds host support and provider configuration into feature booleans. src/server/sessionAvailabilityPolicy.ts:31-49 carries them into policy, and src/application/CapabilityInventory.ts:467-520 returns literal provider_missing for the disabled features. Execution guards at src/signals/SigrokRecordingProvider.ts:136-139, src/native/pwntools/PwntoolsDecoder.ts:253-260, and src/evm/EvmoleInterfaceProvider.ts:413-420 reject these hosts before checking the command or limiter. These references are at the baseline and unchanged on the main revision tested. This classification originates in REA's policy/projection, rather than an observed bundled-dependency failure.

Additional context

Related reports have different restoration signals: #5711 concerns pcap capture incorrectly advertised as available; #5447 concerns false open-target prerequisites; #5086 concerns a missing EVM limiter on supported Linux; #3069 and #5451 concern JEB endpoint probing and recovery guidance. Correcting those signals can leave these five host reason values unchanged.

Prepared with AI assistance and an independent verifier. No patch or successful restoration was tested. NOT_ENOUGH_INFO: the first affected and last known-good revisions were not established.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5730 ### 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 On macOS arm64, target-free `binary_session {}` reports five Linux-only tools as `available: false`, `reason: "provider_missing"`. Their remediation already names Linux. In the same production stdio MCP connection, three representative calls return typed Linux-host refusals. The unavailable flag is correct, but its machine-readable reason confuses an unsupported host with a missing provider. ### Reproduction Run from a built source checkout on macOS with locked dependencies installed. This creates an isolated configuration and a synthetic hex fixture, queries discovery, and makes three calls on one connection. It opens no binary target and launches no GUI provider. ```sh work_dir="$(mktemp -d)" mkdir -p "$work_dir/home" "$work_dir/cfg" "$work_dir/data" "$work_dir/cache" "$work_dir/state" "$work_dir/tmp" env -i PATH="$PATH" HOME="$work_dir/home" XDG_CONFIG_HOME="$work_dir/cfg" \ XDG_DATA_HOME="$work_dir/data" XDG_CACHE_HOME="$work_dir/cache" \ XDG_STATE_HOME="$work_dir/state" TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent \ python3 - "$work_dir" <<'PY' import json, os, pathlib, subprocess, sys work=pathlib.Path(sys.argv[1]); work.mkdir(exist_ok=True) for n in ('home','cfg','data','cache','state','tmp'): (work/n).mkdir(exist_ok=True) (work/'runtime.hex').write_text('0x600160005260206000f3\n') env={'PATH':os.environ['PATH'],'HOME':str(work/'home'),'XDG_CONFIG_HOME':str(work/'cfg'),'XDG_DATA_HOME':str(work/'data'),'XDG_CACHE_HOME':str(work/'cache'),'XDG_STATE_HOME':str(work/'state'),'TMPDIR':str(work/'tmp'),'REA_LOG_LEVEL':'silent'} log=open(work/'stderr.log','w') p=subprocess.Popen(['node','scripts/rea.mjs','mcp'],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=log,env=env,text=True,bufsize=1) frames=[] def request(i,method,params): p.stdin.write(json.dumps({'jsonrpc':'2.0','id':i,'method':method,'params':params})+'\n');p.stdin.flush() while True: line=p.stdout.readline() if not line: raise RuntimeError('server stdout closed') msg=json.loads(line);frames.append(msg) if msg.get('id')==i:return msg def call(i,name,args): reply=request(i,'tools/call',{'name':name,'arguments':args}) r=reply['result']; body=r.get('structuredContent') if body is None:body=json.loads(next(c['text'] for c in r['content'] if c['type']=='text')) return body.get('result',body) try: request(1,'initialize',{'protocolVersion':'2025-11-25','capabilities':{},'clientInfo':{'name':'independent-verifier','version':'1'}}) p.stdin.write(json.dumps({'jsonrpc':'2.0','method':'notifications/initialized'})+'\n');p.stdin.flush() session=call(2,'binary_session',{}) names={'inspect_signal_decoders','decode_signal_recording','inspect_binary_layout','inspect_recorded_crash','inspect_evm_interface'} for g in session['tool_availability']['groups']: if names.intersection(g['names']):print(json.dumps({'availability':sorted(names.intersection(g['names'])),'available':g['available'],'reason':g['reason'],'remediation':g['remediation']})) for i,(name,args) in enumerate([('inspect_signal_decoders',{}),('inspect_binary_layout',{'path':str(work/'runtime.hex')}),('inspect_evm_interface',{'path':str(work/'runtime.hex'),'encoding':'hex'})],start=3):print(json.dumps({'call':name,'result':call(i,name,args)})) finally: p.stdin.close();p.wait(timeout=20);log.close() (work/'frames.json').write_text(json.dumps(frames,indent=2)) print('server_exit',p.returncode) PY ``` The independent verifier executed this sequence at the baseline below. Before submission, I built current main with `npm run build:cached` and reran the same Python probe with a fresh isolated directory. Both revisions returned all five incorrect reason values and the three host refusals; both servers exited 0 after stdin EOF. ### Expected behavior Keep these tools unavailable, but report `reason: "unsupported_host"` with their host requirements. Installing a provider on the same unsupported host cannot satisfy the execution guard. At the baseline, `docs/mcp-contracts.md:470-481` defines discovery for the current target, provider, host, and client capabilities, carrying each group's reason and remediation. `src/contracts/toolOutputSchemaPrimitives.ts:373-380` separately represents `unsupported_host` and `provider_missing`. `src/application/CapabilityInventory.ts:577-580,604-612` uses or preserves host reasons for other operations. ### Impact and scope P3: the observed defect is an inaccurate diagnostic reason. All five entries remain correctly unavailable, their human-readable Linux requirements remain present, and the three tested calls correctly refuse execution. No consequential client selection behavior, data mutation, or security impact was demonstrated. Callers can respect `available: false` and read the Linux remediation as a practical workaround. The other two tools' shared execution guards were inspected statically. Linux positive-provider behavior and other host architectures were not tested. ### REA version `rea-agents` 6.7.0 at verified baseline `9d8e7803326a382cbbb3ffdb900f5e93e5442d68`. Rechecked after building origin/main `d0067ba34a39ea47dd7e11bb2e6169bc0938cc3f`, also 6.7.0: still reproduces. Intervening edits to cited files add unrelated resource-session documentation and Objective-C schemas; the cited availability branches and execution guards are unchanged. ### Runtime and platform Node.js v26.12.0; macOS 27.0.1 arm64. Production stdio MCP, protocol 2025-11-25, replacement HOME/XDG/TMPDIR environment. The main build was also checked with the repository-pinned Node.js 24.18.0. Hopper/Ghidra are not involved. ### Target and artifact context No active binary target. A locally generated `runtime.hex` contains `0x600160005260206000f3`. No chain endpoint, hardware, live process, or external provider command is needed to reach these host refusals. ### Interface MCP ### Earliest failure stage Setup or provider admission ### Relevant output or evidence Observed on both revisions, with unrelated discovery metadata omitted: ```text [decode_signal_recording, inspect_signal_decoders] available=false reason=provider_missing [inspect_binary_layout, inspect_recorded_crash] available=false reason=provider_missing [inspect_evm_interface] available=false reason=provider_missing ``` All three calls return `code: "capability_unavailable"`, `category: "unsupported_provider"`, `retryable: false`. Their messages identify the host requirement: ```text inspect_signal_decoders: Signal decoding requires Linux with util-linux prlimit and REA_SIGROK_COMMAND so expanded recordings are processed under an enforced address-space limit. inspect_binary_layout: The initial real-verified pwntools artifact profile supports Linux x64 only. inspect_evm_interface: Initial real-verified offline EVM interface profile supports Linux x64 only. server_exit 0 ``` Evidence provenance: observed; local verification: independently reproduced at baseline and replayed on main. Direct calls covered these three tools; discovery covered all five. ### Typed outcome or recovery expected Discovery should identify `unsupported_host` while preserving the existing typed execution refusals. Proposed restoration checks: repeat the macOS sequence and confirm all five reasons change while availability stays false. On supported hosts, preserve each provider's own prerequisites and distinguish missing tools from available capability. EVM is bundled; signal and pwntools operations require caller-supplied commands. These supported-host controls were not executed. ### Analysis or suspected cause Source-derived reasoning: `src/server/createServer.ts:207-229` folds host support and provider configuration into feature booleans. `src/server/sessionAvailabilityPolicy.ts:31-49` carries them into policy, and `src/application/CapabilityInventory.ts:467-520` returns literal `provider_missing` for the disabled features. Execution guards at `src/signals/SigrokRecordingProvider.ts:136-139`, `src/native/pwntools/PwntoolsDecoder.ts:253-260`, and `src/evm/EvmoleInterfaceProvider.ts:413-420` reject these hosts before checking the command or limiter. These references are at the baseline and unchanged on the main revision tested. This classification originates in REA's policy/projection, rather than an observed bundled-dependency failure. ### Additional context Related reports have different restoration signals: #5711 concerns pcap capture incorrectly advertised as available; #5447 concerns false open-target prerequisites; #5086 concerns a missing EVM limiter on supported Linux; #3069 and #5451 concern JEB endpoint probing and recovery guidance. Correcting those signals can leave these five host reason values unchanged. Prepared with AI assistance and an independent verifier. No patch or successful restoration was tested. NOT_ENOUGH_INFO: the first affected and last known-good revisions were not established.
Author
Owner

@Lew1sWong commented on GitHub (Oct 21, 2026):

Reproduced on current main (7d8e4e433), macOS arm64, with the issue's MCP probe: all five Linux-only tools report provider_missing, and the three direct calls return the Linux host refusals. I'd like to work on a fix and will open a PR shortly.

<!-- gh-comment-id:6308889419 --> @Lew1sWong commented on GitHub (Oct 21, 2026): Reproduced on current main (7d8e4e433), macOS arm64, with the issue's MCP probe: all five Linux-only tools report `provider_missing`, and the three direct calls return the Linux host refusals. I'd like to work on a fix and will open a PR shortly.
Author
Owner

@XinwangTao commented on GitHub (Oct 21, 2026):

Confirmed bug — on macOS the discovery flag is right (unavailable) but the machine-readable reason is wrong: provider_missing implies "install/configure a provider will fix this", when the real cause is host incompatibility and the remediation already says Linux. Callers who branch on reason (e.g. to prompt for provider setup) would send users down a dead end. The unsupported_host classification in the typed refusals is the correct semantic; aligning discovery with it, as #5793 proposes, is the right fix.

<!-- gh-comment-id:6311447161 --> @XinwangTao commented on GitHub (Oct 21, 2026): Confirmed bug — on macOS the discovery flag is right (unavailable) but the machine-readable reason is wrong: provider_missing implies "install/configure a provider will fix this", when the real cause is host incompatibility and the remediation already says Linux. Callers who branch on reason (e.g. to prompt for provider setup) would send users down a dead end. The unsupported_host classification in the typed refusals is the correct semantic; aligning discovery with it, as #5793 proposes, is the right fix.
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#3477
No description provided.