navigator.modelContext) that don’t exist on the server. Remix renders components on both server and client during SSR, so you must ensure WebMCP code only runs in the browser.
The challenge
Remix components run during both SSR and client-side hydration by default. This means:@mcp-b/globalpolyfill cannot be imported at module level in routesuseWebMCPhooks will fail during SSR if not guarded- You need explicit client-only patterns
Client-only tool registration
UseClientOnly from remix-utils (recommended) or lazy imports to ensure tools only register on the client:
Using ClientOnly (recommended)
Using lazy imports
Tools that persist across navigation
For tools that should remain registered across route changes, place them inroot.tsx using the same client-only pattern:
Common errors
Hydration mismatch errors
Hydration mismatch errors
Cause: Server HTML doesn’t match client HTML because tools render differentlySolution: Ensure tool components return
null and use ClientOnly fallback={null} for consistent server/client output.