- 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>
74 lines
2.4 KiB
TypeScript
74 lines
2.4 KiB
TypeScript
import { useState, useEffect, useCallback } from "react";
|
|
import type { InspectorClientProtocol } from "../mcp/inspectorClientProtocol.js";
|
|
import type {
|
|
PagedRequestorTasksState,
|
|
PagedRequestorTasksStateEventMap,
|
|
LoadPageResult,
|
|
} from "../mcp/state/pagedRequestorTasksState.js";
|
|
import type { Task } from "@modelcontextprotocol/client";
|
|
import type { TypedEventGeneric } from "../mcp/typedEventTarget.js";
|
|
|
|
export interface UsePagedRequestorTasksResult {
|
|
tasks: Task[];
|
|
loadPage: (cursor?: string) => Promise<LoadPageResult>;
|
|
clear: () => void;
|
|
nextCursor: string | undefined;
|
|
}
|
|
|
|
/**
|
|
* React hook that subscribes to PagedRequestorTasksState and returns tasks,
|
|
* loadPage, clear, and nextCursor.
|
|
*/
|
|
export function usePagedRequestorTasks(
|
|
client: InspectorClientProtocol | null,
|
|
pagedRequestorTasksState: PagedRequestorTasksState | null,
|
|
): UsePagedRequestorTasksResult {
|
|
const [tasks, setTasks] = useState<Task[]>(
|
|
pagedRequestorTasksState?.getTasks() ?? [],
|
|
);
|
|
const [nextCursor, setNextCursor] = useState<string | undefined>(
|
|
pagedRequestorTasksState?.getNextCursor() ?? undefined,
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!pagedRequestorTasksState) {
|
|
setTasks([]);
|
|
setNextCursor(undefined);
|
|
return;
|
|
}
|
|
setTasks(pagedRequestorTasksState.getTasks());
|
|
setNextCursor(pagedRequestorTasksState.getNextCursor() ?? undefined);
|
|
const onTasksChange = (
|
|
event: TypedEventGeneric<PagedRequestorTasksStateEventMap, "tasksChange">,
|
|
) => {
|
|
setTasks(event.detail);
|
|
setNextCursor(pagedRequestorTasksState.getNextCursor() ?? undefined);
|
|
};
|
|
pagedRequestorTasksState.addEventListener("tasksChange", onTasksChange);
|
|
return () => {
|
|
pagedRequestorTasksState.removeEventListener(
|
|
"tasksChange",
|
|
onTasksChange,
|
|
);
|
|
};
|
|
}, [pagedRequestorTasksState]);
|
|
|
|
const loadPage = useCallback(
|
|
async (cursor?: string): Promise<LoadPageResult> => {
|
|
if (!pagedRequestorTasksState || !client) {
|
|
return { tasks: [], nextCursor: undefined };
|
|
}
|
|
const result = await pagedRequestorTasksState.loadPage(cursor);
|
|
setTasks(pagedRequestorTasksState.getTasks());
|
|
setNextCursor(pagedRequestorTasksState.getNextCursor() ?? undefined);
|
|
return result;
|
|
},
|
|
[client, pagedRequestorTasksState],
|
|
);
|
|
|
|
const clear = useCallback(() => {
|
|
pagedRequestorTasksState?.clear();
|
|
}, [pagedRequestorTasksState]);
|
|
|
|
return { tasks, loadPage, clear, nextCursor };
|
|
}
|