inspector/core/react/usePagedRequestorTasks.ts
cliffhall f9f79fff54 Address @claude review: format core/, typed output-validation error, document progress heuristic
- 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>
2026-07-15 11:08:54 -04:00

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 };
}