fix: salvage a list result's valid entries when one is malformed (#1909)
The SDK validates a list result as a whole, so a single non-conforming entry
rejected the entire response and the panel showed "Couldn't load resources"
with zero items — the exact opposite of what a debugging tool should do with a
server that is 90% right. Reported against a PHP server whose empty
`annotations` object reaches the wire as `[]` rather than `{}`.
`listAll*` now falls back to a lenient re-walk when — and only when — the SDK
rejects a result it received (`InvalidResult` / `UnsupportedResultType`), keeps
the entries that validate, and reports the rest. A conforming server takes the
strict, cache-aware path exactly as before.
Salvaging is not accepting: the dropped entries are named with their reason in
a warning above the list, and the Protocol entry is still marked rejected, so
the wire truth survives. When every entry validates, the original error is
rethrown rather than swallowed — the rejection was about something the
per-item pass cannot explain.
`isClientDecodeRejection` moves to the new module and is now shared with
managedListState, which had the only copy.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
ebeaa142df
commit
fe5b622fd1
10 changed files with 1402 additions and 64 deletions
|
|
@ -0,0 +1,89 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { MalformedItemsWarning } from "./MalformedItemsWarning";
|
||||
|
||||
const meta: Meta<typeof MalformedItemsWarning> = {
|
||||
title: "Elements/MalformedItemsWarning",
|
||||
component: MalformedItemsWarning,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof MalformedItemsWarning>;
|
||||
|
||||
/** The failure this was built for: a PHP server whose empty `annotations`
|
||||
* object reaches the wire as `[]` (#1909). */
|
||||
export const OneDroppedTemplate: Story = {
|
||||
args: {
|
||||
method: "resources/templates/list",
|
||||
what: "resource templates",
|
||||
items: [
|
||||
{
|
||||
method: "resources/templates/list",
|
||||
index: 1,
|
||||
label: "array_annotations",
|
||||
reason: "annotations: Invalid input: expected object, received array",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export const SeveralDropped: Story = {
|
||||
args: {
|
||||
method: "tools/list",
|
||||
what: "tools",
|
||||
items: [
|
||||
{
|
||||
method: "tools/list",
|
||||
index: 0,
|
||||
label: "get_weather",
|
||||
reason: "inputSchema: Invalid input: expected object, received string",
|
||||
},
|
||||
{
|
||||
method: "tools/list",
|
||||
index: 4,
|
||||
label: "add",
|
||||
reason: "name: Invalid input: expected string, received number",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/** An entry too broken to carry a name is reported by its position. */
|
||||
export const UnlabeledEntry: Story = {
|
||||
args: {
|
||||
method: "prompts/list",
|
||||
what: "prompts",
|
||||
items: [
|
||||
{
|
||||
method: "prompts/list",
|
||||
index: 2,
|
||||
reason: "Invalid input: expected object, received null",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/** Entries belonging to another list method are ignored — the same set is
|
||||
* passed to every panel, and each selects its own. */
|
||||
export const OtherMethodOnly: Story = {
|
||||
args: {
|
||||
method: "resources/templates/list",
|
||||
what: "resource templates",
|
||||
items: [
|
||||
{
|
||||
method: "tools/list",
|
||||
index: 0,
|
||||
label: "get_weather",
|
||||
reason: "inputSchema: Invalid input: expected object, received string",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/** The resting state: a conforming server, nothing rendered. */
|
||||
export const Conforming: Story = {
|
||||
args: {
|
||||
method: "resources/templates/list",
|
||||
what: "resource templates",
|
||||
items: [],
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
import { describe, it, expect } from "vitest";
|
||||
import { screen } from "@testing-library/react";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import { renderWithMantine } from "../../../test/renderWithMantine";
|
||||
import { MalformedItemsWarning } from "./MalformedItemsWarning";
|
||||
|
||||
const TEMPLATE_ENTRY: MalformedListItem = {
|
||||
method: "resources/templates/list",
|
||||
index: 1,
|
||||
label: "array_annotations",
|
||||
reason: "annotations: Invalid input: expected object, received array",
|
||||
};
|
||||
|
||||
const TOOL_ENTRY: MalformedListItem = {
|
||||
method: "tools/list",
|
||||
index: 0,
|
||||
label: "get_weather",
|
||||
reason: "inputSchema: Invalid input: expected object, received string",
|
||||
};
|
||||
|
||||
describe("MalformedItemsWarning", () => {
|
||||
it("names the dropped entry and why it was dropped", () => {
|
||||
renderWithMantine(
|
||||
<MalformedItemsWarning
|
||||
items={[TEMPLATE_ENTRY]}
|
||||
method="resources/templates/list"
|
||||
what="resource templates"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("1 malformed entry dropped")).toBeInTheDocument();
|
||||
expect(screen.getByText("array_annotations")).toBeInTheDocument();
|
||||
expect(screen.getByText(TEMPLATE_ENTRY.reason)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders nothing when the server was conforming", () => {
|
||||
renderWithMantine(
|
||||
<MalformedItemsWarning
|
||||
items={[]}
|
||||
method="resources/templates/list"
|
||||
what="resource templates"
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("ignores entries belonging to another list method", () => {
|
||||
// Every panel receives the whole set and selects its own — a tools failure
|
||||
// must not surface above the templates list.
|
||||
renderWithMantine(
|
||||
<MalformedItemsWarning
|
||||
items={[TOOL_ENTRY]}
|
||||
method="resources/templates/list"
|
||||
what="resource templates"
|
||||
/>,
|
||||
);
|
||||
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("get_weather")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("pluralizes the count and lists every entry", () => {
|
||||
renderWithMantine(
|
||||
<MalformedItemsWarning
|
||||
items={[
|
||||
TOOL_ENTRY,
|
||||
{ ...TOOL_ENTRY, index: 4, label: "add", reason: "name: bad" },
|
||||
TEMPLATE_ENTRY,
|
||||
]}
|
||||
method="tools/list"
|
||||
what="tools"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("2 malformed entries dropped")).toBeInTheDocument();
|
||||
expect(screen.getByText("get_weather")).toBeInTheDocument();
|
||||
expect(screen.getByText("add")).toBeInTheDocument();
|
||||
// The template entry belongs to the other panel.
|
||||
expect(screen.queryByText("array_annotations")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("falls back to the position when the entry had no usable name", () => {
|
||||
renderWithMantine(
|
||||
<MalformedItemsWarning
|
||||
items={[
|
||||
{
|
||||
method: "prompts/list",
|
||||
index: 2,
|
||||
reason: "Invalid input: expected object, received null",
|
||||
},
|
||||
]}
|
||||
method="prompts/list"
|
||||
what="prompts"
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText("entry 2")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import { Alert, Code, List, ScrollArea, Text } from "@mantine/core";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
|
||||
export interface MalformedItemsWarningProps {
|
||||
/**
|
||||
* Every malformed entry the client is currently reporting, across all list
|
||||
* methods — pass the hook's set straight through; the component selects its
|
||||
* own by {@link method}.
|
||||
*/
|
||||
items: MalformedListItem[];
|
||||
/** The list method this panel renders, e.g. `"resources/templates/list"`. */
|
||||
method: string;
|
||||
/** What the entries are, for the alert title — e.g. "resource templates". */
|
||||
what: string;
|
||||
}
|
||||
|
||||
// Yellow, not red: unlike ListLoadError this is not a failure — the list DID
|
||||
// load and is rendered below. It reports what the server sent that the
|
||||
// Inspector could not accept.
|
||||
const WarningAlert = Alert.withProps({
|
||||
color: "yellow",
|
||||
variant: "light",
|
||||
});
|
||||
|
||||
// The entry list stays compact; the reasons are the payload, so they get the
|
||||
// monospace treatment while the labels stay in body type.
|
||||
const EntryList = List.withProps({
|
||||
size: "xs",
|
||||
spacing: 4,
|
||||
withPadding: true,
|
||||
});
|
||||
|
||||
const EntryReason = Code.withProps({
|
||||
variant: "wrapping",
|
||||
});
|
||||
|
||||
// A server can be wrong about many entries at once; cap the height so a badly
|
||||
// broken list can't push the list itself off the sidebar.
|
||||
const EntryScroll = ScrollArea.withProps({
|
||||
mah: 180,
|
||||
type: "auto",
|
||||
});
|
||||
|
||||
const SummaryText = Text.withProps({
|
||||
size: "xs",
|
||||
mb: 4,
|
||||
});
|
||||
|
||||
// The entry's identity, emphasized against its monospaced reason.
|
||||
const EntryLabel = Text.withProps({
|
||||
component: "span",
|
||||
size: "xs",
|
||||
fw: 600,
|
||||
});
|
||||
|
||||
/** `label` when the entry had one, else its position in the response. */
|
||||
function describeEntry(item: MalformedListItem): string {
|
||||
return item.label ?? `entry ${item.index}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Some entries were dropped" — the visible half of the #1909 salvage.
|
||||
*
|
||||
* The client keeps the valid entries of a list whose result failed schema
|
||||
* validation, rather than losing the whole list to one bad entry. Dropping them
|
||||
* silently would trade one lie for another, so what was dropped, and why, is
|
||||
* reported here beside the list that rendered without them.
|
||||
*/
|
||||
export function MalformedItemsWarning({
|
||||
items,
|
||||
method,
|
||||
what,
|
||||
}: MalformedItemsWarningProps) {
|
||||
const mine = items.filter((item) => item.method === method);
|
||||
if (mine.length === 0) return null;
|
||||
|
||||
return (
|
||||
<WarningAlert
|
||||
title={`${mine.length} malformed ${
|
||||
mine.length === 1 ? "entry" : "entries"
|
||||
} dropped`}
|
||||
>
|
||||
<SummaryText>
|
||||
The server returned {what} that don't match the MCP schema. The
|
||||
rest of the list is shown below.
|
||||
</SummaryText>
|
||||
<EntryScroll>
|
||||
<EntryList>
|
||||
{mine.map((item) => (
|
||||
<List.Item key={`${item.method}:${item.index}`}>
|
||||
<EntryLabel>{describeEntry(item)}</EntryLabel>{" "}
|
||||
<EntryReason>{item.reason}</EntryReason>
|
||||
</List.Item>
|
||||
))}
|
||||
</EntryList>
|
||||
</EntryScroll>
|
||||
</WarningAlert>
|
||||
);
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
|||
import type { Prompt } from "@modelcontextprotocol/client";
|
||||
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
||||
import { ListLoadError } from "../../elements/ListLoadError/ListLoadError";
|
||||
import { MalformedItemsWarning } from "../../elements/MalformedItemsWarning/MalformedItemsWarning";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import {
|
||||
ListPaginationControls,
|
||||
type ListPaginationControlsProps,
|
||||
|
|
@ -41,6 +43,12 @@ export interface PromptControlsProps {
|
|||
* A failed list load, surfaced above the list instead of leaving the panel
|
||||
* empty (which reads as "this server has none") (#1953).
|
||||
*/
|
||||
/**
|
||||
* Entries the client dropped from this list because they failed the MCP
|
||||
* schema. Rendered as a warning above the list, which still shows the rest
|
||||
* (#1909).
|
||||
*/
|
||||
malformedListItems?: MalformedListItem[];
|
||||
loadError?: Error | null;
|
||||
/** Pagination controls (#1721). */
|
||||
pagination: ListPaginationControlsProps;
|
||||
|
|
@ -54,6 +62,7 @@ export function PromptControls({
|
|||
searchText = "",
|
||||
listChanged,
|
||||
onRefreshList,
|
||||
malformedListItems = [],
|
||||
loadError,
|
||||
pagination,
|
||||
onSearchChange,
|
||||
|
|
@ -83,6 +92,11 @@ export function PromptControls({
|
|||
/>
|
||||
<ListPaginationControls {...pagination} />
|
||||
<ListLoadError error={loadError} what="prompts" onRetry={onRefreshList} />
|
||||
<MalformedItemsWarning
|
||||
items={malformedListItems}
|
||||
method="prompts/list"
|
||||
what="prompts"
|
||||
/>
|
||||
<ListScroll viewportRef={viewportRef}>
|
||||
<Stack gap="xs">
|
||||
{filteredPrompts.map((prompt) => (
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ import { isModernEra } from "../../elements/EraBadge/eraUtils";
|
|||
import { SubscriptionStreamBadge } from "../../elements/SubscriptionStreamBadge/SubscriptionStreamBadge";
|
||||
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
||||
import { ListLoadError } from "../../elements/ListLoadError/ListLoadError";
|
||||
import { MalformedItemsWarning } from "../../elements/MalformedItemsWarning/MalformedItemsWarning";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import {
|
||||
ListPaginationControls,
|
||||
type ListPaginationControlsProps,
|
||||
|
|
@ -73,6 +75,12 @@ export interface ResourceControlsProps {
|
|||
* A failed list load, surfaced above the list instead of leaving the panel
|
||||
* empty (which reads as "this server has none") (#1953).
|
||||
*/
|
||||
/**
|
||||
* Entries the client dropped from `resources/list` or
|
||||
* `resources/templates/list` because they failed the MCP schema. Rendered as
|
||||
* a warning above the list, which still shows the rest (#1909).
|
||||
*/
|
||||
malformedListItems?: MalformedListItem[];
|
||||
loadError?: Error | null;
|
||||
/** Pagination controls for the Resources list (#1721). */
|
||||
pagination: ListPaginationControlsProps;
|
||||
|
|
@ -117,6 +125,7 @@ export function ResourceControls({
|
|||
openSections: controlledOpenSections,
|
||||
listChanged,
|
||||
onRefreshList,
|
||||
malformedListItems = [],
|
||||
loadError,
|
||||
pagination,
|
||||
onSearchChange,
|
||||
|
|
@ -248,6 +257,16 @@ export function ResourceControls({
|
|||
what="resources"
|
||||
onRetry={onRefreshList}
|
||||
/>
|
||||
<MalformedItemsWarning
|
||||
items={malformedListItems}
|
||||
method="resources/list"
|
||||
what="resources"
|
||||
/>
|
||||
<MalformedItemsWarning
|
||||
items={malformedListItems}
|
||||
method="resources/templates/list"
|
||||
what="resource templates"
|
||||
/>
|
||||
{/* Stays inline: Accordion is a compound, `multiple`-discriminated generic,
|
||||
so `.withProps({ multiple: true, ... })` loses its JSX call signature
|
||||
(same tooling limit as Box). */}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import type { Tool } from "@modelcontextprotocol/client";
|
|||
import type { ExcludedTool } from "@inspector/core/mcp/types.js";
|
||||
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
||||
import { ListLoadError } from "../../elements/ListLoadError/ListLoadError";
|
||||
import { MalformedItemsWarning } from "../../elements/MalformedItemsWarning/MalformedItemsWarning";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import {
|
||||
ListPaginationControls,
|
||||
type ListPaginationControlsProps,
|
||||
|
|
@ -37,6 +39,12 @@ export interface ToolControlsProps {
|
|||
* A failed list load, surfaced above the list instead of leaving the panel
|
||||
* empty (which reads as "this server has none") (#1953).
|
||||
*/
|
||||
/**
|
||||
* Entries the client dropped from this list because they failed the MCP
|
||||
* schema. Rendered as a warning above the list, which still shows the rest
|
||||
* (#1909).
|
||||
*/
|
||||
malformedListItems?: MalformedListItem[];
|
||||
loadError?: Error | null;
|
||||
/** Pagination controls (#1721). */
|
||||
pagination: ListPaginationControlsProps;
|
||||
|
|
@ -127,6 +135,7 @@ export function ToolControls({
|
|||
searchText = "",
|
||||
listChanged,
|
||||
onRefreshList,
|
||||
malformedListItems = [],
|
||||
loadError,
|
||||
pagination,
|
||||
onSearchChange,
|
||||
|
|
@ -163,6 +172,11 @@ export function ToolControls({
|
|||
/>
|
||||
<ListPaginationControls {...pagination} />
|
||||
<ListLoadError error={loadError} what="tools" onRetry={onRefreshList} />
|
||||
<MalformedItemsWarning
|
||||
items={malformedListItems}
|
||||
method="tools/list"
|
||||
what="tools"
|
||||
/>
|
||||
<SidebarScroll viewportRef={viewportRef}>
|
||||
<Stack gap="xs">
|
||||
{filteredTools.map(({ tool, key }) => (
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
Stack,
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import type { GetPromptResult, Prompt } from "@modelcontextprotocol/client";
|
||||
import { PromptControls } from "../../groups/PromptControls/PromptControls";
|
||||
import type { ListPaginationControlsProps } from "../../elements/ListPaginationControls/ListPaginationControls";
|
||||
|
|
@ -34,6 +35,11 @@ export interface PromptsScreenProps {
|
|||
onUiChange: (next: PromptsUiState) => void;
|
||||
onRefreshList: () => void;
|
||||
/** A failed list load, rendered above the sidebar list (#1953). */
|
||||
/**
|
||||
* Entries dropped from this screen's list result(s) as malformed; the list
|
||||
* panel warns about them above the list (#1909).
|
||||
*/
|
||||
malformedListItems?: MalformedListItem[];
|
||||
loadError?: Error | null;
|
||||
/** Pagination controls rendered in the sidebar (#1721). */
|
||||
pagination: ListPaginationControlsProps;
|
||||
|
|
@ -138,6 +144,7 @@ export function PromptsScreen({
|
|||
completionsSupported,
|
||||
onUiChange,
|
||||
onRefreshList,
|
||||
malformedListItems,
|
||||
loadError,
|
||||
pagination,
|
||||
onGetPrompt,
|
||||
|
|
@ -277,6 +284,7 @@ export function PromptsScreen({
|
|||
listChanged={listChanged}
|
||||
onRefreshList={onRefreshList}
|
||||
loadError={loadError}
|
||||
malformedListItems={malformedListItems}
|
||||
pagination={pagination}
|
||||
onSearchChange={(value) => onUiChange({ ...ui, search: value })}
|
||||
onSelectPrompt={handleSelectPrompt}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {
|
|||
Stack,
|
||||
Text,
|
||||
} from "@mantine/core";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import type {
|
||||
ProtocolEra,
|
||||
ReadResourceResult,
|
||||
|
|
@ -58,6 +59,11 @@ export interface ResourcesScreenProps {
|
|||
onUiChange: (next: ResourcesUiState) => void;
|
||||
onRefreshList: () => void;
|
||||
/** A failed list load, rendered above the sidebar list (#1953). */
|
||||
/**
|
||||
* Entries dropped from this screen's list result(s) as malformed; the list
|
||||
* panel warns about them above the list (#1909).
|
||||
*/
|
||||
malformedListItems?: MalformedListItem[];
|
||||
loadError?: Error | null;
|
||||
/** Pagination controls rendered in the sidebar (#1721). */
|
||||
pagination: ListPaginationControlsProps;
|
||||
|
|
@ -172,6 +178,7 @@ export function ResourcesScreen({
|
|||
subscriptionsSupported = true,
|
||||
onUiChange,
|
||||
onRefreshList,
|
||||
malformedListItems,
|
||||
loadError,
|
||||
pagination,
|
||||
onReadResource,
|
||||
|
|
@ -329,6 +336,7 @@ export function ResourcesScreen({
|
|||
listChanged={listChanged}
|
||||
onRefreshList={onRefreshList}
|
||||
loadError={loadError}
|
||||
malformedListItems={malformedListItems}
|
||||
pagination={pagination}
|
||||
onSearchChange={(value) => onUiChange({ ...ui, search: value })}
|
||||
onOpenSectionsChange={(value) =>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { Card, Flex, Stack, Text } from "@mantine/core";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import type {
|
||||
CallToolResult,
|
||||
ReadResourceResult,
|
||||
|
|
@ -50,6 +51,11 @@ export interface ToolsScreenProps {
|
|||
/** Tools the SDK excluded from `tools/list` for invalid `x-mcp-header`
|
||||
* annotations (SEP-2243), shown in the sidebar with the reason (#1632). */
|
||||
excludedTools?: ExcludedTool[];
|
||||
/**
|
||||
* Entries dropped from a list result as malformed, across every list method.
|
||||
* Each list panel filters for its own and warns about them (#1909).
|
||||
*/
|
||||
malformedListItems?: MalformedListItem[];
|
||||
callState?: ToolCallState;
|
||||
ui: ToolsUiState;
|
||||
listChanged: boolean;
|
||||
|
|
@ -147,6 +153,7 @@ const EmptyState = Text.withProps({
|
|||
export function ToolsScreen({
|
||||
tools,
|
||||
excludedTools,
|
||||
malformedListItems,
|
||||
callState,
|
||||
ui,
|
||||
listChanged,
|
||||
|
|
@ -191,6 +198,7 @@ export function ToolsScreen({
|
|||
<ToolControls
|
||||
tools={tools}
|
||||
excludedTools={excludedTools}
|
||||
malformedListItems={malformedListItems}
|
||||
selectedName={selectedToolName}
|
||||
searchText={search}
|
||||
listChanged={listChanged}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
type ReactNode,
|
||||
type Ref,
|
||||
} from "react";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import type { DeepLink, DeepLinkParseStatus } from "../../../utils/deepLink";
|
||||
import {
|
||||
AppShell,
|
||||
|
|
@ -418,6 +419,11 @@ export interface InspectorViewProps {
|
|||
/** Tools excluded from `tools/list` for invalid `x-mcp-header` annotations
|
||||
* (SEP-2243); shown in the Tools sidebar with the reason (#1632). */
|
||||
excludedTools?: ExcludedTool[];
|
||||
/**
|
||||
* Entries dropped from a list result as malformed, across every list method.
|
||||
* Each list panel filters for its own and warns about them (#1909).
|
||||
*/
|
||||
malformedListItems?: MalformedListItem[];
|
||||
prompts: Prompt[];
|
||||
resources: Resource[];
|
||||
resourceTemplates: ResourceTemplate[];
|
||||
|
|
@ -632,6 +638,7 @@ export function InspectorView({
|
|||
latencyMs,
|
||||
tools,
|
||||
excludedTools = [],
|
||||
malformedListItems = [],
|
||||
prompts,
|
||||
resources,
|
||||
resourceTemplates,
|
||||
|
|
@ -1365,6 +1372,7 @@ export function InspectorView({
|
|||
<ToolsScreen
|
||||
tools={tools}
|
||||
excludedTools={excludedTools}
|
||||
malformedListItems={malformedListItems}
|
||||
callState={toolCallState}
|
||||
ui={toolsUi}
|
||||
listChanged={toolsListChanged}
|
||||
|
|
@ -1405,6 +1413,7 @@ export function InspectorView({
|
|||
<ScreenStage active={activeTab === "Prompts"}>
|
||||
<PromptsScreen
|
||||
prompts={prompts}
|
||||
malformedListItems={malformedListItems}
|
||||
getPromptState={getPromptState}
|
||||
ui={promptsUi}
|
||||
listChanged={promptsListChanged}
|
||||
|
|
@ -1421,6 +1430,7 @@ export function InspectorView({
|
|||
<ScreenStage active={activeTab === "Resources"}>
|
||||
<ResourcesScreen
|
||||
resources={resources}
|
||||
malformedListItems={malformedListItems}
|
||||
templates={resourceTemplates}
|
||||
subscriptions={subscriptions}
|
||||
subscriptionStreamState={subscriptionStreamState}
|
||||
|
|
|
|||
Loading…
Reference in a new issue