Skip to content

[Bug] binary_session advertises pcap capture inspection as available on macOS #5711

Closed

Description

@coygeek

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, binary_session {} advertises inspect_pcap_capture as available even though capture inspection is unsupported on this host. In the same stdio MCP session, inspect_pcap_client {} succeeds, but inspect_pcap_capture returns a non-retryable capability_unavailable / unsupported_provider host refusal. A subsequent binary_session {} still advertises both tools as available. No binary target is open.

Reproduction

From a built source checkout with locked dependencies installed, run this on macOS. The fixture executables answer only version probes; Wireshark and live traffic are unnecessary.

work_dir="$(mktemp -d)"
mkdir -p "$work_dir/bin" "$work_dir/home" "$work_dir/cfg" "$work_dir/data"   "$work_dir/cache" "$work_dir/state" "$work_dir/tmp"
cat > "$work_dir/bin/tshark" <<'SH'
#!/bin/sh
if [ "$1" = "--version" ]; then printf 'TShark (Wireshark) 4.0.17\n'; exit 0; fi
exit 1
SH
cat > "$work_dir/bin/capinfos" <<'SH'
#!/bin/sh
if [ "$1" = "--version" ]; then printf 'Capinfos (Wireshark) 4.0.17\n'; exit 0; fi
exit 1
SH
chmod 755 "$work_dir/bin/tshark" "$work_dir/bin/capinfos"
printf '\324\303\262\241\002\000\004\000\000\000\000\000\000\000\000\000\377\377\000\000\001\000\000\000'   > "$work_dir/capture.pcap"
cat > "$work_dir/probe.py" <<'PYTHON'
import json, os, subprocess, sys
from pathlib import Path
s=Path(os.environ["HOME"]).parent
p=subprocess.Popen(["node","scripts/rea.mjs","mcp"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=(s/"mcp-stderr.txt").open("w"),text=True)
transcript=(s/"mcp-transcript.jsonl").open("w")
def send(msg):
    transcript.write(json.dumps({"direction":"request","message":msg})+"\n");transcript.flush()
    p.stdin.write(json.dumps(msg)+"\n");p.stdin.flush()
def request(i, method, params):
    send({"jsonrpc":"2.0","id":i,"method":method,"params":params})
    for line in p.stdout:
        msg=json.loads(line)
        transcript.write(json.dumps({"direction":"response","message":msg})+"\n");transcript.flush()
        if msg.get("id")==i:
            (s/f"response-{i}.json").write_text(json.dumps(msg,indent=2))
            return msg
    raise RuntimeError("server exited")
try:
    init=request(1,"initialize",{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"pcap-host-verifier","version":"1"}})
    send({"jsonrpc":"2.0","method":"notifications/initialized"})
    for i,name,args in [(2,"binary_session",{}),(3,"inspect_pcap_client",{}),(4,"inspect_pcap_capture",{"path":str(s/"capture.pcap")}),(5,"binary_session",{})]:
        response=request(i,"tools/call",{"name":name,"arguments":args})
        result=response.get("result",{})
        blocks=result.get("content",[])
        body=next((json.loads(b["text"]) for b in blocks if b["type"]=="text"),result)
        if name=="binary_session":
            status=body.get("result",body)
            groups=status.get("tool_availability",{}).get("groups",[])
            print(json.dumps({"request":i,"name":name,"open":status.get("open"),"groups":[g for g in groups if any(n in g["names"] for n in ["inspect_pcap_client","inspect_pcap_capture"])]}))
        else:
            print(json.dumps({"request":i,"name":name,"body":body}))
finally:
    p.stdin.close()
    try: p.wait(timeout=20)
    except subprocess.TimeoutExpired:
        p.terminate();p.wait(timeout=10)
    transcript.close()
PYTHON
env -i PATH="$work_dir/bin:$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/probe.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 sequence there, changing only the disposable work-directory location. Both runs show the mismatch before and after the capture refusal. The probe retains exact JSON-RPC messages in mcp-transcript.jsonl under its generated work directory.

Expected behavior

Report capture inspection unavailable with an explanatory host limitation, while keeping the host-independent client version probe available. Preserve both tools in the complete catalog. docs/mcp-contracts.md:465-480 distinguishes complete tools/list membership from tool_availability, which callers use to choose a callable operation for the current host. docs/pcap-analysis.md:20-24 explicitly excludes macOS capture inspection while retaining version probes.

Impact and scope

P2: the documented operation-selection signal causes an avoidable failed call and remains wrong after the host refusal. Capture execution correctly refuses unsupported work; no data loss or security boundary violation was observed. A practical workaround is to consult the documented host requirements and omit capture inspection on macOS; performing capture analysis still requires a supported host. Linux/Windows execution and downstream client selection frequency were not tested.

REA version

rea-agents 6.7.0, verified source baseline 9d8e7803326a382cbbb3ffdb900f5e93e5442d68. Rechecked after a fresh runtime build of origin/main ccc7da309d7db385d5c6d73b0a3a8e093be33504 (also 6.7.0): still reproduces. The only change to the cited files between these revisions adds unrelated app-resource-session documentation; the cited pcap implementation is unchanged.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1 arm64. Private HOME/XDG/TMPDIR; fixture TShark and Capinfos 4.0.17 version banners. Hopper/Ghidra are not involved.

Target and artifact context

No active binary target. A self-created 24-byte empty little-endian pcap header supplies the required capture path. Source inspection shows the unsupported-host refusal occurs before snapshot acquisition or capture reading, so it does not depend on packet contents. These fixtures establish version admission and the host refusal, not real Wireshark dissection.

Interface

MCP

Earliest failure stage

Setup or provider admission

Relevant output or evidence

Observed before and after capture invocation, with unrelated discovery metadata omitted:

{"open":false,"group":{"names":["inspect_pcap_capture","inspect_pcap_client"],"available":true,"reason":"available","remediation":null}}

The client probe succeeds and reports both supplied version banners. Capture invocation returns this error subset:

{
  "error": {
    "code": "capability_unavailable",
    "category": "unsupported_provider",
    "message": "Packet summaries require an enforceable native memory bound: Linux with util-linux prlimit, or Windows with owned Job Objects. This host's capture boundary remains unsupported.",
    "retryable": false
  }
}

The CLI control, node scripts/rea.mjs inspect-pcap-capture "$work_dir/capture.pcap" --json under the same isolated environment, exits 1 with the same typed refusal at both revisions. Evidence provenance: observed; local verification: independently reproduced.

Typed outcome or recovery expected

Before execution and after a refused call, discovery should distinguish unsupported capture inspection from the available client probe. Return the host limitation as availability remediation. Proposed restoration check: repeat this MCP sequence on macOS, preserve client-probe success and complete catalog membership, and separately confirm capture inspection remains available on a supported host satisfying its prerequisites. No patch or successful restoration was tested.

Analysis or suspected cause

Source-derived reasoning: src/application/CapabilityInventory.ts:583-584 unconditionally marks pcap-provider tools available. The capture handler at src/server/registerPcapTools.ts:45-81 reaches src/pcap/PcapProvider.ts:519-543, which probes the clients and then calls native-bound preparation before reading the capture. src/pcap/PcapNativeBound.ts:19-35 produces the observed unsupported-host error. The separate readiness check at src/pcap/PcapProvider.ts:314-327 also recognizes the host constraint, but is not the producer of this execution refusal.

Additional context

Related: #5447 concerns the opposite discovery mismatch, where target-independent session tools are advertised unavailable until open_binary. This report concerns capture availability advertised on a host that cannot execute it. #3403 and PR #3407 concern shared TShark selection/version ownership, rather than this operation-specific host fact. The broader lifecycle audit in #4227 requests focused reports for confirmed defects.

Prepared with AI assistance and an independent verifier. NOT_ENOUGH_INFO: last known good version and first affected version were not established.

Activity

  1. Iyanuoluwa007 commented on Oct 21, 2026

    @Iyanuoluwa007
    Contributor

    Fix is ready: fix/pcap-capture-host-availability (commit 49a1c60). The capability inventory treated every pcap-provider tool as available. The provider and native-bound checks and the default availability policy now share one host predicate (pcapCaptureHostSupported: Linux or Windows), so binary_session reports inspect_pcap_capture as unsupported_host on macOS, while inspect_pcap_client stays available. Covered by a CapabilityInventory regression test that fails without the change. I'll open the PR as soon as one of my open PR slots frees up (the 3-open-PR limit).

  2. added 2 commits that reference this issue on Oct 21, 2026
    a06f713
    e6ce1c5
  3. coygeek commented on Oct 24, 2026

    @coygeek
    Author

    Checked the reported fix on main at d87e73f.

    CapabilityInventory handles inspect_pcap_capture separately through hostRestrictedAvailability with pcapCapture host policy, while inspect_pcap_client remains in the available generic pcap group. The policy and provider use the shared supported-host predicate. A Darwin/Linux/Windows inventory matrix asserts capture unsupported_host on Darwin and client available.

    Implementation: CapabilityInventory.ts, CapabilityInventory.test.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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions