Skip to content

[codex] Add source-integrated WebMCP skill - #155

Merged
shrey150 merged 5 commits into
mainfrom
feat/add-webmcp-skill
Sep 2, 2026
Merged

[codex] Add source-integrated WebMCP skill#155
shrey150 merged 5 commits into
mainfrom
feat/add-webmcp-skill

Conversation

@shrey150

@shrey150 shrey150 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Summary

  • benchmark report: https://agentshtml-browserbase.vercel.app/webmcp/add-webmcp-benchmark/
  • add /add-webmcp, a source-first skill that inventories an existing web app and implements first-party WebMCP tools around real routes, forms, handlers, services, schemas, and state
  • validate shipped tool discovery and safe invocation through the Stagehand V4 page.tools() API, with @browserbasehq/stagehand pinned to 4.0.2
  • add an adversarial verification step that probes the live page for contract defects discovery cannot catch, then verifies the effect in the application rather than trusting the return value
  • include a bounded codebase scanner, consequential-action guardrails, validation fixtures, evaluator prompts, and a quality rubric
  • register the skill in the README and Claude plugin marketplace

Why step 6 exists

Discovery plus a passing webmcp.e2e.json proves a tool is registered and that its executor ran. It does not prove the contract is sound: a handler can accept undeclared fields, silently coerce a string into a number, return Completed with a null result when a required field is missing, or report success without changing anything. Every one of those passes step 5.

Step 6 probes schema closure, required fields, types and constraints, error honesty, annotation honesty, clean rejection, and consequential gating against a live persistent browser session, then verifies the effect independently — DOM assertions where there is readable DOM, a screenshot where the surface is a canvas or chart.

Behaviour change

--local validation now runs headed by default so the browser is visible while it validates. Pass --headless for CI and unattended runs. --browserbase is unaffected.

E2E Test Matrix

Command / flow Observed output Confidence / sufficiency
node --test tests/*.test.mjs pass 2, fail 0 Scanner and pre-browser consequential refusal still hold after the validator change. Unit-level only.
node scripts/validate-stagehand.mjs --url <eval site> --config tests/fixtures/browserbase-owned-webmcp.e2e.json --local discovered 4 WebMCP tool(s); 2 read-only invoked, failure and consequential left discovery-only; Validation passed: 4/4 Confirms the headed-by-default change did not alter validator behaviour against the existing fixture.
... --local with no flag / ... --local --headless Visible Chrome window / no window; both Validation passed Headed default and the --headless opt-out both work.
... --browserbase (no flag) / ... --browserbase --headed Reaches the intended BROWSERBASE_API_KEY is required error / --headed and --headless are only valid with --local The new default does not spuriously reject remote runs; the explicit-flag guard still fires.
Full skill run against a third-party OSS React + Vite app (Excalidraw), scanner through step 6 3 tools implemented against the app's existing imperative API; Validation passed: 3/3 End-to-end exercise of the whole workflow on a codebase not written for this skill.
Step 5 first run on that app FAIL set_active_tool: output.activeTool was "selection"; expected "ellipse" Caught a real defect: the handler read state back before the app's async store settled. Fixed, re-run green. This is the failure the new "never echo the request" rule prevents.
Step 6 adversarial probes on that app (undeclared field, missing required, wrong type, out-of-range, two bad enums, read-only with extra field, valid call) 7 rejected, 1 accepted with a real element id All eight probe families behave correctly against a compliant implementation.
Step 6 effect verification App store reported elementCount: 2; screenshot showed the labeled shape actually rendered, and it was the only shape present Proves the effect really happened, and that the 7 rejections left no partial state. Screenshot was necessary here because the surface is <canvas> with no readable DOM.

Integration work on the third-party app was local only; nothing was pushed upstream.

Benchmark-informed hardening

  • align additionalProperties: false discovery schemas with execute-time unknown-key rejection
  • scan named capability functions, network clients, and localForage boundaries
  • score capability coverage separately from selected-tool quality
  • retain limitations around invalid-input, idempotence, lifecycle, exact-shape, and Unicode interoperability testing

No benchmark repository changes were pushed upstream.


Note

Low Risk
Documentation and skill tooling only; no production app or auth paths in this repo are modified. Validator behavior change is limited to default headed local runs.

Overview
Introduces add-webmcp, a Claude skill for making existing web apps agent-ready by analyzing source (routes, forms, server actions, schemas) and wiring first-party WebMCP tools into the app—distinct from webmcp-gen, which targets standalone init scripts from a live URL.

The skill ships a seven-step workflow (boundary scan → capability inventory → contracts → integration → Stagehand verification → adversarial live probes → report), reference docs for implementation patterns and a quality rubric, plus scan-codebase.mjs (bounded repo signal finder) and validate-stagehand.mjs (Stagehand 4.0.2 page.tools() / invoke / result checks against webmcp.e2e.json, with refusal of consequential invocations unless --allow-consequential). Local validation now runs headed by default (--headless for CI).

Also adds eval prompts, unit tests (scanner + pre-browser consequential guard), e2e fixtures, MIT license, and registers the skill in the README and Claude plugin marketplace.

Reviewed by Cursor Bugbot for commit f3137f5. Bugbot is set up for automated code reviews on this repo. Configure here.

@socket-security

socket-security Bot commented Sep 1, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​next@​16.3.461100909970
Addednpm/​react@​19.0.01001008497100
Addednpm/​zod@​4.0.010010010093100
Addednpm/​@​browserbasehq/​stagehand@​4.0.2100100100100100

View full report

@socket-security

socket-security Bot commented Sep 1, 2026

Copy link
Copy Markdown

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8ac284b. Configure here.

Comment thread skills/add-webmcp/scripts/scan-codebase.mjs Outdated
Comment thread skills/add-webmcp/scripts/validate-stagehand.mjs
shrey150 and others added 4 commits September 1, 2026 22:00
Addresses feedback from Cursor Bugbot and Socket Security
Step 5 proves a tool is discoverable and that its executor ran, not that it
did what it claimed. Add step 6: probe schema closure, required fields, types
and constraints, error honesty, annotation honesty, clean rejection, and
consequential gating against the live page, then verify the effect in the
application rather than trusting the return value.

Also:
- Default --local validation to headed so the browser is visible; add
  --headless for CI and unattended runs.
- Record that document.modelContext is the live accessor while
  navigator.modelContext is undefined, so the fallback is load-bearing.
- Record that registerTool is idempotent by name with no unregister handle,
  which is what makes remount and hot reload safe.
- Require handlers to read results back from application state instead of
  echoing the request, including polling asynchronous stores until they settle.
The self-invocation guard compared path.resolve(process.argv[1]) against
fileURLToPath(import.meta.url). path.resolve normalizes . and .. but does not
resolve symlinks, so when the skill is installed as a link the two paths never
match, main() never runs, and the scanner prints nothing and exits 0.

That is the default install shape for a Claude Code user, whose skill directory
is a symlink to the real one, and step 1 points them at exactly that path.
Resolve symlinks on both sides before comparing.

Also tell the skill to report what each step found as it goes, rather than
saving everything for the final report.
Dev servers commonly bind localhost only, so validating against
http://127.0.0.1:PORT discovers zero tools while http://localhost:PORT works.
The examples recommended the address that fails, and the symptom is
indistinguishable from tools never registering.

Point the examples at localhost and say what zero discovered tools usually
means.
@shrey150
shrey150 requested a review from connor443 September 2, 2026 01:35

@connor443 connor443 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@shrey150
shrey150 merged commit 6811ca3 into main Sep 2, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants