// The single point where the toolkit reads the standard WebMCP surface. // // Everything else in the package goes through `getModelContext()` and reads only // the standard `document.modelContext` surface. The polyfill that installs that // surface is vendored in `./webmcp-polyfill` (forked from @mcp-b/webmcp-polyfill // 3.0.0) and wired up in `index.ts` (the init call) — swapping it touches only // those two spots. import type { ModelContext, ModelContextWithResources, ToolDescriptor } from './types.js'; /** True only inside a browser-like environment (not SSR / workers / edge). */ export function isBrowserEnvironment(): boolean { return typeof window !== 'undefined' && typeof document !== 'undefined'; } /** * Return the standard `document.modelContext`, falling back to the deprecated * `navigator.modelContext` alias. `document` is the canonical surface as of the * May 2026 spec change; the navigator getter is retained only for back-compat. * Returns `undefined` outside a browser or before the polyfill has installed. */ export function getModelContext(): ModelContext | undefined { if (!isBrowserEnvironment()) return undefined; const doc = document as Document & { modelContext?: ModelContext }; // `navigator` is the deprecated fallback location and may be absent (some // runtimes have window+document but no navigator), so guard the bare // reference instead of letting it throw a ReferenceError. const nav = typeof navigator !== 'undefined' ? (navigator as Navigator & { modelContext?: ModelContext }) : undefined; return doc.modelContext ?? nav?.modelContext; } /** Same as {@link getModelContext}, typed to include the toolkit's resource extension. */ export function getModelContextWithResources(): ModelContextWithResources | undefined { return getModelContext() as ModelContextWithResources | undefined; } /** * SSR-safe convenience over `document.modelContext.registerTool(...)`, the * function-form counterpart of {@link registerResource}. Outside a browser (or * before the surface exists) it is a silent no-op, so tool modules can call it * unconditionally. In a browser it delegates to the standard method — the * descriptor is plain WebMCP, nothing Napster-specific. */ export function registerTool( tool: ToolDescriptor, options?: { signal?: AbortSignal } ): void | Promise { const mc = getModelContext(); if (!mc || typeof mc.registerTool !== 'function') return; return mc.registerTool(tool, options); }