@mcp-b/react-webmcp 5.0.0 → 5.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{useWebMCP as e,useWebMCP as t}from"usewebmcp";import{createContext as n,useCallback as r,useContext as i,useEffect as a,useLayoutEffect as o,useMemo as s,useRef as c,useState as l}from"react";import{jsx as u}from"react/jsx-runtime";import{normalizeInputSchema as d}from"@mcp-b/webmcp-polyfill/schema";import{isBrowserMcpServer as f}from"@mcp-b/webmcp-ts-sdk";const p=typeof window>`u`?a:o;function m(e){let t=c(e);return p(()=>{t.current=e},[e]),t}const h=n(null);function g(e,t,n){let r=new AbortController;return e.listen(t,{signal:r.signal}).then(async e=>{if(r.signal.aborted){await e.close();return}await n()}).catch(e=>{r.signal.aborted||console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to listen for list_changed notifications:`,e)}),()=>r.abort()}function _({children:e,client:t,transport:n,opts:i}){let[o,d]=l([]),[f,p]=l([]),[_,v]=l(null),[y,b]=l(`disconnected`),[x,S]=l(null),C=y===`initializing`||y===`connected`,w=y===`connecting`||y===`initializing`,T=c(`disconnected`),E=c(0),D=c(null),O=c(null),k=c(0),A=m(i),j=r(async e=>{let n=++k.current,r=t.getServerCapabilities();try{let[i,a]=await Promise.all([r?.resources?t.listResources(void 0,{cacheMode:`refresh`}):void 0,r?.tools?t.listTools(void 0,{cacheMode:`refresh`}):void 0]);if(e!==E.current||n!==k.current)return;d(i?.resources??[]),p(a?.tools??[]),v(null)}catch(t){let r=t instanceof Error?t:Error(String(t));if(e!==E.current||n!==k.current)return;throw v(r),r}},[t]),M=r(async e=>{if(T.current===`connected`&&e===void 0){let e=E.current;T.current=`initializing`,b(`initializing`);try{await j(e)}catch{}e===E.current&&(T.current=`connected`,b(`connected`));return}if(T.current!==`disconnected`)return;T.current=`connecting`,b(`connecting`),v(null);let r=E.current;try{if(await D.current,r!==E.current||(await t.connect(e??n,A.current),r!==E.current))return}catch(e){let t=e instanceof Error?e:Error(String(e));throw r===E.current&&(T.current=`disconnected`,b(`disconnected`),v(t)),t}S(t.getServerCapabilities()??null),T.current=`initializing`,b(`initializing`);try{await j(r)}catch{}r===E.current&&(T.current=`connected`,b(`connected`))},[t,n,j,A]);a(()=>{if(!C)return;let e=t.getServerCapabilities(),n=e?.resources?.listChanged===!0,r=e?.tools?.listChanged===!0,i=async()=>{try{await j(E.current)}catch(e){console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to refresh tools/resources after list_changed:`,e)}};n&&t.setNotificationHandler(`notifications/resources/list_changed`,i),r&&t.setNotificationHandler(`notifications/tools/list_changed`,i);let a=n||r,o=t.getProtocolEra()===`modern`&&a?g(t,{...r&&{toolsListChanged:!0},...n&&{resourcesListChanged:!0}},i):void 0;return!o&&a&&i(),()=>{n&&t.removeNotificationHandler(`notifications/resources/list_changed`),r&&t.removeNotificationHandler(`notifications/tools/list_changed`),o?.()}},[t,C,j]),a(()=>{let e=!0,n=E.current,r=t.onclose,i=()=>{try{r?.()}finally{e&&O.current===null&&(E.current+=1,k.current+=1,T.current=`disconnected`,b(`disconnected`),S(null),d([]),p([]))}};return t.onclose=i,T.current=`disconnected`,b(`disconnected`),M().catch(e=>{n===E.current&&console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to connect MCP client:`,e)}),()=>{e=!1,t.onclose===i&&(r?t.onclose=r:Reflect.deleteProperty(t,`onclose`)),E.current+=1,k.current+=1,T.current=`disconnected`;let n={};O.current=n,D.current=t.close().catch(e=>{console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to close MCP client:`,e)}).finally(()=>{O.current===n&&(O.current=null)})}},[t,n,M]);let N=s(()=>({client:t,tools:f,resources:o,isConnected:C,isLoading:w,error:_,capabilities:x,reconnect:M}),[t,f,o,C,w,_,x,M]);return u(h.Provider,{value:N,children:e})}function v(){let e=i(h);if(!e)throw Error(`useMcpClient must be used within an McpClientProvider`);return e}function y(e,n,r){return t({name:e,description:n,annotations:{title:`Context: ${e}`,readOnlyHint:!0,idempotentHint:!0,destructiveHint:!1,openWorldHint:!1},execute:r})}function b(){if(typeof document>`u`||typeof navigator>`u`)return;let e=document.modelContext??navigator.modelContext;return e&&f(e)?e:void 0}function x(e){let[t,n]=l(!1);return a(()=>{let t=e();if(!t){n(!1);return}return n(!0),()=>{t.unregister()}},[e]),t}function S(e){let{name:t,description:n}=e,i=m(e);return{isRegistered:x(r(()=>{let e=b();if(!e){console.warn(`[ReactWebMCP] BrowserMcpServer is not available. Prompt "${t}" will not be registered.`);return}let{argsSchema:r}=i.current,a=r?d(r).inputSchema:void 0;return e.registerPrompt({name:t,...n!==void 0&&{description:n},...a&&{argsSchema:a},get:async e=>i.current.get(e)})},[t,n,i]))}}function C(e){let{uri:t,name:n,description:i,mimeType:a,read:o}=e,s=m(o);return{isRegistered:x(r(()=>{let e=b();if(!e){console.warn(`[ReactWebMCP] BrowserMcpServer is not available. Resource "${t}" will not be registered.`);return}return e.registerResource({uri:t,name:n,...i!==void 0&&{description:i},...a!==void 0&&{mimeType:a},read:(e,t)=>s.current(e,t)})},[t,n,i,a,s]))}}export{_ as McpClientProvider,v as useMcpClient,e as useWebMCP,y as useWebMCPContext,S as useWebMCPPrompt,C as useWebMCPResource};
|
|
1
|
+
"use client";import{useWebMCP as e,useWebMCP as t}from"usewebmcp";import{createContext as n,useCallback as r,useContext as i,useEffect as a,useLayoutEffect as o,useMemo as s,useRef as c,useState as l}from"react";import{jsx as u}from"react/jsx-runtime";import{normalizeInputSchema as d}from"@mcp-b/webmcp-polyfill/schema";import{isBrowserMcpServer as f}from"@mcp-b/webmcp-ts-sdk";const p=typeof window>`u`?a:o;function m(e){let t=c(e);return p(()=>{t.current=e},[e]),t}const h=n(null);function g(e,t,n){let r=new AbortController;return e.listen(t,{signal:r.signal}).then(async e=>{if(r.signal.aborted){await e.close();return}await n()}).catch(e=>{r.signal.aborted||console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to listen for list_changed notifications:`,e)}),()=>r.abort()}function _({children:e,client:t,transport:n,opts:i}){let[o,d]=l([]),[f,p]=l([]),[_,v]=l(null),[y,b]=l(`disconnected`),[x,S]=l(null),C=y===`initializing`||y===`connected`,w=y===`connecting`||y===`initializing`,T=c(`disconnected`),E=c(0),D=c(null),O=c(null),k=c(0),A=m(i),j=r(async e=>{let n=++k.current,r=t.getServerCapabilities();try{let[i,a]=await Promise.all([r?.resources?t.listResources(void 0,{cacheMode:`refresh`}):void 0,r?.tools?t.listTools(void 0,{cacheMode:`refresh`}):void 0]);if(e!==E.current||n!==k.current)return;d(i?.resources??[]),p(a?.tools??[]),v(null)}catch(t){let r=t instanceof Error?t:Error(String(t));if(e!==E.current||n!==k.current)return;throw v(r),r}},[t]),M=r(async e=>{if(T.current===`connected`&&e===void 0){let e=E.current;T.current=`initializing`,b(`initializing`);try{await j(e)}catch{}e===E.current&&(T.current=`connected`,b(`connected`));return}if(T.current!==`disconnected`)return;T.current=`connecting`,b(`connecting`),v(null);let r=E.current;try{if(await D.current,r!==E.current||(await t.connect(e??n,A.current),r!==E.current))return}catch(e){let t=e instanceof Error?e:Error(String(e));throw r===E.current&&(T.current=`disconnected`,b(`disconnected`),v(t)),t}S(t.getServerCapabilities()??null),T.current=`initializing`,b(`initializing`);try{await j(r)}catch{}r===E.current&&(T.current=`connected`,b(`connected`))},[t,n,j,A]);a(()=>{if(!C)return;let e=t.getServerCapabilities(),n=e?.resources?.listChanged===!0,r=e?.tools?.listChanged===!0,i=async()=>{try{await j(E.current)}catch(e){console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to refresh tools/resources after list_changed:`,e)}};n&&t.setNotificationHandler(`notifications/resources/list_changed`,i),r&&t.setNotificationHandler(`notifications/tools/list_changed`,i);let a=n||r,o=t.getProtocolEra()===`modern`&&a?g(t,{...r&&{toolsListChanged:!0},...n&&{resourcesListChanged:!0}},i):void 0;return!o&&a&&i(),()=>{n&&t.removeNotificationHandler(`notifications/resources/list_changed`),r&&t.removeNotificationHandler(`notifications/tools/list_changed`),o?.()}},[t,C,j]),a(()=>{let e=!0,n=E.current,r=t.onclose,i=()=>{try{r?.()}finally{e&&O.current===null&&(E.current+=1,k.current+=1,T.current=`disconnected`,b(`disconnected`),S(null),d([]),p([]))}};return t.onclose=i,T.current=`disconnected`,b(`disconnected`),S(null),d([]),p([]),M().catch(e=>{n===E.current&&console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to connect MCP client:`,e)}),()=>{e=!1,t.onclose===i&&(r?t.onclose=r:Reflect.deleteProperty(t,`onclose`)),E.current+=1,k.current+=1,T.current=`disconnected`;let n={};O.current=n,D.current=t.close().catch(e=>{console.error(`[ReactWebMCP:McpClientProvider]`,`Failed to close MCP client:`,e)}).finally(()=>{O.current===n&&(O.current=null)})}},[t,n,M]);let N=s(()=>({client:t,tools:f,resources:o,isConnected:C,isLoading:w,error:_,capabilities:x,reconnect:M}),[t,f,o,C,w,_,x,M]);return u(h.Provider,{value:N,children:e})}function v(){let e=i(h);if(!e)throw Error(`useMcpClient must be used within an McpClientProvider`);return e}function y(e,n,r){return t({name:e,description:n,annotations:{title:`Context: ${e}`,readOnlyHint:!0,idempotentHint:!0,destructiveHint:!1,openWorldHint:!1},execute:r})}function b(){if(typeof document>`u`||typeof navigator>`u`)return;let e=document.modelContext??navigator.modelContext;return e&&f(e)?e:void 0}function x(e){let[t,n]=l(!1);return a(()=>{let t=e();if(!t){n(!1);return}return n(!0),()=>{t.unregister()}},[e]),t}function S(e){let{name:t,description:n}=e,i=m(e);return{isRegistered:x(r(()=>{let e=b();if(!e){console.warn(`[ReactWebMCP] BrowserMcpServer is not available. Prompt "${t}" will not be registered.`);return}let{argsSchema:r}=i.current,a=r?d(r).inputSchema:void 0;return e.registerPrompt({name:t,...n!==void 0&&{description:n},...a&&{argsSchema:a},get:async e=>i.current.get(e)})},[t,n,i]))}}function C(e){let{uri:t,name:n,description:i,mimeType:a,read:o}=e,s=m(o);return{isRegistered:x(r(()=>{let e=b();if(!e){console.warn(`[ReactWebMCP] BrowserMcpServer is not available. Resource "${t}" will not be registered.`);return}return e.registerResource({uri:t,name:n,...i!==void 0&&{description:i},...a!==void 0&&{mimeType:a},read:(e,t)=>s.current(e,t)})},[t,n,i,a,s]))}}export{_ as McpClientProvider,v as useMcpClient,e as useWebMCP,y as useWebMCPContext,S as useWebMCPPrompt,C as useWebMCPResource};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["useWebMCP"],"sources":["../src/useCommittedRef.ts","../src/client/McpClientProvider.tsx","../src/useWebMCPContext.ts","../src/model-context.ts","../src/useMcpRegistration.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef } from 'react';\n\nconst useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\nexport function useCommittedRef<T>(value: T) {\n const ref = useRef(value);\n\n // External MCP work may read values after commit but before passive effects. SSR cannot.\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n }, [value]);\n\n return ref;\n}\n","import {\n type Client,\n type ConnectOptions,\n type Resource,\n type ServerCapabilities,\n type SubscriptionFilter,\n type Tool as McpTool,\n type Transport,\n} from '@modelcontextprotocol/client';\nimport {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useCommittedRef } from '../useCommittedRef.js';\n\n/**\n * Context value provided by McpClientProvider.\n *\n * @internal\n */\ninterface McpClientContextValue {\n client: Client;\n tools: McpTool[];\n resources: Resource[];\n isConnected: boolean;\n isLoading: boolean;\n error: Error | null;\n capabilities: ServerCapabilities | null;\n reconnect: (freshTransport?: Transport) => Promise<void>;\n}\n\ntype ConnectionState = 'disconnected' | 'connecting' | 'initializing' | 'connected';\n\nconst McpClientContext = createContext<McpClientContextValue | null>(null);\n\nfunction startListChangedSubscription(\n client: Client,\n filter: SubscriptionFilter,\n refreshLists: () => Promise<void>\n): () => void {\n const controller = new AbortController();\n\n void client\n .listen(filter, { signal: controller.signal })\n .then(async (subscription) => {\n if (controller.signal.aborted) {\n await subscription.close();\n return;\n }\n\n // Modern servers deliver list_changed only after listen is acknowledged.\n await refreshLists();\n })\n .catch((error) => {\n if (!controller.signal.aborted) {\n console.error(\n '[ReactWebMCP:McpClientProvider]',\n 'Failed to listen for list_changed notifications:',\n error\n );\n }\n });\n\n return () => controller.abort();\n}\n\n/**\n * Props for the McpClientProvider component.\n *\n * @public\n */\nexport interface McpClientProviderProps {\n /**\n * React children to render within the provider.\n */\n children: ReactNode;\n\n /**\n * MCP Client instance to use for communication.\n */\n client: Client;\n\n /**\n * Transport instance for the client to connect through.\n */\n transport: Transport;\n\n /**\n * Optional connection options.\n */\n opts?: ConnectOptions;\n}\n\n/**\n * Provider component that manages an MCP client connection and exposes\n * tools, resources, and connection state to child components.\n *\n * This provider handles:\n * - Establishing and maintaining the MCP client connection\n * - Fetching available tools and resources from the server\n * - Listening for server notifications about tool/resource changes\n * - Managing connection state and errors\n * - Automatic cleanup on unmount\n *\n * @param props - Component props\n * @returns Provider component wrapping children\n *\n * @public\n *\n * @example\n * Connect to an MCP server via tab transport:\n * ```tsx\n * import { TabClientTransport } from '@mcp-b/transports';\n * import { McpClientProvider } from '@mcp-b/react-webmcp';\n * import { Client } from '@modelcontextprotocol/client';\n *\n * const client = new Client(\n * { name: 'my-app', version: '1.0.0' },\n * { versionNegotiation: { mode: 'auto' } }\n * );\n *\n * const transport = new TabClientTransport({\n * channelId: 'mcp',\n * targetOrigin: window.location.origin,\n * });\n *\n * function App() {\n * return (\n * <McpClientProvider client={client} transport={transport}>\n * <MyAppContent />\n * </McpClientProvider>\n * );\n * }\n * ```\n *\n * @example\n * Access tools from child components:\n * ```tsx\n * function MyAppContent() {\n * const { tools, isConnected, isLoading } = useMcpClient();\n *\n * if (isLoading) {\n * return <div>Connecting to MCP server...</div>;\n * }\n *\n * if (!isConnected) {\n * return <div>Failed to connect to MCP server</div>;\n * }\n *\n * return (\n * <div>\n * <h2>Available Tools:</h2>\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n */\nexport function McpClientProvider({\n children,\n client,\n transport,\n opts,\n}: McpClientProviderProps): ReactElement {\n const [resources, setResources] = useState<Resource[]>([]);\n const [tools, setTools] = useState<McpTool[]>([]);\n const [error, setError] = useState<Error | null>(null);\n const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected');\n const [capabilities, setCapabilities] = useState<ServerCapabilities | null>(null);\n const isConnected = connectionState === 'initializing' || connectionState === 'connected';\n const isLoading = connectionState === 'connecting' || connectionState === 'initializing';\n const connectionStateRef = useRef<ConnectionState>('disconnected');\n const connectionGenerationRef = useRef(0);\n const closePromiseRef = useRef<Promise<void> | null>(null);\n const providerCloseRef = useRef<object | null>(null);\n const inventoryRequestRef = useRef(0);\n const requestOptsRef = useCommittedRef(opts);\n\n /**\n * Refreshes every provider-owned inventory list and clears a prior inventory\n * error only after the complete refresh succeeds.\n */\n const refreshInventory = useCallback(\n async (connectionGeneration: number): Promise<void> => {\n const inventoryRequest = ++inventoryRequestRef.current;\n const serverCapabilities = client.getServerCapabilities();\n\n try {\n const [resourceResponse, toolResponse] = await Promise.all([\n serverCapabilities?.resources\n ? client.listResources(undefined, { cacheMode: 'refresh' })\n : undefined,\n serverCapabilities?.tools\n ? client.listTools(undefined, { cacheMode: 'refresh' })\n : undefined,\n ]);\n\n if (\n connectionGeneration !== connectionGenerationRef.current ||\n inventoryRequest !== inventoryRequestRef.current\n ) {\n return;\n }\n\n setResources(resourceResponse?.resources ?? []);\n setTools(toolResponse?.tools ?? []);\n setError(null);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n if (\n connectionGeneration !== connectionGenerationRef.current ||\n inventoryRequest !== inventoryRequestRef.current\n ) {\n return;\n }\n setError(error);\n throw error;\n }\n },\n [client]\n );\n\n /**\n * Connects a disconnected client, or retries inventory discovery when the\n * MCP handshake is already alive. Calls made while work is in progress no-op.\n *\n * Pass a fresh transport after a one-shot transport has closed.\n */\n const reconnect = useCallback(\n async (freshTransport?: Transport): Promise<void> => {\n if (connectionStateRef.current === 'connected' && freshTransport === undefined) {\n const connectionGeneration = connectionGenerationRef.current;\n connectionStateRef.current = 'initializing';\n setConnectionState('initializing');\n\n try {\n await refreshInventory(connectionGeneration);\n } catch {\n // Inventory failure does not undo the completed MCP handshake.\n }\n\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'connected';\n setConnectionState('connected');\n }\n return;\n }\n\n if (connectionStateRef.current !== 'disconnected') {\n return;\n }\n\n connectionStateRef.current = 'connecting';\n setConnectionState('connecting');\n setError(null);\n const connectionGeneration = connectionGenerationRef.current;\n\n try {\n await closePromiseRef.current;\n if (connectionGeneration !== connectionGenerationRef.current) {\n return;\n }\n await client.connect(freshTransport ?? transport, requestOptsRef.current);\n if (connectionGeneration !== connectionGenerationRef.current) {\n return;\n }\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setError(err);\n }\n throw err;\n }\n\n const caps = client.getServerCapabilities();\n setCapabilities(caps ?? null);\n connectionStateRef.current = 'initializing';\n setConnectionState('initializing');\n\n try {\n await refreshInventory(connectionGeneration);\n } catch {\n // Inventory failure does not undo the completed MCP handshake.\n }\n\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'connected';\n setConnectionState('connected');\n }\n },\n [client, transport, refreshInventory, requestOptsRef]\n );\n\n useEffect(() => {\n if (!isConnected) {\n return;\n }\n\n const serverCapabilities = client.getServerCapabilities();\n\n const resourcesListChanged = serverCapabilities?.resources?.listChanged === true;\n const toolsListChanged = serverCapabilities?.tools?.listChanged === true;\n const refreshLists = async () => {\n try {\n await refreshInventory(connectionGenerationRef.current);\n } catch (error) {\n console.error(\n '[ReactWebMCP:McpClientProvider]',\n 'Failed to refresh tools/resources after list_changed:',\n error\n );\n }\n };\n\n if (resourcesListChanged) {\n client.setNotificationHandler('notifications/resources/list_changed', refreshLists);\n }\n\n if (toolsListChanged) {\n client.setNotificationHandler('notifications/tools/list_changed', refreshLists);\n }\n\n const hasListChanged = resourcesListChanged || toolsListChanged;\n const stopListening =\n client.getProtocolEra() === 'modern' && hasListChanged\n ? startListChangedSubscription(\n client,\n {\n ...(toolsListChanged && { toolsListChanged: true }),\n ...(resourcesListChanged && { resourcesListChanged: true }),\n },\n refreshLists\n )\n : undefined;\n if (!stopListening && hasListChanged) {\n // Legacy servers deliver list_changed unsolicited once handlers are installed.\n void refreshLists();\n }\n\n return () => {\n if (resourcesListChanged) {\n client.removeNotificationHandler('notifications/resources/list_changed');\n }\n\n if (toolsListChanged) {\n client.removeNotificationHandler('notifications/tools/list_changed');\n }\n\n stopListening?.();\n };\n }, [client, isConnected, refreshInventory]);\n\n useEffect(() => {\n let active = true;\n const connectionGeneration = connectionGenerationRef.current;\n const previousOnclose = client.onclose;\n const handleClientClose = () => {\n try {\n previousOnclose?.();\n } finally {\n if (active && providerCloseRef.current === null) {\n connectionGenerationRef.current += 1;\n inventoryRequestRef.current += 1;\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setCapabilities(null);\n setResources([]);\n setTools([]);\n }\n }\n };\n client.onclose = handleClientClose;\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n\n // Initial connection - reconnect() has its own guard to prevent concurrent connections\n reconnect().catch((err) => {\n if (connectionGeneration === connectionGenerationRef.current) {\n console.error('[ReactWebMCP:McpClientProvider]', 'Failed to connect MCP client:', err);\n }\n });\n\n return () => {\n active = false;\n if (client.onclose === handleClientClose) {\n if (previousOnclose) {\n client.onclose = previousOnclose;\n } else {\n Reflect.deleteProperty(client, 'onclose');\n }\n }\n connectionGenerationRef.current += 1;\n inventoryRequestRef.current += 1;\n connectionStateRef.current = 'disconnected';\n const closeToken = {};\n providerCloseRef.current = closeToken;\n closePromiseRef.current = client\n .close()\n .catch((error: unknown) => {\n console.error('[ReactWebMCP:McpClientProvider]', 'Failed to close MCP client:', error);\n })\n .finally(() => {\n if (providerCloseRef.current === closeToken) {\n providerCloseRef.current = null;\n }\n });\n };\n }, [client, transport, reconnect]);\n\n const contextValue = useMemo(\n () => ({\n client,\n tools,\n resources,\n isConnected,\n isLoading,\n error,\n capabilities,\n reconnect,\n }),\n [client, tools, resources, isConnected, isLoading, error, capabilities, reconnect]\n );\n\n return <McpClientContext.Provider value={contextValue}>{children}</McpClientContext.Provider>;\n}\n\n/**\n * Hook to access the MCP client context.\n * Must be used within an {@link McpClientProvider}.\n *\n * @returns The MCP client context including client instance, tools, resources, and connection state\n * @throws Error if used outside of McpClientProvider\n *\n * @public\n *\n * @example\n * ```tsx\n * function ToolsList() {\n * const { tools, isConnected, error, reconnect } = useMcpClient();\n *\n * if (error) {\n * return (\n * <div>\n * Error: {error.message}\n * <button onClick={() => void reconnect()}>Retry</button>\n * </div>\n * );\n * }\n *\n * if (!isConnected) {\n * return <div>Not connected</div>;\n * }\n *\n * return (\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useMcpClient() {\n const context = useContext(McpClientContext);\n if (!context) {\n throw new Error('useMcpClient must be used within an McpClientProvider');\n }\n return context;\n}\n","import { useWebMCP, type WebMCPReturn } from 'usewebmcp';\n\n/**\n * Convenience hook for exposing read-only context data to AI assistants.\n *\n * This is a simplified wrapper around {@link useWebMCP} specifically designed for\n * context tools that expose data without performing actions. The hook automatically\n * configures appropriate annotations (read-only, idempotent) and handles value\n * serialization.\n *\n * JSON-compatible values are normalized to text plus `structuredContent`.\n *\n * @template T - The type of context data to expose\n *\n * @param name - Unique identifier for the context tool (e.g., 'context_current_post')\n * @param description - Human-readable description of the context for AI assistants\n * @param getValue - Function that returns the current context value\n * @returns Tool execution state and control methods\n *\n * @public\n *\n * @example\n * Expose current post context:\n * ```tsx\n * function PostDetailPage() {\n * const { postId } = useParams();\n * const { data: post } = useQuery(['post', postId], () => fetchPost(postId));\n *\n * useWebMCPContext(\n * 'context_current_post',\n * 'Get the currently viewed post ID and metadata',\n * () => ({\n * postId,\n * title: post?.title,\n * author: post?.author,\n * tags: post?.tags,\n * createdAt: post?.createdAt,\n * })\n * );\n *\n * return <PostContent post={post} />;\n * }\n * ```\n *\n * @example\n * Expose user session context:\n * ```tsx\n * function AppRoot() {\n * const { user, isAuthenticated } = useAuth();\n *\n * useWebMCPContext(\n * 'context_user_session',\n * 'Get the current user session information',\n * () => ({\n * isAuthenticated,\n * userId: user?.id,\n * email: user?.email,\n * permissions: user?.permissions,\n * })\n * );\n *\n * return <App />;\n * }\n * ```\n */\nexport function useWebMCPContext<T>(\n name: string,\n description: string,\n getValue: () => T\n): WebMCPReturn {\n return useWebMCP({\n name,\n description,\n annotations: {\n title: `Context: ${name}`,\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n execute: getValue,\n });\n}\n","import { isBrowserMcpServer, type BrowserMcpServer } from '@mcp-b/webmcp-ts-sdk';\n\nexport function getBrowserMcpServer(): BrowserMcpServer | undefined {\n if (typeof document === 'undefined' || typeof navigator === 'undefined') {\n return undefined;\n }\n\n const modelContext = document.modelContext ?? navigator.modelContext;\n return modelContext && isBrowserMcpServer(modelContext) ? modelContext : undefined;\n}\n","import type { RegistrationHandle } from '@mcp-b/webmcp-types';\nimport { useEffect, useState } from 'react';\n\nexport function useMcpRegistration(register: () => RegistrationHandle | undefined): boolean {\n const [isRegistered, setIsRegistered] = useState(false);\n\n useEffect(() => {\n const registration = register();\n if (!registration) {\n setIsRegistered(false);\n return;\n }\n\n setIsRegistered(true);\n return () => {\n registration.unregister();\n };\n }, [register]);\n\n return isRegistered;\n}\n","import { normalizeInputSchema } from '@mcp-b/webmcp-polyfill/schema';\nimport { useCallback } from 'react';\nimport type { WebMCPPromptConfig, WebMCPPromptReturn } from './types.js';\nimport { getBrowserMcpServer } from './model-context.js';\nimport { useCommittedRef } from './useCommittedRef.js';\nimport { useMcpRegistration } from './useMcpRegistration.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) prompts.\n *\n * This hook handles the complete lifecycle of an MCP prompt:\n * - Registers the prompt with the installed MCP-B `BrowserMcpServer`\n * - Automatically unregisters on component unmount\n *\n * @param config - Configuration object for the prompt\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Simple prompt without arguments:\n * ```tsx\n * function HelpPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'help',\n * description: 'Get help with using the application',\n * get: async () => ({\n * messages: [{\n * role: 'user',\n * content: { type: 'text', text: 'How do I use this application?' }\n * }]\n * }),\n * });\n *\n * return <div>Help prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Prompt with typed arguments:\n * ```tsx\n * function CodeReviewPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'review_code',\n * description: 'Review code for best practices',\n * argsSchema: {\n * type: 'object',\n * properties: {\n * code: { type: 'string', description: 'The code to review' },\n * language: { type: 'string', description: 'Programming language' },\n * },\n * required: ['code'],\n * } as const,\n * get: async ({ code, language }) => ({\n * messages: [{\n * role: 'user',\n * content: {\n * type: 'text',\n * text: `Please review this ${language ?? ''} code:\\n\\n${code}`\n * }\n * }]\n * }),\n * });\n *\n * return <div>Code review prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPPrompt(config: WebMCPPromptConfig): WebMCPPromptReturn {\n const { name, description } = config;\n\n const configRef = useCommittedRef(config);\n\n const register = useCallback(() => {\n const modelContext = getBrowserMcpServer();\n if (!modelContext) {\n console.warn(\n `[ReactWebMCP] BrowserMcpServer is not available. Prompt \"${name}\" will not be registered.`\n );\n return;\n }\n\n // ponytail: read at registration time so an inline `argsSchema` literal cannot re-register\n // every render. Ceiling: a changed schema is picked up only when `name` or `description`\n // changes; add a caller-supplied deps list, as `useWebMCP` has, if that is ever needed.\n const { argsSchema } = configRef.current;\n const resolvedArgsSchema = argsSchema\n ? normalizeInputSchema(argsSchema).inputSchema\n : undefined;\n\n return modelContext.registerPrompt({\n name,\n ...(description !== undefined && { description }),\n ...(resolvedArgsSchema && { argsSchema: resolvedArgsSchema }),\n get: async (args) => configRef.current.get(args),\n });\n }, [name, description, configRef]);\n\n return { isRegistered: useMcpRegistration(register) };\n}\n","import { useCallback } from 'react';\nimport { getBrowserMcpServer } from './model-context.js';\nimport type { WebMCPResourceConfig, WebMCPResourceReturn } from './types.js';\nimport { useCommittedRef } from './useCommittedRef.js';\nimport { useMcpRegistration } from './useMcpRegistration.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) resources.\n *\n * This hook handles the complete lifecycle of an MCP resource:\n * - Registers the resource with the installed MCP-B `BrowserMcpServer`\n * - Supports both static URIs and URI templates with parameters\n * - Automatically unregisters on component unmount\n *\n * @param config - Configuration object for the resource\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Static resource:\n * ```tsx\n * function AppSettingsResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'config://app-settings',\n * name: 'App Settings',\n * description: 'Application configuration',\n * mimeType: 'application/json',\n * read: async (uri) => ({\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify({ theme: 'dark', language: 'en' })\n * }]\n * }),\n * });\n *\n * return <div>Settings resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Dynamic resource with URI template:\n * ```tsx\n * function UserProfileResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'user://{userId}/profile',\n * name: 'User Profile',\n * description: 'User profile data by ID',\n * mimeType: 'application/json',\n * read: async (uri, params) => {\n * const userId = typeof params?.userId === 'string' ? params.userId : '';\n * const profile = await fetchUserProfile(userId);\n * return {\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify(profile)\n * }]\n * };\n * },\n * });\n *\n * return <div>User profile resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPResource(config: WebMCPResourceConfig): WebMCPResourceReturn {\n const { uri, name, description, mimeType, read } = config;\n\n const readRef = useCommittedRef(read);\n\n const register = useCallback(() => {\n const modelContext = getBrowserMcpServer();\n if (!modelContext) {\n console.warn(\n `[ReactWebMCP] BrowserMcpServer is not available. Resource \"${uri}\" will not be registered.`\n );\n return;\n }\n\n return modelContext.registerResource({\n uri,\n name,\n ...(description !== undefined && { description }),\n ...(mimeType !== undefined && { mimeType }),\n read: (resolvedUri, params) => readRef.current(resolvedUri, params),\n });\n }, [uri, name, description, mimeType, readRef]);\n\n return { isRegistered: useMcpRegistration(register) };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["useWebMCP"],"sources":["../src/useCommittedRef.ts","../src/client/McpClientProvider.tsx","../src/useWebMCPContext.ts","../src/model-context.ts","../src/useMcpRegistration.ts","../src/useWebMCPPrompt.ts","../src/useWebMCPResource.ts"],"sourcesContent":["import { useEffect, useLayoutEffect, useRef } from 'react';\n\nconst useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;\n\nexport function useCommittedRef<T>(value: T) {\n const ref = useRef(value);\n\n // External MCP work may read values after commit but before passive effects. SSR cannot.\n useIsomorphicLayoutEffect(() => {\n ref.current = value;\n }, [value]);\n\n return ref;\n}\n","import {\n type Client,\n type ConnectOptions,\n type Resource,\n type ServerCapabilities,\n type SubscriptionFilter,\n type Tool as McpTool,\n type Transport,\n} from '@modelcontextprotocol/client';\nimport {\n createContext,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useCommittedRef } from '../useCommittedRef.js';\n\n/**\n * Context value provided by McpClientProvider.\n *\n * @internal\n */\ninterface McpClientContextValue {\n client: Client;\n tools: McpTool[];\n resources: Resource[];\n isConnected: boolean;\n isLoading: boolean;\n error: Error | null;\n capabilities: ServerCapabilities | null;\n reconnect: (freshTransport?: Transport) => Promise<void>;\n}\n\ntype ConnectionState = 'disconnected' | 'connecting' | 'initializing' | 'connected';\n\nconst McpClientContext = createContext<McpClientContextValue | null>(null);\n\nfunction startListChangedSubscription(\n client: Client,\n filter: SubscriptionFilter,\n refreshLists: () => Promise<void>\n): () => void {\n const controller = new AbortController();\n\n void client\n .listen(filter, { signal: controller.signal })\n .then(async (subscription) => {\n if (controller.signal.aborted) {\n await subscription.close();\n return;\n }\n\n // Modern servers deliver list_changed only after listen is acknowledged.\n await refreshLists();\n })\n .catch((error) => {\n if (!controller.signal.aborted) {\n console.error(\n '[ReactWebMCP:McpClientProvider]',\n 'Failed to listen for list_changed notifications:',\n error\n );\n }\n });\n\n return () => controller.abort();\n}\n\n/**\n * Props for the McpClientProvider component.\n *\n * @public\n */\nexport interface McpClientProviderProps {\n /**\n * React children to render within the provider.\n */\n children: ReactNode;\n\n /**\n * MCP Client instance to use for communication.\n */\n client: Client;\n\n /**\n * Transport instance for the client to connect through.\n */\n transport: Transport;\n\n /**\n * Optional connection options.\n */\n opts?: ConnectOptions;\n}\n\n/**\n * Provider component that manages an MCP client connection and exposes\n * tools, resources, and connection state to child components.\n *\n * This provider handles:\n * - Establishing and maintaining the MCP client connection\n * - Fetching available tools and resources from the server\n * - Listening for server notifications about tool/resource changes\n * - Managing connection state and errors\n * - Automatic cleanup on unmount\n *\n * @param props - Component props\n * @returns Provider component wrapping children\n *\n * @public\n *\n * @example\n * Connect to an MCP server via tab transport:\n * ```tsx\n * import { TabClientTransport } from '@mcp-b/transports';\n * import { McpClientProvider } from '@mcp-b/react-webmcp';\n * import { Client } from '@modelcontextprotocol/client';\n *\n * const client = new Client(\n * { name: 'my-app', version: '1.0.0' },\n * { versionNegotiation: { mode: 'auto' } }\n * );\n *\n * const transport = new TabClientTransport({\n * channelId: 'mcp',\n * targetOrigin: window.location.origin,\n * });\n *\n * function App() {\n * return (\n * <McpClientProvider client={client} transport={transport}>\n * <MyAppContent />\n * </McpClientProvider>\n * );\n * }\n * ```\n *\n * @example\n * Access tools from child components:\n * ```tsx\n * function MyAppContent() {\n * const { tools, isConnected, isLoading } = useMcpClient();\n *\n * if (isLoading) {\n * return <div>Connecting to MCP server...</div>;\n * }\n *\n * if (!isConnected) {\n * return <div>Failed to connect to MCP server</div>;\n * }\n *\n * return (\n * <div>\n * <h2>Available Tools:</h2>\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * </div>\n * );\n * }\n * ```\n */\nexport function McpClientProvider({\n children,\n client,\n transport,\n opts,\n}: McpClientProviderProps): ReactElement {\n const [resources, setResources] = useState<Resource[]>([]);\n const [tools, setTools] = useState<McpTool[]>([]);\n const [error, setError] = useState<Error | null>(null);\n const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected');\n const [capabilities, setCapabilities] = useState<ServerCapabilities | null>(null);\n const isConnected = connectionState === 'initializing' || connectionState === 'connected';\n const isLoading = connectionState === 'connecting' || connectionState === 'initializing';\n const connectionStateRef = useRef<ConnectionState>('disconnected');\n const connectionGenerationRef = useRef(0);\n const closePromiseRef = useRef<Promise<void> | null>(null);\n const providerCloseRef = useRef<object | null>(null);\n const inventoryRequestRef = useRef(0);\n const requestOptsRef = useCommittedRef(opts);\n\n /**\n * Refreshes every provider-owned inventory list and clears a prior inventory\n * error only after the complete refresh succeeds.\n */\n const refreshInventory = useCallback(\n async (connectionGeneration: number): Promise<void> => {\n const inventoryRequest = ++inventoryRequestRef.current;\n const serverCapabilities = client.getServerCapabilities();\n\n try {\n const [resourceResponse, toolResponse] = await Promise.all([\n serverCapabilities?.resources\n ? client.listResources(undefined, { cacheMode: 'refresh' })\n : undefined,\n serverCapabilities?.tools\n ? client.listTools(undefined, { cacheMode: 'refresh' })\n : undefined,\n ]);\n\n if (\n connectionGeneration !== connectionGenerationRef.current ||\n inventoryRequest !== inventoryRequestRef.current\n ) {\n return;\n }\n\n setResources(resourceResponse?.resources ?? []);\n setTools(toolResponse?.tools ?? []);\n setError(null);\n } catch (cause) {\n const error = cause instanceof Error ? cause : new Error(String(cause));\n if (\n connectionGeneration !== connectionGenerationRef.current ||\n inventoryRequest !== inventoryRequestRef.current\n ) {\n return;\n }\n setError(error);\n throw error;\n }\n },\n [client]\n );\n\n /**\n * Connects a disconnected client, or retries inventory discovery when the\n * MCP handshake is already alive. Calls made while work is in progress no-op.\n *\n * Pass a fresh transport after a one-shot transport has closed.\n */\n const reconnect = useCallback(\n async (freshTransport?: Transport): Promise<void> => {\n if (connectionStateRef.current === 'connected' && freshTransport === undefined) {\n const connectionGeneration = connectionGenerationRef.current;\n connectionStateRef.current = 'initializing';\n setConnectionState('initializing');\n\n try {\n await refreshInventory(connectionGeneration);\n } catch {\n // Inventory failure does not undo the completed MCP handshake.\n }\n\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'connected';\n setConnectionState('connected');\n }\n return;\n }\n\n if (connectionStateRef.current !== 'disconnected') {\n return;\n }\n\n connectionStateRef.current = 'connecting';\n setConnectionState('connecting');\n setError(null);\n const connectionGeneration = connectionGenerationRef.current;\n\n try {\n await closePromiseRef.current;\n if (connectionGeneration !== connectionGenerationRef.current) {\n return;\n }\n await client.connect(freshTransport ?? transport, requestOptsRef.current);\n if (connectionGeneration !== connectionGenerationRef.current) {\n return;\n }\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e));\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setError(err);\n }\n throw err;\n }\n\n const caps = client.getServerCapabilities();\n setCapabilities(caps ?? null);\n connectionStateRef.current = 'initializing';\n setConnectionState('initializing');\n\n try {\n await refreshInventory(connectionGeneration);\n } catch {\n // Inventory failure does not undo the completed MCP handshake.\n }\n\n if (connectionGeneration === connectionGenerationRef.current) {\n connectionStateRef.current = 'connected';\n setConnectionState('connected');\n }\n },\n [client, transport, refreshInventory, requestOptsRef]\n );\n\n useEffect(() => {\n if (!isConnected) {\n return;\n }\n\n const serverCapabilities = client.getServerCapabilities();\n\n const resourcesListChanged = serverCapabilities?.resources?.listChanged === true;\n const toolsListChanged = serverCapabilities?.tools?.listChanged === true;\n const refreshLists = async () => {\n try {\n await refreshInventory(connectionGenerationRef.current);\n } catch (error) {\n console.error(\n '[ReactWebMCP:McpClientProvider]',\n 'Failed to refresh tools/resources after list_changed:',\n error\n );\n }\n };\n\n if (resourcesListChanged) {\n client.setNotificationHandler('notifications/resources/list_changed', refreshLists);\n }\n\n if (toolsListChanged) {\n client.setNotificationHandler('notifications/tools/list_changed', refreshLists);\n }\n\n const hasListChanged = resourcesListChanged || toolsListChanged;\n const stopListening =\n client.getProtocolEra() === 'modern' && hasListChanged\n ? startListChangedSubscription(\n client,\n {\n ...(toolsListChanged && { toolsListChanged: true }),\n ...(resourcesListChanged && { resourcesListChanged: true }),\n },\n refreshLists\n )\n : undefined;\n if (!stopListening && hasListChanged) {\n // Legacy servers deliver list_changed unsolicited once handlers are installed.\n void refreshLists();\n }\n\n return () => {\n if (resourcesListChanged) {\n client.removeNotificationHandler('notifications/resources/list_changed');\n }\n\n if (toolsListChanged) {\n client.removeNotificationHandler('notifications/tools/list_changed');\n }\n\n stopListening?.();\n };\n }, [client, isConnected, refreshInventory]);\n\n useEffect(() => {\n let active = true;\n const connectionGeneration = connectionGenerationRef.current;\n const previousOnclose = client.onclose;\n const handleClientClose = () => {\n try {\n previousOnclose?.();\n } finally {\n if (active && providerCloseRef.current === null) {\n connectionGenerationRef.current += 1;\n inventoryRequestRef.current += 1;\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setCapabilities(null);\n setResources([]);\n setTools([]);\n }\n }\n };\n client.onclose = handleClientClose;\n connectionStateRef.current = 'disconnected';\n setConnectionState('disconnected');\n setCapabilities(null);\n setResources([]);\n setTools([]);\n\n // Initial connection - reconnect() has its own guard to prevent concurrent connections\n reconnect().catch((err) => {\n if (connectionGeneration === connectionGenerationRef.current) {\n console.error('[ReactWebMCP:McpClientProvider]', 'Failed to connect MCP client:', err);\n }\n });\n\n return () => {\n active = false;\n if (client.onclose === handleClientClose) {\n if (previousOnclose) {\n client.onclose = previousOnclose;\n } else {\n Reflect.deleteProperty(client, 'onclose');\n }\n }\n connectionGenerationRef.current += 1;\n inventoryRequestRef.current += 1;\n connectionStateRef.current = 'disconnected';\n const closeToken = {};\n providerCloseRef.current = closeToken;\n closePromiseRef.current = client\n .close()\n .catch((error: unknown) => {\n console.error('[ReactWebMCP:McpClientProvider]', 'Failed to close MCP client:', error);\n })\n .finally(() => {\n if (providerCloseRef.current === closeToken) {\n providerCloseRef.current = null;\n }\n });\n };\n }, [client, transport, reconnect]);\n\n const contextValue = useMemo(\n () => ({\n client,\n tools,\n resources,\n isConnected,\n isLoading,\n error,\n capabilities,\n reconnect,\n }),\n [client, tools, resources, isConnected, isLoading, error, capabilities, reconnect]\n );\n\n return <McpClientContext.Provider value={contextValue}>{children}</McpClientContext.Provider>;\n}\n\n/**\n * Hook to access the MCP client context.\n * Must be used within an {@link McpClientProvider}.\n *\n * @returns The MCP client context including client instance, tools, resources, and connection state\n * @throws Error if used outside of McpClientProvider\n *\n * @public\n *\n * @example\n * ```tsx\n * function ToolsList() {\n * const { tools, isConnected, error, reconnect } = useMcpClient();\n *\n * if (error) {\n * return (\n * <div>\n * Error: {error.message}\n * <button onClick={() => void reconnect()}>Retry</button>\n * </div>\n * );\n * }\n *\n * if (!isConnected) {\n * return <div>Not connected</div>;\n * }\n *\n * return (\n * <ul>\n * {tools.map(tool => (\n * <li key={tool.name}>{tool.description}</li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useMcpClient() {\n const context = useContext(McpClientContext);\n if (!context) {\n throw new Error('useMcpClient must be used within an McpClientProvider');\n }\n return context;\n}\n","import { useWebMCP, type WebMCPReturn } from 'usewebmcp';\n\n/**\n * Convenience hook for exposing read-only context data to AI assistants.\n *\n * This is a simplified wrapper around {@link useWebMCP} specifically designed for\n * context tools that expose data without performing actions. The hook automatically\n * configures appropriate annotations (read-only, idempotent) and handles value\n * serialization.\n *\n * JSON-compatible values are normalized to text plus `structuredContent`.\n *\n * @template T - The type of context data to expose\n *\n * @param name - Unique identifier for the context tool (e.g., 'context_current_post')\n * @param description - Human-readable description of the context for AI assistants\n * @param getValue - Function that returns the current context value\n * @returns Tool execution state and control methods\n *\n * @public\n *\n * @example\n * Expose current post context:\n * ```tsx\n * function PostDetailPage() {\n * const { postId } = useParams();\n * const { data: post } = useQuery(['post', postId], () => fetchPost(postId));\n *\n * useWebMCPContext(\n * 'context_current_post',\n * 'Get the currently viewed post ID and metadata',\n * () => ({\n * postId,\n * title: post?.title,\n * author: post?.author,\n * tags: post?.tags,\n * createdAt: post?.createdAt,\n * })\n * );\n *\n * return <PostContent post={post} />;\n * }\n * ```\n *\n * @example\n * Expose user session context:\n * ```tsx\n * function AppRoot() {\n * const { user, isAuthenticated } = useAuth();\n *\n * useWebMCPContext(\n * 'context_user_session',\n * 'Get the current user session information',\n * () => ({\n * isAuthenticated,\n * userId: user?.id,\n * email: user?.email,\n * permissions: user?.permissions,\n * })\n * );\n *\n * return <App />;\n * }\n * ```\n */\nexport function useWebMCPContext<T>(\n name: string,\n description: string,\n getValue: () => T\n): WebMCPReturn {\n return useWebMCP({\n name,\n description,\n annotations: {\n title: `Context: ${name}`,\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n execute: getValue,\n });\n}\n","import { isBrowserMcpServer, type BrowserMcpServer } from '@mcp-b/webmcp-ts-sdk';\n\nexport function getBrowserMcpServer(): BrowserMcpServer | undefined {\n if (typeof document === 'undefined' || typeof navigator === 'undefined') {\n return undefined;\n }\n\n const modelContext = document.modelContext ?? navigator.modelContext;\n return modelContext && isBrowserMcpServer(modelContext) ? modelContext : undefined;\n}\n","import type { RegistrationHandle } from '@mcp-b/webmcp-types';\nimport { useEffect, useState } from 'react';\n\nexport function useMcpRegistration(register: () => RegistrationHandle | undefined): boolean {\n const [isRegistered, setIsRegistered] = useState(false);\n\n useEffect(() => {\n const registration = register();\n if (!registration) {\n setIsRegistered(false);\n return;\n }\n\n setIsRegistered(true);\n return () => {\n registration.unregister();\n };\n }, [register]);\n\n return isRegistered;\n}\n","import { normalizeInputSchema } from '@mcp-b/webmcp-polyfill/schema';\nimport { useCallback } from 'react';\nimport type { WebMCPPromptConfig, WebMCPPromptReturn } from './types.js';\nimport { getBrowserMcpServer } from './model-context.js';\nimport { useCommittedRef } from './useCommittedRef.js';\nimport { useMcpRegistration } from './useMcpRegistration.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) prompts.\n *\n * This hook handles the complete lifecycle of an MCP prompt:\n * - Registers the prompt with the installed MCP-B `BrowserMcpServer`\n * - Automatically unregisters on component unmount\n *\n * @param config - Configuration object for the prompt\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Simple prompt without arguments:\n * ```tsx\n * function HelpPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'help',\n * description: 'Get help with using the application',\n * get: async () => ({\n * messages: [{\n * role: 'user',\n * content: { type: 'text', text: 'How do I use this application?' }\n * }]\n * }),\n * });\n *\n * return <div>Help prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Prompt with typed arguments:\n * ```tsx\n * function CodeReviewPrompt() {\n * const { isRegistered } = useWebMCPPrompt({\n * name: 'review_code',\n * description: 'Review code for best practices',\n * argsSchema: {\n * type: 'object',\n * properties: {\n * code: { type: 'string', description: 'The code to review' },\n * language: { type: 'string', description: 'Programming language' },\n * },\n * required: ['code'],\n * } as const,\n * get: async ({ code, language }) => ({\n * messages: [{\n * role: 'user',\n * content: {\n * type: 'text',\n * text: `Please review this ${language ?? ''} code:\\n\\n${code}`\n * }\n * }]\n * }),\n * });\n *\n * return <div>Code review prompt {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPPrompt(config: WebMCPPromptConfig): WebMCPPromptReturn {\n const { name, description } = config;\n\n const configRef = useCommittedRef(config);\n\n const register = useCallback(() => {\n const modelContext = getBrowserMcpServer();\n if (!modelContext) {\n console.warn(\n `[ReactWebMCP] BrowserMcpServer is not available. Prompt \"${name}\" will not be registered.`\n );\n return;\n }\n\n // ponytail: read at registration time so an inline `argsSchema` literal cannot re-register\n // every render. Ceiling: a changed schema is picked up only when `name` or `description`\n // changes; add a caller-supplied deps list, as `useWebMCP` has, if that is ever needed.\n const { argsSchema } = configRef.current;\n const resolvedArgsSchema = argsSchema\n ? normalizeInputSchema(argsSchema).inputSchema\n : undefined;\n\n return modelContext.registerPrompt({\n name,\n ...(description !== undefined && { description }),\n ...(resolvedArgsSchema && { argsSchema: resolvedArgsSchema }),\n get: async (args) => configRef.current.get(args),\n });\n }, [name, description, configRef]);\n\n return { isRegistered: useMcpRegistration(register) };\n}\n","import { useCallback } from 'react';\nimport { getBrowserMcpServer } from './model-context.js';\nimport type { WebMCPResourceConfig, WebMCPResourceReturn } from './types.js';\nimport { useCommittedRef } from './useCommittedRef.js';\nimport { useMcpRegistration } from './useMcpRegistration.js';\n\n/**\n * React hook for registering Model Context Protocol (MCP) resources.\n *\n * This hook handles the complete lifecycle of an MCP resource:\n * - Registers the resource with the installed MCP-B `BrowserMcpServer`\n * - Supports both static URIs and URI templates with parameters\n * - Automatically unregisters on component unmount\n *\n * @param config - Configuration object for the resource\n * @returns Object indicating registration status\n *\n * @public\n *\n * @example\n * Static resource:\n * ```tsx\n * function AppSettingsResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'config://app-settings',\n * name: 'App Settings',\n * description: 'Application configuration',\n * mimeType: 'application/json',\n * read: async (uri) => ({\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify({ theme: 'dark', language: 'en' })\n * }]\n * }),\n * });\n *\n * return <div>Settings resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n *\n * @example\n * Dynamic resource with URI template:\n * ```tsx\n * function UserProfileResource() {\n * const { isRegistered } = useWebMCPResource({\n * uri: 'user://{userId}/profile',\n * name: 'User Profile',\n * description: 'User profile data by ID',\n * mimeType: 'application/json',\n * read: async (uri, params) => {\n * const userId = typeof params?.userId === 'string' ? params.userId : '';\n * const profile = await fetchUserProfile(userId);\n * return {\n * contents: [{\n * uri: uri.href,\n * text: JSON.stringify(profile)\n * }]\n * };\n * },\n * });\n *\n * return <div>User profile resource {isRegistered ? 'ready' : 'loading'}</div>;\n * }\n * ```\n */\nexport function useWebMCPResource(config: WebMCPResourceConfig): WebMCPResourceReturn {\n const { uri, name, description, mimeType, read } = config;\n\n const readRef = useCommittedRef(read);\n\n const register = useCallback(() => {\n const modelContext = getBrowserMcpServer();\n if (!modelContext) {\n console.warn(\n `[ReactWebMCP] BrowserMcpServer is not available. Resource \"${uri}\" will not be registered.`\n );\n return;\n }\n\n return modelContext.registerResource({\n uri,\n name,\n ...(description !== undefined && { description }),\n ...(mimeType !== undefined && { mimeType }),\n read: (resolvedUri, params) => readRef.current(resolvedUri, params),\n });\n }, [uri, name, description, mimeType, readRef]);\n\n return { isRegistered: useMcpRegistration(register) };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-b/react-webmcp",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.3",
|
|
4
4
|
"description": "React hooks for Model Context Protocol (MCP) - expose React components as AI tools for Claude, ChatGPT, Cursor, and Copilot",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -61,24 +61,24 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@modelcontextprotocol/client": "2.0.0",
|
|
64
|
-
"@mcp-b/webmcp-polyfill": "5.0.
|
|
65
|
-
"@mcp-b/webmcp-ts-sdk": "5.0.
|
|
66
|
-
"@mcp-b/webmcp-types": "5.0.
|
|
67
|
-
"usewebmcp": "5.0.
|
|
64
|
+
"@mcp-b/webmcp-polyfill": "5.0.3",
|
|
65
|
+
"@mcp-b/webmcp-ts-sdk": "5.0.3",
|
|
66
|
+
"@mcp-b/webmcp-types": "5.0.3",
|
|
67
|
+
"usewebmcp": "5.0.3"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@modelcontextprotocol/server": "2.0.0",
|
|
71
71
|
"@types/node": "22.17.2",
|
|
72
72
|
"@types/react": "^19.2.9",
|
|
73
|
-
"@vitest/coverage-v8": "
|
|
73
|
+
"@vitest/coverage-v8": "4.1.6",
|
|
74
74
|
"playwright": "^1.60.0",
|
|
75
75
|
"react": "^19.1.0",
|
|
76
76
|
"react-dom": "^19.1.0",
|
|
77
77
|
"typescript": "^5.8.3",
|
|
78
|
-
"vite-plus": "
|
|
79
|
-
"vitest": "npm:@voidzero-dev/vite-plus-test@
|
|
78
|
+
"vite-plus": "0.1.22",
|
|
79
|
+
"vitest": "npm:@voidzero-dev/vite-plus-test@0.1.22",
|
|
80
80
|
"vitest-browser-react": "^2.0.4",
|
|
81
|
-
"@mcp-b/global": "5.0.
|
|
81
|
+
"@mcp-b/global": "5.0.3"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
84
|
"react": "^18.0.0 || ^19.0.0"
|
|
@@ -97,6 +97,6 @@
|
|
|
97
97
|
"publish:npm": "pnpm publish --access public",
|
|
98
98
|
"test": "vp test run",
|
|
99
99
|
"test:watch": "vp test",
|
|
100
|
-
"typecheck": "tsc --noEmit && vp test run --typecheck --silent"
|
|
100
|
+
"typecheck": "tsc --noEmit && vp test run --typecheck.only --silent"
|
|
101
101
|
}
|
|
102
102
|
}
|