fix(browser): preserve distinct WebMCP declaration metadata (#2854)

* fix(browser): preserve distinct WebMCP declaration metadata

* test(browser): retain the first equivalent declaration location
This commit is contained in:
xingzhou
2026-10-13 23:19:37 +08:00
committed by GitHub
parent 3e7c630223
commit 40e94aeb9d
2 changed files with 59 additions and 9 deletions
+51
View File
@@ -714,3 +714,54 @@ it("keeps RPC lexical identity and bundled factory recognition in captured scrip
]);
expect(result.completeness.status).toBe("complete");
});
it("retains distinct WebMCP descriptions and names while deduplicating equivalent properties", () => {
const result = analyzeCapturedWebBundle(
inspection(`
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: {}, alpha: {} } } });
document.modelContext.registerTool({ description: "unnamed" });
document.modelContext.registerTool({ name: "", description: "unnamed" });
`),
);
expect(
result.observations.webmcp_declarations.map(
({ name, description, schema_property_names }) => ({
name,
description,
schema_property_names,
}),
),
).toEqual([
{
name: "lookup",
description: "first",
schema_property_names: ["alpha"],
},
{
name: "lookup",
description: "second",
schema_property_names: ["alpha"],
},
{
name: "equivalent",
description: null,
schema_property_names: ["alpha", "beta"],
},
{ name: null, description: "unnamed", schema_property_names: [] },
{ name: "", description: "unnamed", schema_property_names: [] },
]);
expect(result.observations.webmcp_declarations[2]?.location).toEqual({
script_key: `scr_${"1".repeat(64)}`,
line: 4,
column: 6,
});
expect(
result.observations.webmcp_declarations.every(
({ trust }) => trust === "page-declared-untrusted",
),
).toBe(true);
});
+8 -9
View File
@@ -293,19 +293,18 @@ const addWebMcpDeclaration = (
: [],
)
: [];
const metadata = {
name: name ?? null,
description: description ?? null,
schema_property_names: [...new Set(schemaPropertyNames)].sort(),
};
// Deduplicate the metadata we return, preserving omission and descriptions.
// Property names are a variable-length source-derived list: NUL-joins
// would collapse ["a\0b"] with ["a", "b"] and drop a declaration.
const key = compositeKey([
"webmcp",
script.script_key,
name ?? "",
schemaPropertyNames,
]);
const key = compositeKey(["webmcp", script.script_key, metadata]);
addUnique(accumulator, key, () =>
accumulator.webMcp.push({
name: name ?? null,
description: description ?? null,
schema_property_names: [...new Set(schemaPropertyNames)].sort(),
...metadata,
trust: "page-declared-untrusted",
location: location(script.script_key, node),
}),