import { useState, useEffect, useCallback } from "react"; import type { InspectorClientProtocol } from "../mcp/inspectorClientProtocol.js"; import type { PagedResourcesState, PagedResourcesStateEventMap, LoadPageResult, } from "../mcp/state/pagedResourcesState.js"; import type { Resource } from "@modelcontextprotocol/sdk/types.js"; import type { TypedEventGeneric } from "../mcp/typedEventTarget.js"; export interface UsePagedResourcesResult { resources: Resource[]; loadPage: ( cursor?: string, metadata?: Record, ) => Promise; clear: () => void; } /** * React hook that subscribes to PagedResourcesState and returns resources + loadPage. */ export function usePagedResources( client: InspectorClientProtocol | null, pagedResourcesState: PagedResourcesState | null, ): UsePagedResourcesResult { const [resources, setResources] = useState( pagedResourcesState?.getResources() ?? [], ); useEffect(() => { if (!pagedResourcesState) { setResources([]); return; } setResources(pagedResourcesState.getResources()); const onResourcesChange = ( event: TypedEventGeneric, ) => { setResources(event.detail); }; pagedResourcesState.addEventListener("resourcesChange", onResourcesChange); return () => { pagedResourcesState.removeEventListener( "resourcesChange", onResourcesChange, ); }; }, [pagedResourcesState]); const loadPage = useCallback( async ( cursor?: string, metadata?: Record, ): Promise => { if (!pagedResourcesState || !client) { return { resources: [], nextCursor: undefined }; } const result = await pagedResourcesState.loadPage(cursor, metadata); setResources(pagedResourcesState.getResources()); return result; }, [client, pagedResourcesState], ); const clear = useCallback(() => { pagedResourcesState?.clear(); }, [pagedResourcesState]); return { resources, loadPage, clear }; }