Phase 2: Refactor Resources cluster to MCP SDK types

Replace ad hoc local types with MCP SDK schema types across the Resources
group components and ResourcesScreen, per the v2 UX interfaces plan.

- ResourceListItem: accept `resource: Resource | ResourceTemplate` instead
  of flat `name`/`uri`; render `title ?? name`; delete local
  `ResourceAnnotations` type
- ResourceControls: accept `Resource[]`, `ResourceTemplate[]`,
  `InspectorResourceSubscription[]` + `selectedUri`/`selectedTemplate`
  instead of `ResourceItem[]`/`TemplateListItem[]`/`SubscriptionItem[]`;
  no-op on re-selecting
- ResourcePreviewPanel: accept `resource: Resource` +
  `contents: (TextResourceContents | BlobResourceContents)[]` instead of
  flat `content: string`; use SDK `Annotations` (audience as Role[]);
  `lastUpdated` as `Date` not `string`
- ResourceTemplatePanel: accept `template: ResourceTemplate` instead of
  flat props; use SDK `Annotations`
- ResourceSubscribedItem: accept `subscription: InspectorResourceSubscription`
  instead of flat `name`/`lastUpdated`; `lastUpdated` as `Date`
- ResourcesScreen: accept `Resource[]`, `ResourceTemplate[]`,
  `InspectorResourceSubscription[]` + `selectedResourceUri`/
  `selectedTemplateUri` + `ReadResourceState`; derive selections via
  `find()`; add loading/error states
- Delete local `ResourceItem`, `TemplateListItem`, `SubscriptionItem`,
  `SelectedResource`, `SelectedTemplate`, `ResourceAnnotations` types
- Update all stories with real SDK fixtures
- Update ConnectedView ResourcesActive story

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-04-18 18:34:32 -04:00
commit d817c46911
2 changed files with 465 additions and 616 deletions

View file

@ -15,34 +15,42 @@ type Story = StoryObj<typeof ResourceTemplatePanel>;
export const SingleVariable: Story = {
args: {
name: "User Profile",
uriTemplate: "file:///users/{userId}/profile",
description: "Fetch a user profile by their unique identifier.",
template: {
name: "User Profile",
uriTemplate: "file:///users/{userId}/profile",
description: "Fetch a user profile by their unique identifier.",
},
},
};
export const MultipleVariables: Story = {
args: {
name: "Table Row",
title: "Database Table Row",
uriTemplate: "db://tables/{tableName}/rows/{rowId}",
description: "Access a specific row in a database table.",
template: {
name: "Table Row",
title: "Database Table Row",
uriTemplate: "db://tables/{tableName}/rows/{rowId}",
description: "Access a specific row in a database table.",
},
},
};
export const WithAnnotations: Story = {
args: {
name: "Dynamic Text Resource",
uriTemplate: "resource://dynamic/{resourceId}",
description:
"Plaintext dynamic resource fabricated from the {resourceId} variable, which must be an integer.",
annotations: { audience: "developer", priority: 0.8 },
template: {
name: "Dynamic Text Resource",
uriTemplate: "resource://dynamic/{resourceId}",
description:
"Plaintext dynamic resource fabricated from the {resourceId} variable, which must be an integer.",
annotations: { audience: ["user"], priority: 0.8 },
},
},
};
export const NoDescription: Story = {
args: {
name: "Simple Template",
uriTemplate: "file:///data/{filename}",
template: {
name: "Simple Template",
uriTemplate: "file:///data/{filename}",
},
},
};

View file

@ -1,14 +1,11 @@
import { useState, useMemo } from "react";
import { Button, Group, Stack, Text, TextInput, Title } from "@mantine/core";
import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js";
import { AnnotationBadge } from "../../elements/AnnotationBadge/AnnotationBadge";
import { CopyButton } from "../../elements/CopyButton/CopyButton";
export interface ResourceTemplatePanelProps {
name: string;
title?: string;
uriTemplate: string;
description?: string;
annotations?: { audience?: string; priority?: number };
template: ResourceTemplate;
onReadResource: (uri: string) => void;
}
@ -70,13 +67,11 @@ const AnnotationGroup = Group.withProps({
});
export function ResourceTemplatePanel({
name,
title,
uriTemplate,
description,
annotations,
template,
onReadResource,
}: ResourceTemplatePanelProps) {
const { name, title, uriTemplate, description, annotations } = template;
const variableNames = useMemo(
() => parseVariableNames(uriTemplate),
[uriTemplate],
@ -122,12 +117,7 @@ export function ResourceTemplatePanel({
<FooterRow>
<AnnotationGroup>
{annotations?.audience && (
<AnnotationBadge
facet="audience"
value={
annotations.audience.split(", ") as ("user" | "assistant")[]
}
/>
<AnnotationBadge facet="audience" value={annotations.audience} />
)}
{annotations?.priority !== undefined && (
<AnnotationBadge facet="priority" value={annotations.priority} />