[GH-ISSUE #3947] [Bug] Offline HAR inspection is advertised unavailable without a browser provider #1553

Closed
opened 2026-10-16 21:07:04 +00:00 by bsakaguchi · 0 comments
Owner

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

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

Observed in both executed cases: inspect_web_network_capture has available:false, while calling it on the same connection succeeds and returns schema-valid HAR Evidence with total_records:0 and records:[].

With the browser port omitted and no failure metadata, the entry has reason:"provider_missing" and remediation:"Start REA with its browser observation provider available.". With a simulated recorded load failure, it instead has reason:"provider_unavailable" and remediation:"Adapter rea-cdp-browser could not load: synthetic browser import failure. Repair the adapter installation and restart REA; other capabilities remain available.".

Observed control: list_browser_targets advertises the corresponding unavailable reason and returns isError:true. The successful HAR inspection returns no error. This distinguishes an incorrect availability report from an actual requirement to connect a browser.

Reproduction

  1. Use the stated baseline with its locked dependencies and built runtime already prepared. Create the following temporary MCP boundary test as tests/boundary/mcp/networkCaptureAvailability.test.ts. It writes a valid empty HAR into an isolated home, creates a session with no browser port, calls binary_session, and then invokes the advertised-unavailable operation. The second case adds the failure metadata that startup retains when browser adapter loading fails.
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
import { toolContract } from "../../../src/contracts/toolContracts.js";
import { expandToolAvailability } from "../../../src/contracts/toolOutputSchemaPrimitives.js";
import { createServer } from "../../../dist/server/createServer.js";
import { createTestBinarySession } from "../../fixtures/binarySession.js";
import { createTestMcp } from "../../support/mcp/mcpFixture.js";

it.each([false, true])("offline capture succeeds despite false availability, adapter failure=%s", async (failed) => {
  const capturePath = join(process.env.HOME!, "capture.har");
  await writeFile(capturePath, JSON.stringify({ log: { version: "1.2", creator: { name: "fixture", version: "1" }, entries: [] } }));
  const session = createTestBinarySession(() => { throw new Error("No binary provider should start"); });
  const mcp = createTestMcp();
  const server = createServer({ kind: "session", session }, {
    environment: { ...process.env },
    ...(failed ? { optionalProviderLoadFailures: { browserObservation: { providerId: "rea-cdp-browser", reason: "synthetic browser import failure" } } } : {}),
  });
  try {
    const client = await mcp.connect(server);
    const status = await client.callTool({ name: "binary_session", arguments: {} });
    expect(status.isError).not.toBe(true);
    const parsed = toolContract("binary_session").outputSchema.parse(status.structuredContent);
    const availability = expandToolAvailability(parsed.result.tool_availability);
    const offline = availability.find(({ name }) => name === "inspect_web_network_capture");
    const live = availability.find(({ name }) => name === "list_browser_targets");
    console.log(JSON.stringify({ failed, offline, live }));
    expect(offline).toMatchObject({ available: false, reason: failed ? "provider_unavailable" : "provider_missing" });
    expect(live).toMatchObject({ available: false, reason: failed ? "provider_unavailable" : "provider_missing" });
    const response = await client.callTool({ name: "inspect_web_network_capture", arguments: { capture_path: capturePath, format: "har" } });
    console.log(JSON.stringify({ failed, isError: response.isError ?? false }));
    expect(response.isError).not.toBe(true);
    const capture = toolContract("inspect_web_network_capture").outputSchema.parse(response.structuredContent);
    expect(capture.normalized_result).toMatchObject({ format: "har", total_records: 0, records: [] });
    const denied = await client.callTool({ name: "list_browser_targets", arguments: { cdp_endpoint: "http://127.0.0.1:9222" } });
    expect(denied.isError).toBe(true);
  } finally {
    await mcp.close();
    await session.close();
  }
});
  1. Run only that test from the repository root with an isolated home:
scratch_dir="$(mktemp -d)"
mkdir -p "$scratch_dir/home"
HOME="$scratch_dir/home" npx --no-install vitest run tests/boundary/mcp/networkCaptureAvailability.test.ts
rm tests/boundary/mcp/networkCaptureAvailability.test.ts
rm -rf "$scratch_dir"
  1. Inspect the printed availability entries and successful inspection result. The two tests passed in the recorded run because they assert the existing contradiction. The test also invokes list_browser_targets as a control and confirms that this genuinely browser-dependent operation returns an error.

Expected behavior

Availability should describe the operation's actual requirements. Retained capture inspection is documented as offline and starts no browser or proxy listener. The MCP discovery contract tells callers to use binary_session.result.tool_availability to select callable operations. For the valid local HAR in this reproduction, the inspection operation should be available and should carry no browser-adapter remediation. The live-browser control should remain unavailable.

Impact and scope

Incorrect operation discovery can prevent an MCP client from choosing a working offline inspection operation and gives unrelated repair advice. The observed scope is sessions without a browser observation port, with and without recorded browser-load failure. The inspection itself remains callable; no data loss or broader service failure was observed.

Workaround: In both tested sessions, directly invoking the advertised-unavailable inspection operation on a valid local HAR succeeded. A client can bypass the incorrect availability entry without installing or repairing a browser adapter.

REA version

rea 6.5.0 at source commit 4692bb3330; current-main source checked at 22ec8bdb2f

Runtime and platform

Node 24.18.0; macOS arm64

Target and artifact context

Valid empty local HAR 1.2 capture; SDK in-memory MCP connection to the built production server. Browser-port omission and optional browser-load failure metadata were simulated.

Interface

MCP

Earliest failure stage

Setup or provider admission

Relevant output or evidence

docs/web-network-captures.md:3-5 documents offline inspection; docs/mcp-contracts.md:346-354 defines availability as the caller selection guide. src/contracts/webNetworkCaptureToolContracts.ts:20 assigns browser-provider; src/application/CapabilityInventory.ts:294-325,571-592 applies browser absence/failure to it. src/server/createServer.ts:221,312-313,751-759 composes and registers the real offline service independently. Both tested sessions advertised unavailable yet decoded the local HAR successfully; list_browser_targets remained unavailable and returned an error.

Typed outcome or recovery expected

Advertise inspect_web_network_capture as available for these valid local-capture sessions, without browser-repair remediation. Preserve unavailability of genuinely browser-dependent operations.

Analysis or suspected cause

Source-derived: the tool kind causes discovery to apply an unrelated browser requirement. src/composition/optionalObservationProviders.ts:67-104 and src/main/transport.ts:91-99 show startup can supply the simulated states. No real adapter installation was damaged or import failure induced.

Additional context

Runtime decoder coverage is HAR only. Other supported formats share the availability classification by source. The successful test used the existing built server and real HAR decoder; the initial source-mode test lacked a generated child entry file.

Freshness check: the failing source is unchanged on current upstream main at 22ec8bdb2f. The only change among the cited files is unrelated CLI output-flag normalization. Runtime observations above are from the stated baseline; current-main persistence is source-derived.

Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3947 ### 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 Observed in both executed cases: `inspect_web_network_capture` has `available:false`, while calling it on the same connection succeeds and returns schema-valid HAR Evidence with `total_records:0` and `records:[]`. With the browser port omitted and no failure metadata, the entry has `reason:"provider_missing"` and `remediation:"Start REA with its browser observation provider available."`. With a simulated recorded load failure, it instead has `reason:"provider_unavailable"` and `remediation:"Adapter rea-cdp-browser could not load: synthetic browser import failure. Repair the adapter installation and restart REA; other capabilities remain available."`. Observed control: `list_browser_targets` advertises the corresponding unavailable reason and returns `isError:true`. The successful HAR inspection returns no error. This distinguishes an incorrect availability report from an actual requirement to connect a browser. ### Reproduction 1. Use the stated baseline with its locked dependencies and built runtime already prepared. Create the following temporary MCP boundary test as `tests/boundary/mcp/networkCaptureAvailability.test.ts`. It writes a valid empty HAR into an isolated home, creates a session with no browser port, calls `binary_session`, and then invokes the advertised-unavailable operation. The second case adds the failure metadata that startup retains when browser adapter loading fails. ```ts import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { expect, it } from "vitest"; import { toolContract } from "../../../src/contracts/toolContracts.js"; import { expandToolAvailability } from "../../../src/contracts/toolOutputSchemaPrimitives.js"; import { createServer } from "../../../dist/server/createServer.js"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; import { createTestMcp } from "../../support/mcp/mcpFixture.js"; it.each([false, true])("offline capture succeeds despite false availability, adapter failure=%s", async (failed) => { const capturePath = join(process.env.HOME!, "capture.har"); await writeFile(capturePath, JSON.stringify({ log: { version: "1.2", creator: { name: "fixture", version: "1" }, entries: [] } })); const session = createTestBinarySession(() => { throw new Error("No binary provider should start"); }); const mcp = createTestMcp(); const server = createServer({ kind: "session", session }, { environment: { ...process.env }, ...(failed ? { optionalProviderLoadFailures: { browserObservation: { providerId: "rea-cdp-browser", reason: "synthetic browser import failure" } } } : {}), }); try { const client = await mcp.connect(server); const status = await client.callTool({ name: "binary_session", arguments: {} }); expect(status.isError).not.toBe(true); const parsed = toolContract("binary_session").outputSchema.parse(status.structuredContent); const availability = expandToolAvailability(parsed.result.tool_availability); const offline = availability.find(({ name }) => name === "inspect_web_network_capture"); const live = availability.find(({ name }) => name === "list_browser_targets"); console.log(JSON.stringify({ failed, offline, live })); expect(offline).toMatchObject({ available: false, reason: failed ? "provider_unavailable" : "provider_missing" }); expect(live).toMatchObject({ available: false, reason: failed ? "provider_unavailable" : "provider_missing" }); const response = await client.callTool({ name: "inspect_web_network_capture", arguments: { capture_path: capturePath, format: "har" } }); console.log(JSON.stringify({ failed, isError: response.isError ?? false })); expect(response.isError).not.toBe(true); const capture = toolContract("inspect_web_network_capture").outputSchema.parse(response.structuredContent); expect(capture.normalized_result).toMatchObject({ format: "har", total_records: 0, records: [] }); const denied = await client.callTool({ name: "list_browser_targets", arguments: { cdp_endpoint: "http://127.0.0.1:9222" } }); expect(denied.isError).toBe(true); } finally { await mcp.close(); await session.close(); } }); ``` 2. Run only that test from the repository root with an isolated home: ```sh scratch_dir="$(mktemp -d)" mkdir -p "$scratch_dir/home" HOME="$scratch_dir/home" npx --no-install vitest run tests/boundary/mcp/networkCaptureAvailability.test.ts rm tests/boundary/mcp/networkCaptureAvailability.test.ts rm -rf "$scratch_dir" ``` 3. Inspect the printed availability entries and successful inspection result. The two tests passed in the recorded run because they assert the existing contradiction. The test also invokes `list_browser_targets` as a control and confirms that this genuinely browser-dependent operation returns an error. ### Expected behavior Availability should describe the operation's actual requirements. Retained capture inspection is documented as offline and starts no browser or proxy listener. The MCP discovery contract tells callers to use `binary_session.result.tool_availability` to select callable operations. For the valid local HAR in this reproduction, the inspection operation should be available and should carry no browser-adapter remediation. The live-browser control should remain unavailable. ### Impact and scope Incorrect operation discovery can prevent an MCP client from choosing a working offline inspection operation and gives unrelated repair advice. The observed scope is sessions without a browser observation port, with and without recorded browser-load failure. The inspection itself remains callable; no data loss or broader service failure was observed. Workaround: In both tested sessions, directly invoking the advertised-unavailable inspection operation on a valid local HAR succeeded. A client can bypass the incorrect availability entry without installing or repairing a browser adapter. ### REA version rea 6.5.0 at source commit 4692bb33300c7cb77b095b6cc0c33da861c1b860; current-main source checked at 22ec8bdb2f7d4626587f23b1eed4a207df600386 ### Runtime and platform Node 24.18.0; macOS arm64 ### Target and artifact context Valid empty local HAR 1.2 capture; SDK in-memory MCP connection to the built production server. Browser-port omission and optional browser-load failure metadata were simulated. ### Interface MCP ### Earliest failure stage Setup or provider admission ### Relevant output or evidence `docs/web-network-captures.md:3-5` documents offline inspection; `docs/mcp-contracts.md:346-354` defines availability as the caller selection guide. `src/contracts/webNetworkCaptureToolContracts.ts:20` assigns browser-provider; `src/application/CapabilityInventory.ts:294-325,571-592` applies browser absence/failure to it. `src/server/createServer.ts:221,312-313,751-759` composes and registers the real offline service independently. Both tested sessions advertised unavailable yet decoded the local HAR successfully; list_browser_targets remained unavailable and returned an error. ### Typed outcome or recovery expected Advertise inspect_web_network_capture as available for these valid local-capture sessions, without browser-repair remediation. Preserve unavailability of genuinely browser-dependent operations. ### Analysis or suspected cause Source-derived: the tool kind causes discovery to apply an unrelated browser requirement. `src/composition/optionalObservationProviders.ts:67-104` and `src/main/transport.ts:91-99` show startup can supply the simulated states. No real adapter installation was damaged or import failure induced. ### Additional context Runtime decoder coverage is HAR only. Other supported formats share the availability classification by source. The successful test used the existing built server and real HAR decoder; the initial source-mode test lacked a generated child entry file. Freshness check: the failing source is unchanged on current upstream main at 22ec8bdb2f7d4626587f23b1eed4a207df600386. The only change among the cited files is unrelated CLI output-flag normalization. Runtime observations above are from the stated baseline; current-main persistence is source-derived.
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#1553
No description provided.