Merge pull request #105 from devin-open-source/devin/1733551277-capability-negotiation

feat: implement capability negotiation for UI tabs
This commit is contained in:
Ashwin Bhat
2024-12-09 03:56:58 -08:00
committed by GitHub

View File

@@ -19,10 +19,11 @@ import {
Request, Request,
Resource, Resource,
ResourceTemplate, ResourceTemplate,
Result,
Root, Root,
ServerNotification, ServerNotification,
Tool, Tool,
ServerCapabilitiesSchema,
Result,
} from "@modelcontextprotocol/sdk/types.js"; } from "@modelcontextprotocol/sdk/types.js";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
@@ -43,7 +44,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { ZodType } from "zod"; import { z, type ZodType } from "zod";
import "./App.css"; import "./App.css";
import ConsoleTab from "./components/ConsoleTab"; import ConsoleTab from "./components/ConsoleTab";
import HistoryAndNotifications from "./components/History"; import HistoryAndNotifications from "./components/History";
@@ -55,6 +56,8 @@ import SamplingTab, { PendingRequest } from "./components/SamplingTab";
import Sidebar from "./components/Sidebar"; import Sidebar from "./components/Sidebar";
import ToolsTab from "./components/ToolsTab"; import ToolsTab from "./components/ToolsTab";
type ServerCapabilities = z.infer<typeof ServerCapabilitiesSchema>;
const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000; const DEFAULT_REQUEST_TIMEOUT_MSEC = 10000;
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
@@ -67,6 +70,7 @@ const App = () => {
const [connectionStatus, setConnectionStatus] = useState< const [connectionStatus, setConnectionStatus] = useState<
"disconnected" | "connected" | "error" "disconnected" | "connected" | "error"
>("disconnected"); >("disconnected");
const [serverCapabilities, setServerCapabilities] = useState<ServerCapabilities | null>(null);
const [resources, setResources] = useState<Resource[]>([]); const [resources, setResources] = useState<Resource[]>([]);
const [resourceTemplates, setResourceTemplates] = useState< const [resourceTemplates, setResourceTemplates] = useState<
ResourceTemplate[] ResourceTemplate[]
@@ -455,6 +459,9 @@ const App = () => {
await client.connect(clientTransport); await client.connect(clientTransport);
const capabilities = client.getServerCapabilities();
setServerCapabilities(capabilities ?? null);
client.setRequestHandler(CreateMessageRequestSchema, (request) => { client.setRequestHandler(CreateMessageRequestSchema, (request) => {
return new Promise<CreateMessageResult>((resolve, reject) => { return new Promise<CreateMessageResult>((resolve, reject) => {
setPendingSampleRequests((prev) => [ setPendingSampleRequests((prev) => [
@@ -497,20 +504,27 @@ const App = () => {
<div className="flex-1 overflow-auto"> <div className="flex-1 overflow-auto">
{mcpClient ? ( {mcpClient ? (
<Tabs <Tabs
defaultValue={window.location.hash.slice(1) || "resources"} defaultValue={
Object.keys(serverCapabilities ?? {}).includes(window.location.hash.slice(1)) ?
window.location.hash.slice(1) :
serverCapabilities?.resources ? "resources" :
serverCapabilities?.prompts ? "prompts" :
serverCapabilities?.tools ? "tools" :
"ping"
}
className="w-full p-4" className="w-full p-4"
onValueChange={(value) => (window.location.hash = value)} onValueChange={(value) => (window.location.hash = value)}
> >
<TabsList className="mb-4 p-0"> <TabsList className="mb-4 p-0">
<TabsTrigger value="resources"> <TabsTrigger value="resources" disabled={!serverCapabilities?.resources}>
<Files className="w-4 h-4 mr-2" /> <Files className="w-4 h-4 mr-2" />
Resources Resources
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="prompts"> <TabsTrigger value="prompts" disabled={!serverCapabilities?.prompts}>
<MessageSquare className="w-4 h-4 mr-2" /> <MessageSquare className="w-4 h-4 mr-2" />
Prompts Prompts
</TabsTrigger> </TabsTrigger>
<TabsTrigger value="tools"> <TabsTrigger value="tools" disabled={!serverCapabilities?.tools}>
<Hammer className="w-4 h-4 mr-2" /> <Hammer className="w-4 h-4 mr-2" />
Tools Tools
</TabsTrigger> </TabsTrigger>
@@ -534,6 +548,14 @@ const App = () => {
</TabsList> </TabsList>
<div className="w-full"> <div className="w-full">
{!serverCapabilities?.resources && !serverCapabilities?.prompts && !serverCapabilities?.tools ? (
<div className="flex items-center justify-center p-4">
<p className="text-lg text-gray-500">
The connected server does not support any MCP capabilities
</p>
</div>
) : (
<>
<ResourcesTab <ResourcesTab
resources={resources} resources={resources}
resourceTemplates={resourceTemplates} resourceTemplates={resourceTemplates}
@@ -635,6 +657,8 @@ const App = () => {
setRoots={setRoots} setRoots={setRoots}
onRootsChange={handleRootsChange} onRootsChange={handleRootsChange}
/> />
</>
)}
</div> </div>
</Tabs> </Tabs>
) : ( ) : (