From a669272fda9f5b2af99bd9a0f2241e779fc947e1 Mon Sep 17 00:00:00 2001 From: cliffhall Date: Sat, 8 Mar 2025 13:40:37 -0500 Subject: [PATCH] Track subscribed resources and show the appropriate subscribe or unsubscribe button on selected resource panel. If the server does not support resource subscriptions, do not show any subscription buttons. * In App.tsx - useState for resourceSubscriptions, setResourceSubscriptions a Set of type string. - in subscribeToResource() - only make the request to subscribe if the uri is not in the resourceSubscriptions set - in unsubscribeFromResource() - only make the request to unsubscribe if the uri is in the resourceSubscriptions set - in ResourceTab element, - pass a boolean resourceSubscriptionsSupported as serverCapabilities.resources.subscribe - pass resourceSubscriptions as a prop * In ResourcesTab.tsx - deconstruct resourceSubscriptions and resourceSubscriptionsSupported from props and add prop type - in selected resource panel - don't show subscribe or unsubscribe buttons unless resourceSubscriptionsSupported is true - only show subscribe button if selected resource uri is not in resourceSubscriptions set - only show unsubscribe button if selected resource uri is in resourceSubscriptions set - wrap buttons in a flex div that is - justified right - has a minimal gap between - 2/5 wide (just big enough to contain two buttons and leave the h3 text 3/5 of the row to render and not overflow. --- client/src/App.tsx | 47 +++++++++++++++++--------- client/src/components/ResourcesTab.tsx | 13 +++++-- package-lock.json | 4 +-- package.json | 4 +-- 4 files changed, 45 insertions(+), 23 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index f5b1f79..382ae03 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -128,6 +128,8 @@ const App = () => { const [selectedResource, setSelectedResource] = useState( null, ); + const [resourceSubscriptions, setResourceSubscriptions] = useState>(new Set()); + const [selectedPrompt, setSelectedPrompt] = useState(null); const [selectedTool, setSelectedTool] = useState(null); const [nextResourceCursor, setNextResourceCursor] = useState< @@ -310,26 +312,37 @@ const App = () => { const subscribeToResource = async (uri: string) => { - await makeRequest( - { - method: "resources/subscribe" as const, - params: { uri }, - }, - z.object({}), - "resources", - ); + 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) => { - await makeRequest( - { - method: "resources/unsubscribe" as const, - params: { uri }, - }, - z.object({}), - "resources", - ); + 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); + } }; @@ -510,6 +523,8 @@ const App = () => { clearError("resources"); setSelectedResource(resource); }} + resourceSubscriptionsSupported={serverCapabilities?.resources?.subscribe || false} + resourceSubscriptions={resourceSubscriptions} subscribeToResource={(uri) => { clearError("resources"); subscribeToResource(uri); diff --git a/client/src/components/ResourcesTab.tsx b/client/src/components/ResourcesTab.tsx index 9d94296..317ec85 100644 --- a/client/src/components/ResourcesTab.tsx +++ b/client/src/components/ResourcesTab.tsx @@ -26,6 +26,8 @@ const ResourcesTab = ({ readResource, selectedResource, setSelectedResource, + resourceSubscriptionsSupported, + resourceSubscriptions, subscribeToResource, unsubscribeFromResource, handleCompletion, @@ -54,6 +56,8 @@ const ResourcesTab = ({ nextCursor: ListResourcesResult["nextCursor"]; nextTemplateCursor: ListResourceTemplatesResult["nextCursor"]; error: string | null; + resourceSubscriptionsSupported: boolean; + resourceSubscriptions: Set; subscribeToResource: (uri: string) => void; unsubscribeFromResource: (uri: string) => void; }) => { @@ -168,14 +172,16 @@ const ResourcesTab = ({ : "Select a resource or template"} {selectedResource && ( - <> - + } + { resourceSubscriptionsSupported && resourceSubscriptions.has(selectedResource.uri) && + } - + )}
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",