import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Combobox } from "@/components/ui/combobox"; import { TabsContent } from "@/components/ui/tabs"; import { ListResourcesResult, Resource, ResourceTemplate, ListResourceTemplatesResult, ResourceReference, PromptReference, } from "@modelcontextprotocol/sdk/types.js"; import { AlertCircle, ChevronRight, FileText, RefreshCw } from "lucide-react"; import ListPane from "./ListPane"; import { useEffect, useState } from "react"; import { useCompletionState } from "@/lib/hooks/useCompletionState"; import JsonView from "./JsonView"; const ResourcesTab = ({ resources, resourceTemplates, listResources, clearResources, listResourceTemplates, clearResourceTemplates, readResource, selectedResource, setSelectedResource, resourceSubscriptionsSupported, resourceSubscriptions, subscribeToResource, unsubscribeFromResource, handleCompletion, completionsSupported, resourceContent, nextCursor, nextTemplateCursor, error, }: { resources: Resource[]; resourceTemplates: ResourceTemplate[]; listResources: () => void; clearResources: () => void; listResourceTemplates: () => void; clearResourceTemplates: () => void; readResource: (uri: string) => void; selectedResource: Resource | null; setSelectedResource: (resource: Resource | null) => void; handleCompletion: ( ref: ResourceReference | PromptReference, argName: string, value: string, ) => Promise; completionsSupported: boolean; resourceContent: string; nextCursor: ListResourcesResult["nextCursor"]; nextTemplateCursor: ListResourceTemplatesResult["nextCursor"]; error: string | null; resourceSubscriptionsSupported: boolean; resourceSubscriptions: Set; subscribeToResource: (uri: string) => void; unsubscribeFromResource: (uri: string) => void; }) => { const [selectedTemplate, setSelectedTemplate] = useState(null); const [templateValues, setTemplateValues] = useState>( {}, ); const { completions, clearCompletions, requestCompletions } = useCompletionState(handleCompletion, completionsSupported); useEffect(() => { clearCompletions(); }, [clearCompletions]); const fillTemplate = ( template: string, values: Record, ): string => { return template.replace( /{([^}]+)}/g, (_, key) => values[key] || `{${key}}`, ); }; const handleTemplateValueChange = async (key: string, value: string) => { setTemplateValues((prev) => ({ ...prev, [key]: value })); if (selectedTemplate?.uriTemplate) { requestCompletions( { type: "ref/resource", uri: selectedTemplate.uriTemplate, }, key, value, ); } }; const handleReadTemplateResource = () => { if (selectedTemplate) { const uri = fillTemplate(selectedTemplate.uriTemplate, templateValues); readResource(uri); setSelectedTemplate(null); // We don't have the full Resource object here, so we create a partial one setSelectedResource({ uri, name: uri } as Resource); } }; return (
{ setSelectedResource(resource); readResource(resource.uri); setSelectedTemplate(null); }} renderItem={(resource) => (
{resource.name}
)} title="Resources" buttonText={nextCursor ? "List More Resources" : "List Resources"} isButtonDisabled={!nextCursor && resources.length > 0} /> { setSelectedTemplate(template); setSelectedResource(null); setTemplateValues({}); }} renderItem={(template) => (
{template.name}
)} title="Resource Templates" buttonText={ nextTemplateCursor ? "List More Templates" : "List Templates" } isButtonDisabled={!nextTemplateCursor && resourceTemplates.length > 0} />

{selectedResource ? selectedResource.name : selectedTemplate ? selectedTemplate.name : "Select a resource or template"}

{selectedResource && (
{resourceSubscriptionsSupported && !resourceSubscriptions.has(selectedResource.uri) && ( )} {resourceSubscriptionsSupported && resourceSubscriptions.has(selectedResource.uri) && ( )}
)}
{error ? ( Error {error} ) : selectedResource ? ( ) : selectedTemplate ? (

{selectedTemplate.description}

{selectedTemplate.uriTemplate .match(/{([^}]+)}/g) ?.map((param) => { const key = param.slice(1, -1); return (
handleTemplateValueChange(key, value) } onInputChange={(value) => handleTemplateValueChange(key, value) } options={completions[key] || []} />
); })}
) : ( Select a resource or template from the list to view its contents )}
); }; export default ResourcesTab;