diff --git a/client/src/App.tsx b/client/src/App.tsx index a9adea5..e902da9 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -128,6 +128,10 @@ const App = () => { const [selectedResource, setSelectedResource] = useState( null, ); + const [resourceSubscriptions, setResourceSubscriptions] = useState< + Set + >(new Set()); + const [selectedPrompt, setSelectedPrompt] = useState(null); const [selectedTool, setSelectedTool] = useState(null); const [nextResourceCursor, setNextResourceCursor] = useState< @@ -308,6 +312,38 @@ const App = () => { setResourceContent(JSON.stringify(response, null, 2)); }; + const subscribeToResource = async (uri: string) => { + if (!resourceSubscriptions.has(uri)) { + await makeRequest( + { + method: "resources/subscribe" as const, + params: { uri }, + }, + z.object({}), + "resources", + ); + const clone = new Set(resourceSubscriptions); + clone.add(uri); + setResourceSubscriptions(clone); + } + }; + + const unsubscribeFromResource = async (uri: string) => { + if (resourceSubscriptions.has(uri)) { + await makeRequest( + { + method: "resources/unsubscribe" as const, + params: { uri }, + }, + z.object({}), + "resources", + ); + const clone = new Set(resourceSubscriptions); + clone.delete(uri); + setResourceSubscriptions(clone); + } + }; + const listPrompts = async () => { const response = await makeRequest( { @@ -485,6 +521,18 @@ const App = () => { clearError("resources"); setSelectedResource(resource); }} + resourceSubscriptionsSupported={ + serverCapabilities?.resources?.subscribe || false + } + resourceSubscriptions={resourceSubscriptions} + subscribeToResource={(uri) => { + clearError("resources"); + subscribeToResource(uri); + }} + unsubscribeFromResource={(uri) => { + clearError("resources"); + unsubscribeFromResource(uri); + }} handleCompletion={handleCompletion} completionsSupported={completionsSupported} resourceContent={resourceContent} diff --git a/client/src/components/ResourcesTab.tsx b/client/src/components/ResourcesTab.tsx index 93127a9..f000840 100644 --- a/client/src/components/ResourcesTab.tsx +++ b/client/src/components/ResourcesTab.tsx @@ -26,6 +26,10 @@ const ResourcesTab = ({ readResource, selectedResource, setSelectedResource, + resourceSubscriptionsSupported, + resourceSubscriptions, + subscribeToResource, + unsubscribeFromResource, handleCompletion, completionsSupported, resourceContent, @@ -52,6 +56,10 @@ const ResourcesTab = ({ 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); @@ -164,14 +172,38 @@ const ResourcesTab = ({ : "Select a resource or template"} {selectedResource && ( - +
+ {resourceSubscriptionsSupported && + !resourceSubscriptions.has(selectedResource.uri) && ( + + )} + {resourceSubscriptionsSupported && + resourceSubscriptions.has(selectedResource.uri) && ( + + )} + +
)}
diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index 75b5467..9e7bb11 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -9,6 +9,7 @@ import { CreateMessageRequestSchema, ListRootsRequestSchema, ProgressNotificationSchema, + ResourceUpdatedNotificationSchema, Request, Result, ServerCapabilities, @@ -247,6 +248,11 @@ export function useConnection({ ProgressNotificationSchema, onNotification, ); + + client.setNotificationHandler( + ResourceUpdatedNotificationSchema, + onNotification, + ); } if (onStdErrNotification) { diff --git a/client/src/lib/notificationTypes.ts b/client/src/lib/notificationTypes.ts index 7aa6518..82c1fd8 100644 --- a/client/src/lib/notificationTypes.ts +++ b/client/src/lib/notificationTypes.ts @@ -1,6 +1,7 @@ import { NotificationSchema as BaseNotificationSchema, ClientNotificationSchema, + ServerNotificationSchema, } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; @@ -13,7 +14,7 @@ export const StdErrNotificationSchema = BaseNotificationSchema.extend({ export const NotificationSchema = ClientNotificationSchema.or( StdErrNotificationSchema, -); +).or(ServerNotificationSchema); export type StdErrNotification = z.infer; export type Notification = z.infer; diff --git a/package-lock.json b/package-lock.json index 550bb75..ed9bc96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,8 @@ "server" ], "dependencies": { - "@modelcontextprotocol/inspector-client": "0.4.1", - "@modelcontextprotocol/inspector-server": "0.4.1", + "@modelcontextprotocol/inspector-client": "^0.5.1", + "@modelcontextprotocol/inspector-server": "^0.5.1", "concurrently": "^9.0.1", "shell-quote": "^1.8.2", "spawn-rx": "^5.1.2", diff --git a/package.json b/package.json index 3de7ce4..b84fbd6 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,8 @@ "publish-all": "npm publish --workspaces --access public && npm publish --access public" }, "dependencies": { - "@modelcontextprotocol/inspector-client": "0.4.1", - "@modelcontextprotocol/inspector-server": "0.4.1", + "@modelcontextprotocol/inspector-client": "^0.5.1", + "@modelcontextprotocol/inspector-server": "^0.5.1", "concurrently": "^9.0.1", "shell-quote": "^1.8.2", "spawn-rx": "^5.1.2",