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.
This commit is contained in:
cliffhall
2025-03-08 13:40:37 -05:00
parent 747c0154c5
commit a669272fda
4 changed files with 45 additions and 23 deletions

View File

@@ -128,6 +128,8 @@ const App = () => {
const [selectedResource, setSelectedResource] = useState<Resource | null>(
null,
);
const [resourceSubscriptions, setResourceSubscriptions] = useState<Set<string>>(new Set<string>());
const [selectedPrompt, setSelectedPrompt] = useState<Prompt | null>(null);
const [selectedTool, setSelectedTool] = useState<Tool | null>(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);