Skip to content

feat(root)!: adopt upstream WebMCP core and simplify React hooks - #329

Merged
MiguelsPizza merged 73 commits into
mainfrom
alex/react-hooks-upstream-types
Oct 1, 2026
Merged

MiguelsPizza merged 73 commits into
mainfrom
alex/react-hooks-upstream-types

Conversation

@MiguelsPizza

@MiguelsPizza MiguelsPizza commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Move the WebMCP core to the Community Group's upstream implementation and types, then build MCP-B extensions and React hooks on that contract. This removes duplicated browser behavior and gives the major release concrete migration instructions.

This is the improvements and cleanup PR. Plugin and guarded-consent features are tracked separately in #332, which builds on this branch and will need to incorporate the final core migration.

Package ownership

Package Responsibility after this change
webmcp-types Standard browser contracts and input inference, following the upstream types package.
@mcp-b/webmcp-types Temporary, types-only forwarding alias. Exports upstream WebMCP; old top-level MCP-B exports move to the SDK.
@mcp-b/webmcp-polyfill Vendors the official polyfill at 439c6c3 until its official distribution is available; retains declarative forms until upstream has parity.
@mcp-b/webmcp-ts-sdk MCP tools, output schemas, prompts, resources, schema helpers, and the BrowserMcpServer adapter.
@mcp-b/global Installs the extended document API and default transports around the underlying polyfill/native context.
usewebmcp Core React tools with upstream JSON Schema inference, raw results, execution state, and cancellation.
@mcp-b/react-webmcp Composes the core hook with Standard Schema validation, MCP formatting, output metadata, prompts, resources, and client hooks.

Both legacy core package READMEs explain their eventual removal and recommend the upstream packages. No removal date is set. Standalone declarative-form users should keep @mcp-b/webmcp-polyfill until upstream supports forms. Follow the official polyfill installation instructions for its distribution.

Compatibility and intentional breaking changes

  • new BrowserMcpServer(info, options?) remains supported. It uses the page context and installs the polyfill when needed; { native } remains optional. SDK, global, and transport packages remain separate.
  • Browser APIs now use document.modelContext, a discovered tool descriptor, and object input. Imperative results are JSON strings; native declarative forms may return plain text. The MCP adapter and relay normalize both formats, while direct browser calls preserve the underlying result. Navigator aliases, the testing shim, legacy Chrome serialization paths, and initializeWebMCPPolyfill() are removed. Use installWebMCP().
  • Core discovery, execution, access checks, and frame routing delegate upstream. MCP mirroring is scoped to the document and descendants to prevent recursive iframe imports. Existing origin restrictions remain enforced.
  • Core React hooks accept plain JSON Schema and return raw values. Standard Schema validation and MCP response formatting belong to the extension hook. Replace schemas instead of mutating them, and inspect registration errors separately from execution errors.
  • Core cleanup uses registration signals and document lifetime. Global cleanup removes its MCP extension layer and restores the underlying context; declarative forms stay installed. Invocation signals cancel running work.
  • Service workers and Node.js use the official MCP McpServer directly.

The target follows the WebMCP Community Group draft, with the vendored revision and upstream types defining the shipped implementation.

Migration notes

Nine changesets schedule the coordinated 6.0.0 release. They include before/after imports and calls, explicit generic mappings, validation changes, test-double updates, and frame/relay setup:

Cleanup and review scope

  • Add the requested generic anti-slop Oxlint rules through Vite+, and resolve findings across package consumers, tests, and the landing page.
  • Retain benchmark highlights and runnable commands in the benchmark README; generated benchmark results remain ignored.
  • Preserve the existing branch's Astro 7.3.5 / Cloudflare 14.3.3 / MDX 8.0.2 upgrades for GHSA-26w7-cxv4-gfx2.
  • The local relay still uses its page-to-widget bridge; this PR updates its WebMCP contract and fixes tool-list races during reconnect. Replacing that bridge entirely with upstream frame messaging is separate work.

Standalone declarative forms

Keep the existing declarative implementation in @mcp-b/webmcp-polyfill as a temporary layer around the unchanged upstream core. Both ESM installation and the standalone script bundle support annotated forms and SubmitEvent.agentInvoked / respondWith(). @mcp-b/global reuses this layer; cleanup preserves it. Existing native form hooks take precedence. No MCP SDK dependency is introduced into the polyfill.

The shared form suite now runs against both the standalone polyfill and global bridge. The standalone harness runs inside the polyfill package so its source coverage is included in CI. A lifecycle regression covers repeated installation and form execution after bridge cleanup; a built-bundle check also verified duplicate IIFE loads and subsequent global cleanup.

Native form compatibility fix

The native extension CI lane caught an overly strict result decoder introduced during cleanup: native respondWith() string responses are plain text, while imperative tool results are JSON-serialized. Chromium's form response handler explicitly supports both shapes. The SDK and relay now normalize plain text into MCP content without changing direct browser execution or suppressing execution failures. Regression coverage includes empty text, JSON strings, MCP envelopes, and both response formats through the relay.

Validation

Commands run locally:

pnpm install --frozen-lockfile --ignore-scripts
pnpm build
pnpm typecheck
pnpm exec vp check
pnpm test:unit
CI=true pnpm --filter @mcp-b/webmcp-polyfill test:coverage
pnpm test:e2e
pnpm --filter mcp-e2e-tests test:native-parity
pnpm --filter @mcp-b/global test:conformance:native
CHROME_BIN="/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary" pnpm test:wpt
pnpm check:packages
pnpm build:docs
pnpm --filter @mcp-b/webmcp-local-relay test:e2e
PLAYWRIGHT_EXTENSION_CHROMIUM_EXECUTABLE_PATH=/tmp/core-chromium-snapshot/chrome-mac/Chromium.app/Contents/MacOS/Chromium PLAYWRIGHT_EXTENSION_ENABLE_WEBMCP_FLAGS=1 pnpm --filter @mcp-b/webmcp-extension test:e2e
pnpm build:landing
pnpm syncpack:lint
pnpm audit:ci:critical
pnpm release:check
pnpm exec changeset status

pnpm test:unit includes isolated packed-package declarations and React 18/19 SSR checks. All 12 release-note code examples and the core/extension generic migration also passed strict TypeScript compilation. The Changesets plan confirms all 12 published packages advance from 5.1.0 to 6.0.0 without duplicate package summaries.

The informational pnpm audit:ci:high report still lists four high-severity advisories in landing-page transitive dependencies (browserslist, sharp, and smol-toml). The required critical audit reports zero critical advisories.

Checklist

  • Public API changes include package-specific migration changesets.
  • Browser contracts and MCP-B extension ownership are reflected in types, implementation, and documentation.
  • Registration, execution, cancellation, frame/extension integration, and package artifacts have regression coverage.
  • Build, typecheck, lint, unit, browser, and release checks are recorded above.

Use the Community Group's webmcp-types package as the browser contract so
the core hook can ship without MCP-B runtime or MCP SDK dependencies.
Derive the MCP-B compatibility types from upstream instead of declaring
Document.modelContext twice.

Keep Standard Schema validation in the shared callback, using the
validator supplied by the application. This preserves async validation,
defaults, and transforms across local, native, and polyfill calls.
Move MCP output schemas and response formatting into the React adapter.

Add native and packed React 18/19 checks, preserve the enabled-hook and
render-budget work already on main, and document the migration with runnable
examples and explicit conversion/validation boundaries.

BREAKING CHANGE: usewebmcp returns raw agent results and uses TResult
generics. Import useWebMCP from @mcp-b/react-webmcp to retain outputSchema,
MCP annotations, and automatic MCP response formatting.

Refs: #317
@MiguelsPizza
MiguelsPizza requested a review from a team as a code owner September 5, 2026 16:35
@github-actions github-actions Bot added documentation Improvements or additions to documentation packages/react-webmcp Changes to @mcp-b/react-webmcp packages/webmcp-ts-sdk Changes to @mcp-b/webmcp-ts-sdk packages/usewebmcp Changes to usewebmcp testing Test-related changes ci CI/CD and GitHub Actions changes config Configuration file changes dependencies Dependency updates packages/webmcp-polyfill packages/webmcp-types labels Sep 5, 2026
@codecov

codecov Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Exercise circular schema metadata through the public hook to ensure bad
descriptors report registration errors and recover after correction.

Cover returned Errors and non-Error rejections through both local and
browser-triggered execution so failures never count as successful results.
These regressions close the core hook's uncovered statement and line paths.
React does not run effects during server rendering, so DOM-presence checks
inside the registration effect only add unreachable paths. Keep browser
discovery in that effect and retain the isomorphic layout effect for SSR.

Exercise browsers without navigator.modelContext by removing the legacy
alias during the unsupported-browser test and restoring it afterward.
Returning undefined from its getter did not cover an absent property.

Packed React 18/19 server-rendering checks still verify imports and renders
without DOM globals.

Refs: https://react.dev/reference/react/useEffect#caveats
Compare four hooks against the same native WebMCP registry with five trials
per StrictMode setting. Record every scenario so the pending-call chart can
be checked against registration and completion behavior.

Render light and dark figures with the shared design-system D3 components.
Keep benchmark dependencies isolated from the published packages.
Keep examples first, then show the measured React commit counts and package
boundaries. Link the full scenarios and feature comparison so the figures
can be evaluated in context.

Use light and dark screenshots with alt text, local paths in the docs,
and permanent asset URLs for README rendering on GitHub and npm.
Show all four hooks in one compact bar table. Google's row explains that
it tracks registration rather than running calls, so it has no comparable
execution-state count.

Remove the duplicate table and long captions. Reduce the package diagram
to two choices while retaining D3, shared tokens, and both color themes.
Keep the working examples and package choices easy to scan. Link detailed
state, schema, and lifecycle behavior to the existing references instead
of repeating it throughout the introductions.

Point README images at the compact figures, including Google's labeled row.
Surface registration calls and metadata-related React commits alongside
execution updates. Google has comparable registration measurements, so
excluding them made the previous figure an incomplete comparison.

Include metadata registration counts in the generated results table and
keep raw samples, metric groups, and shared bar scales explicit.
Replace the execution-only figure and alt text with the shared registration
and execution measurements. Readers can compare Google directly without
having to find the registration results in a separate methodology page.
Show one metric, component re-renders, for changing a tool and running calls.
Sum call starts and completions so readers can compare the whole operation.
Keep Google visible and name its missing execution state directly.

Replace mixed registration and render columns with two shared-scale bar charts.
Keep registration counts and methodology in the benchmark documentation.
Give readers a short comparison of all four hooks after the code example.
Keep registration counts in one sentence and compare six concrete features.

Remove the extra package diagram and replace adapter overview illustrations
with direct links to the performance and feature comparisons.
Reuse the successful registration state so React can bail out when pending
and success updates batch together. Previously, a fresh success object caused
another consumer commit even though the registration status ended unchanged.

Keep pending and error updates observable. Add StrictMode render budgets and
deferred registration success, rejection, and recovery regressions. The old
allocation fails the new budget with 20 commits; the fix needs only 10.
Rerun all forty comparison samples from the committed optimization. Both
hooks now match MCP Cat at 10 commits for 10 metadata edits, while Google
remains at 20. Execution and registration counts are unchanged.

Regenerate both chart themes and record the updated measurement provenance.
Link both chart themes and benchmark details to the new measured results.
Update accessible descriptions to reflect 10 metadata commits for both hooks.
Development test batching can hide registration and execution state commits. Add a native
browser production lane for sequential calls, schema size, and tool count, and use it
for the README chart. Keep raw timings and lifecycle counts to expose feature tradeoffs.

Measure each production ESM hook bundle with React external and built-in dependencies
included. Prebundle the remaining React test entries so Vite reloads cannot invalidate
the existing render-budget checks.
Use measured production behavior for the README chart and preserve the development fixture.
Document registration-status tradeoffs and pin samples to the tested harness source.
Pin the new chart and measurements so npm readers see the same comparison as the docs.
Successful metadata replacement previously rendered for pending and completed registration.
Remove isRegistered and keep setup errors observable without those success-path updates.
Cache schema conversion and its serialized key together so stable schemas avoid repeat work.

Use one completion update for result, errors, and pending execution counts. Core agent failures
now reject through the browser contract; the MCP adapter supplies its error response formatter.
Keep asynchronous formatting inside the cancellation race and retain each call's committed config.

Regression tests cover delayed registration, schema reuse, cancellation during formatting,
concurrent completions, native Chrome failures, and packed React 18/19 client boundaries.
The production harness now requires one parent commit and registration per changed tool.

Validated the workspace build, typecheck, lint, unit suites, hook coverage, and native hook tests.
… package

The shared form suite exercised the standalone installer through global's
build, so the polyfill coverage report missed the moved implementation.
Run that harness in the polyfill package and import its source entry directly.

Derive the shared suite's browser types from the polyfill contract to avoid a
reverse dependency on the MCP adapter. Keep the global harness for bridge
coverage and update the documented conformance commands.

Verified build, typecheck, lint, and unit tests. All 20 standalone tests pass;
the retained declarative implementation has 88.75% line coverage.
@MiguelsPizza
MiguelsPizza merged commit 160acc3 into main Oct 1, 2026
14 checks passed
@MiguelsPizza
MiguelsPizza deleted the alex/react-hooks-upstream-types branch October 1, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci CI/CD and GitHub Actions changes config Configuration file changes dependencies Dependency updates documentation Improvements or additions to documentation packages/global Changes to @mcp-b/global packages/mcp-iframe Changes to @mcp-b/mcp-iframe packages/react-webmcp Changes to @mcp-b/react-webmcp packages/smart-dom-reader Changes to @mcp-b/smart-dom-reader packages/transports Changes to @mcp-b/transports packages/usewebmcp Changes to usewebmcp packages/webmcp-extension packages/webmcp-local-relay packages/webmcp-polyfill packages/webmcp-ts-sdk Changes to @mcp-b/webmcp-ts-sdk packages/webmcp-types testing Test-related changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant