[PR #2854] [MERGED] fix(browser): preserve distinct WebMCP declaration metadata #5440

Closed
opened 2026-10-31 01:06:38 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2854
Author: @zhangguiping-xydt
Created: 10/13/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: fix/browser-webmcp-declaration-identity


📝 Commits (2)

  • f99c1f3 fix(browser): preserve distinct WebMCP declaration metadata
  • 4fd1955 test(browser): retain the first equivalent declaration location

📊 Changes

2 files changed (+59 additions, -9 deletions)

View changed files

📝 src/domain/webBundleAnalyzer.test.ts (+51 -0)
📝 src/domain/webBundleAnalyzerInspection.ts (+8 -9)

📄 Description

Summary

Fixes #2624. Preserve distinct WebMCP declaration metadata in captured page bundles while deduplicating equivalent recorded property sets.

Problem and expected behavior

What Problem This Solves

Two registerTool declarations named lookup with different descriptions collapse to the first one. Conversely, equivalent schema property sets written in a different order produce duplicate records. An absent tool name also collides with an explicitly empty name.

User Impact

analyze-web-bundle and analyze_web_bundle retain the distinct page-declared descriptions and name values, and return one record for equivalent metadata. These remain static, untrusted declarations; no successful runtime registration is inferred.

Change and scope

Why This Change Was Made

The deduplication key omitted description, converted absent names to empty strings, and used the property list before normalizing it. Build the returned metadata once and use that same value in the collision-free composite key. This keeps source-script identity and first equivalent declaration location.

Contract and boundary impact

  • Semantic owner: static WebMCP declaration collection in webBundleAnalyzerInspection.ts.
  • Scope/maintainer decision: routine correction of existing passive analysis; advance decision not applicable.
  • CLI/MCP/catalog: existing schemas and tool names; corrected declaration inventory only.
  • Provider/platform: shared analyzer, exercised through real Chrome capture on Linux.
  • Evidence: distinct observed metadata survives; equivalent metadata retains the first source location and untrusted designation.
  • Authority/cleanup: no new execution, mutation, network destination, or ownership behavior. The localhost proof owns its page and temporary Chrome profile.
  • Generated metadata, package, installation, migration: no changes.

Expected effects of the verified public workflow:

  • Read captured data.
  • Attach to an explicitly selected local browser.
  • Contact its loopback CDP endpoint.

Evidence and regression coverage

Evidence

Executed on Linux x64, Node 24.18.0, npm 11.16.0, Google Chrome 138.0.7204.157. Served an owned HTML page and /app.js on localhost; inspected the same Chrome target using the compiled CLI and an actual stdio MCP client. The page script defines the following function without calling it:

function declarations() {
  document.modelContext.registerTool({name:'lookup',description:'first',inputSchema:{properties:{alpha:{}}}});
  document.modelContext.registerTool({name:'lookup',description:'second',inputSchema:{properties:{alpha:{}}}});
  document.modelContext.registerTool({name:'equivalent',inputSchema:{properties:{alpha:{},beta:{}}}});
  document.modelContext.registerTool({name:'equivalent',inputSchema:{properties:{beta:{},alpha:{}}}});
  document.modelContext.registerTool({description:'unnamed'});
  document.modelContext.registerTool({name:'',description:'unnamed'});
}

Run node scripts/rea.mjs analyze-web-bundle <loopback-cdp-endpoint> <target-id> --observation-ms 0 --format json. MCP uses analyze_web_bundle with the same endpoint/target, the page origin in allowed_origins, include_script_sources: true, and observation_ms: 0. Inspect normalized_result.observations.webmcp_declarations.

Observed result in both CLI and MCP Before cbe769b After 4fd1955
lookup descriptions first only first, second
Equivalent property-order declarations 2 identical records 1 record
Absent and empty names absent only both distinct values
Total retained declarations 4 5

The current branch starts at 6e25cc3; the relevant producer was unchanged between that base and the live-before revision. The added regression failed on this branch's unmodified producer (1 failed / 23 passed), then passed after the fix. It also checks duplicate property spellings, untrusted metadata, and the exact first retained source location (script key, line, and column). Existing computed-name and composite-key collision controls remain covered.

  • Observations, inferences, and unknowns stay distinct.
  • Source identity and provenance are preserved.
  • Unavailable outcomes are not converted into facts.

Validation performed

  • npm run test:focused -- src/domain/webBundleAnalyzer.test.ts — 24 passed.
  • npm run build:cached — passed; compiled CLI and real stdio MCP then exercised at 4fd1955.
  • npm run check — attempted; blocked by pre-existing formatting in src/domain/functionComparison.test.ts and src/ida/IdaOperationRunner.ts. Both files are byte-identical to base 6e25cc3, and a direct formatter check reproduces those two failures.
  • Remaining check stages (typecheck, lint:built, knip, metadata:check) and npm run verify:architecture-guards — passed. Changed files pass formatting; git diff --check passes.
  • Follow-up location assertion: all 24 focused tests and npm run check:fast passed; rebuilt and repeated real CLI/MCP capture at 4fd1955. Both retain the first equivalent declaration at line 4, column 0 in the served script (the indented unit fixture uses column 6).
  • Remote CI: pending at submission; checks are evaluated separately for the updated commit.

Compatibility, safety, and release

No schema, dependency, package, or release changes. No private application data or credentials in the fixture. What was not tested: Windows/macOS, other Chrome versions, live WebMCP registration or invocation, the published npm package, and the full browser verifier. This evidence establishes static declaration extraction from genuinely captured scripts.

Before merge

  • Concrete bug linked; focused Conventional Commit title.
  • Scope and authority resolved for the final diff.
  • Regression fails before and passes after; relevant failure/negative controls retained.
  • Owning contracts and generated metadata need no changes.
  • Public CLI/MCP outputs demonstrated.
  • Final diff checked for unrelated edits, secrets, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/2854 **Author:** [@zhangguiping-xydt](https://github.com/zhangguiping-xydt) **Created:** 10/13/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/browser-webmcp-declaration-identity` --- ### 📝 Commits (2) - [`f99c1f3`](https://github.com/morluto/rea/commit/f99c1f3eb39f3ed8468bf8b40c4aa41a8509bf1a) fix(browser): preserve distinct WebMCP declaration metadata - [`4fd1955`](https://github.com/morluto/rea/commit/4fd1955ea4693406732af2ee461564fb55aa5977) test(browser): retain the first equivalent declaration location ### 📊 Changes **2 files changed** (+59 additions, -9 deletions) <details> <summary>View changed files</summary> 📝 `src/domain/webBundleAnalyzer.test.ts` (+51 -0) 📝 `src/domain/webBundleAnalyzerInspection.ts` (+8 -9) </details> ### 📄 Description ## Summary Fixes #2624. Preserve distinct WebMCP declaration metadata in captured page bundles while deduplicating equivalent recorded property sets. ## Problem and expected behavior ### What Problem This Solves Two `registerTool` declarations named `lookup` with different descriptions collapse to the first one. Conversely, equivalent schema property sets written in a different order produce duplicate records. An absent tool name also collides with an explicitly empty name. ### User Impact `analyze-web-bundle` and `analyze_web_bundle` retain the distinct page-declared descriptions and name values, and return one record for equivalent metadata. These remain static, untrusted declarations; no successful runtime registration is inferred. ## Change and scope ### Why This Change Was Made The deduplication key omitted description, converted absent names to empty strings, and used the property list before normalizing it. Build the returned metadata once and use that same value in the collision-free composite key. This keeps source-script identity and first equivalent declaration location. ## Contract and boundary impact - Semantic owner: static WebMCP declaration collection in `webBundleAnalyzerInspection.ts`. - Scope/maintainer decision: routine correction of existing passive analysis; advance decision not applicable. - CLI/MCP/catalog: existing schemas and tool names; corrected declaration inventory only. - Provider/platform: shared analyzer, exercised through real Chrome capture on Linux. - Evidence: distinct observed metadata survives; equivalent metadata retains the first source location and untrusted designation. - Authority/cleanup: no new execution, mutation, network destination, or ownership behavior. The localhost proof owns its page and temporary Chrome profile. - Generated metadata, package, installation, migration: no changes. Expected effects of the verified public workflow: - [x] Read captured data. - [x] Attach to an explicitly selected local browser. - [x] Contact its loopback CDP endpoint. ## Evidence and regression coverage ### Evidence Executed on Linux x64, Node 24.18.0, npm 11.16.0, Google Chrome 138.0.7204.157. Served an owned HTML page and `/app.js` on localhost; inspected the same Chrome target using the compiled CLI and an actual stdio MCP client. The page script defines the following function without calling it: ```js function declarations() { document.modelContext.registerTool({name:'lookup',description:'first',inputSchema:{properties:{alpha:{}}}}); document.modelContext.registerTool({name:'lookup',description:'second',inputSchema:{properties:{alpha:{}}}}); document.modelContext.registerTool({name:'equivalent',inputSchema:{properties:{alpha:{},beta:{}}}}); document.modelContext.registerTool({name:'equivalent',inputSchema:{properties:{beta:{},alpha:{}}}}); document.modelContext.registerTool({description:'unnamed'}); document.modelContext.registerTool({name:'',description:'unnamed'}); } ``` Run `node scripts/rea.mjs analyze-web-bundle <loopback-cdp-endpoint> <target-id> --observation-ms 0 --format json`. MCP uses `analyze_web_bundle` with the same endpoint/target, the page origin in `allowed_origins`, `include_script_sources: true`, and `observation_ms: 0`. Inspect `normalized_result.observations.webmcp_declarations`. | Observed result in both CLI and MCP | Before `cbe769b` | After `4fd1955` | | --- | --- | --- | | `lookup` descriptions | `first` only | `first`, `second` | | Equivalent property-order declarations | 2 identical records | 1 record | | Absent and empty names | absent only | both distinct values | | Total retained declarations | 4 | 5 | The current branch starts at `6e25cc3`; the relevant producer was unchanged between that base and the live-before revision. The added regression failed on this branch's unmodified producer (1 failed / 23 passed), then passed after the fix. It also checks duplicate property spellings, untrusted metadata, and the exact first retained source location (script key, line, and column). Existing computed-name and composite-key collision controls remain covered. - [x] Observations, inferences, and unknowns stay distinct. - [x] Source identity and provenance are preserved. - [x] Unavailable outcomes are not converted into facts. ## Validation performed - `npm run test:focused -- src/domain/webBundleAnalyzer.test.ts` — 24 passed. - `npm run build:cached` — passed; compiled CLI and real stdio MCP then exercised at `4fd1955`. - `npm run check` — attempted; blocked by pre-existing formatting in `src/domain/functionComparison.test.ts` and `src/ida/IdaOperationRunner.ts`. Both files are byte-identical to base `6e25cc3`, and a direct formatter check reproduces those two failures. - Remaining check stages (`typecheck`, `lint:built`, `knip`, `metadata:check`) and `npm run verify:architecture-guards` — passed. Changed files pass formatting; `git diff --check` passes. - Follow-up location assertion: all 24 focused tests and `npm run check:fast` passed; rebuilt and repeated real CLI/MCP capture at `4fd1955`. Both retain the first equivalent declaration at line 4, column 0 in the served script (the indented unit fixture uses column 6). - Remote CI: pending at submission; checks are evaluated separately for the updated commit. ## Compatibility, safety, and release No schema, dependency, package, or release changes. No private application data or credentials in the fixture. **What was not tested:** Windows/macOS, other Chrome versions, live WebMCP registration or invocation, the published npm package, and the full browser verifier. This evidence establishes static declaration extraction from genuinely captured scripts. ## Before merge - [x] Concrete bug linked; focused Conventional Commit title. - [x] Scope and authority resolved for the final diff. - [x] Regression fails before and passes after; relevant failure/negative controls retained. - [x] Owning contracts and generated metadata need no changes. - [x] Public CLI/MCP outputs demonstrated. - [x] Final diff checked for unrelated edits, secrets, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:06:38 +00:00
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#5440
No description provided.