Basic support for roots

This commit is contained in:
Justin Spahr-Summers
2024-11-07 14:11:48 +00:00
parent 645f2e942e
commit f3406ca43d
2 changed files with 112 additions and 0 deletions

View File

@@ -10,11 +10,13 @@ import {
ListPromptsResultSchema,
ListResourcesResultSchema,
ListResourceTemplatesResultSchema,
ListRootsRequestSchema,
ListToolsResultSchema,
ProgressNotificationSchema,
ReadResourceResultSchema,
Resource,
ResourceTemplate,
Root,
ServerNotification,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
@@ -39,6 +41,7 @@ import {
Play,
Send,
Terminal,
FolderTree,
} from "lucide-react";
import { AnyZodObject } from "zod";
@@ -49,6 +52,7 @@ import PingTab from "./components/PingTab";
import PromptsTab, { Prompt } from "./components/PromptsTab";
import RequestsTab from "./components/RequestsTabs";
import ResourcesTab from "./components/ResourcesTab";
import RootsTab from "./components/RootsTab";
import SamplingTab, { PendingRequest } from "./components/SamplingTab";
import Sidebar from "./components/Sidebar";
import ToolsTab from "./components/ToolsTab";
@@ -86,6 +90,7 @@ const App = () => {
>([]);
const [mcpClient, setMcpClient] = useState<Client | null>(null);
const [notifications, setNotifications] = useState<ServerNotification[]>([]);
const [roots, setRoots] = useState<Root[]>([]);
const [pendingSampleRequests, setPendingSampleRequests] = useState<
Array<
@@ -254,6 +259,16 @@ const App = () => {
setToolResult(JSON.stringify(response.toolResult, null, 2));
};
const handleRootsChange = async () => {
if (mcpClient) {
try {
await mcpClient.sendRootsListChanged();
} catch (e) {
console.error("Failed to send roots list changed notification:", e);
}
}
};
const connectMcpServer = async () => {
try {
const client = new Client({
@@ -293,6 +308,10 @@ const App = () => {
});
});
client.setRequestHandler(ListRootsRequestSchema, async () => {
return { roots };
});
setMcpClient(client);
setConnectionStatus("connected");
} catch (e) {
@@ -387,6 +406,10 @@ const App = () => {
</span>
)}
</TabsTrigger>
<TabsTrigger value="roots">
<FolderTree className="w-4 h-4 mr-2" />
Roots
</TabsTrigger>
</TabsList>
<div className="w-full">
@@ -443,6 +466,11 @@ const App = () => {
onApprove={handleApproveSampling}
onReject={handleRejectSampling}
/>
<RootsTab
roots={roots}
setRoots={setRoots}
onRootsChange={handleRootsChange}
/>
</div>
</Tabs>
) : (

View File

@@ -0,0 +1,84 @@
import { Alert, AlertDescription } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { TabsContent } from "@/components/ui/tabs";
import { Root } from "@modelcontextprotocol/sdk/types.js";
import { Plus, Minus, Save } from "lucide-react";
import { useCallback } from "react";
const RootsTab = ({
roots,
setRoots,
onRootsChange,
}: {
roots: Root[];
setRoots: React.Dispatch<React.SetStateAction<Root[]>>;
onRootsChange: () => void;
}) => {
const addRoot = useCallback(() => {
setRoots((currentRoots) => [...currentRoots, { uri: "file://", name: "" }]);
}, [setRoots]);
const removeRoot = useCallback(
(index: number) => {
setRoots((currentRoots) => currentRoots.filter((_, i) => i !== index));
},
[setRoots],
);
const updateRoot = useCallback(
(index: number, field: keyof Root, value: string) => {
setRoots((currentRoots) =>
currentRoots.map((root, i) =>
i === index ? { ...root, [field]: value } : root,
),
);
},
[setRoots],
);
const handleSave = useCallback(() => {
onRootsChange();
}, [onRootsChange]);
return (
<TabsContent value="roots" className="space-y-4">
<Alert>
<AlertDescription>
Configure the root directories that the server can access
</AlertDescription>
</Alert>
{roots.map((root, index) => (
<div key={index} className="flex gap-2 items-center">
<Input
placeholder="file:// URI"
value={root.uri}
onChange={(e) => updateRoot(index, "uri", e.target.value)}
className="flex-1"
/>
<Button
variant="destructive"
size="sm"
onClick={() => removeRoot(index)}
>
<Minus className="h-4 w-4" />
</Button>
</div>
))}
<div className="flex gap-2">
<Button variant="outline" onClick={addRoot}>
<Plus className="h-4 w-4 mr-2" />
Add Root
</Button>
<Button onClick={handleSave}>
<Save className="h-4 w-4 mr-2" />
Save Changes
</Button>
</div>
</TabsContent>
);
};
export default RootsTab;