feat(completions): integrate MCP Completion support into Prompts and Resources tabs

- create useCompletion hook to fetch completions with debouncing and abort control
- Updated `PromptsTab.tsx` and `ResourcesTab.tsx` to utilize the `Combobox` component and `useCompletions` hook, enabling argument autocompletion for prompts and resource URIs as per the MCP specification.
- Added a combobox to show completions
This commit is contained in:
Gavin Aboulhosn
2024-12-16 14:28:02 -05:00
parent 6d930ecae7
commit 9b624e8c87
10 changed files with 1350 additions and 145 deletions

View File

@@ -1,16 +1,19 @@
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Combobox } from "@/components/ui/combobox";
import { TabsContent } from "@/components/ui/tabs";
import {
ListResourcesResult,
Resource,
ResourceTemplate,
ListResourceTemplatesResult,
ResourceReference,
} from "@modelcontextprotocol/sdk/types.js";
import { AlertCircle, ChevronRight, FileText, RefreshCw } from "lucide-react";
import ListPane from "./ListPane";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useCompletions } from "@/lib/useCompletion";
const ResourcesTab = ({
resources,
@@ -22,6 +25,7 @@ const ResourcesTab = ({
readResource,
selectedResource,
setSelectedResource,
onComplete,
resourceContent,
nextCursor,
nextTemplateCursor,
@@ -36,6 +40,11 @@ const ResourcesTab = ({
readResource: (uri: string) => void;
selectedResource: Resource | null;
setSelectedResource: (resource: Resource | null) => void;
onComplete: (
ref: ResourceReference,
argName: string,
value: string,
) => Promise<string[]>;
resourceContent: string;
nextCursor: ListResourcesResult["nextCursor"];
nextTemplateCursor: ListResourceTemplatesResult["nextCursor"];
@@ -47,6 +56,14 @@ const ResourcesTab = ({
{},
);
const { clearCompletions, completions, requestCompletions } = useCompletions({
onComplete,
});
useEffect(() => {
clearCompletions();
}, [clearCompletions]);
const fillTemplate = (
template: string,
values: Record<string, string>,
@@ -57,6 +74,21 @@ const ResourcesTab = ({
);
};
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);
@@ -162,22 +194,18 @@ const ResourcesTab = ({
const key = param.slice(1, -1);
return (
<div key={key}>
<label
htmlFor={key}
className="block text-sm font-medium text-gray-700"
>
{key}
</label>
<Input
<Label htmlFor={key}>{key}</Label>
<Combobox
id={key}
placeholder={`Enter ${key}`}
value={templateValues[key] || ""}
onChange={(e) =>
setTemplateValues({
...templateValues,
[key]: e.target.value,
})
onChange={(value) =>
handleTemplateValueChange(key, value)
}
className="mt-1"
onInputChange={(value) =>
handleTemplateValueChange(key, value)
}
options={completions[key] || []}
/>
</div>
);