- Run Prettier on the 60 core/ files this PR touched — the deterministic import rewrite had collapsed multi-line imports and left throws unwrapped; core/ is in no format gate so it slipped past CI. Now Prettier-clean. - Default-path output-schema violation now throws ProtocolError(ProtocolErrorCode.InvalidParams, …) instead of a bare Error, matching the prior v1 contract (SDK threw a typed McpError with a code) so downstream `instanceof`/`.code` branching keeps working. - Document the "assumes serial construction" invariant on pollTaskToolCall's progress-subscription revival (the `_progressHandlers` new-key heuristic). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
60 lines
1.8 KiB
TypeScript
60 lines
1.8 KiB
TypeScript
import { useState, useEffect, useCallback } from "react";
|
|
import type { InspectorClientProtocol } from "../mcp/inspectorClientProtocol.js";
|
|
import type {
|
|
PagedToolsState,
|
|
PagedToolsStateEventMap,
|
|
LoadPageResult,
|
|
} from "../mcp/state/pagedToolsState.js";
|
|
import type { Tool } from "@modelcontextprotocol/client";
|
|
import type { TypedEventGeneric } from "../mcp/typedEventTarget.js";
|
|
|
|
export interface UsePagedToolsResult {
|
|
tools: Tool[];
|
|
loadPage: (cursor?: string) => Promise<LoadPageResult>;
|
|
clear: () => void;
|
|
}
|
|
|
|
/**
|
|
* React hook that subscribes to PagedToolsState and returns tools + loadPage.
|
|
*/
|
|
export function usePagedTools(
|
|
client: InspectorClientProtocol | null,
|
|
pagedToolsState: PagedToolsState | null,
|
|
): UsePagedToolsResult {
|
|
const [tools, setTools] = useState<Tool[]>(pagedToolsState?.getTools() ?? []);
|
|
|
|
useEffect(() => {
|
|
if (!pagedToolsState) {
|
|
setTools([]);
|
|
return;
|
|
}
|
|
setTools(pagedToolsState.getTools());
|
|
const onToolsChange = (
|
|
event: TypedEventGeneric<PagedToolsStateEventMap, "toolsChange">,
|
|
) => {
|
|
setTools(event.detail);
|
|
};
|
|
pagedToolsState.addEventListener("toolsChange", onToolsChange);
|
|
return () => {
|
|
pagedToolsState.removeEventListener("toolsChange", onToolsChange);
|
|
};
|
|
}, [pagedToolsState]);
|
|
|
|
const loadPage = useCallback(
|
|
async (cursor?: string): Promise<LoadPageResult> => {
|
|
if (!pagedToolsState || !client) {
|
|
return { tools: [], nextCursor: undefined };
|
|
}
|
|
const result = await pagedToolsState.loadPage(cursor);
|
|
setTools(pagedToolsState.getTools());
|
|
return result;
|
|
},
|
|
[client, pagedToolsState],
|
|
);
|
|
|
|
const clear = useCallback(() => {
|
|
pagedToolsState?.clear();
|
|
}, [pagedToolsState]);
|
|
|
|
return { tools, loadPage, clear };
|
|
}
|