Draft conformance
Current global, names, schema closure, and runtime validation seams.
Tool Contract Workbench
Paste imperative WebMCP code or declarative markup. The audit checks current draft syntax, Chrome guidance, privacy risks, runtime validation seams, and lifecycle cleanup—without sending code to a model.
Input
Remove secrets and customer data. Tool contracts should never contain credentials.
Current global, names, schema closure, and runtime validation seams.
Annotations, metadata budgets, data minimization, and verifiability.
AbortSignal-based cleanup for route and component ownership.
Audit ledger
Uses the current document.modelContext surface.
No navigator.modelContext reference was found.
An imperative tool registration is source-visible.
1 tool name detected. The draft permits 1–128 ASCII letters, numbers, underscore, hyphen, and period; Chrome recommends about 30 characters.
At least one schema rejects undeclared properties.
A runtime validation seam is source-visible.
Registration uses an AbortSignal so route or component teardown can unregister the tool.
A read-only behavior hint is source-visible. Confirm it matches the actual effect.
Tool output is marked as untrusted.
Detected descriptions fit the current non-normative Chrome character budgets.
No common demographic or profiling fields were detected by this static check.
A structured result or verification field is source-visible.
Quick fix
const registration = new AbortController();
if (typeof document.modelContext?.registerTool === "function") {
await document.modelContext.registerTool({
name: "search_catalog",
title: "Search catalog",
description: "Find public catalog items that match explicit filters.",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
maxLength: 120,
description: "Product words to search for."
},
max_price: {
type: "number",
minimum: 0,
description: "Maximum price in USD."
}
},
additionalProperties: false
},
annotations: {
readOnlyHint: true,
untrustedContentHint: true
},
async execute(input, { signal }) {
const filters = validateSearchInput(input);
const items = await catalog.search(filters, { signal });
return { items, count: items.length };
}
}, { signal: registration.signal });
}
// Keep this callback and invoke it only on route change or component unmount.
const unregisterPageTools = () => registration.abort();