WIP: Subscribe to resources

* In App.tsx
  - added subscribeToResource()
    - takes a uri
    - sends a `resources/subscribe` message with the uri
  - added unsubscribeFromResource()
    - takes a uri
    - sends a `resources/unsubscribe` message with the uri
  - in ResourcesTab element,
    - pass subscribeToResource and subscribeToResource invokers to component
* In notificationTypes.ts
  - add ServerNotificationSchema to NotificationSchema to permit server update messages.

* In ResourcesTab.tsx
  - deconstruct subscribeToResource and unsubscribeFromResource and add prop types
  - Add Subscribe and Unsubscribe buttons to selected resource panel, left of the refresh button. They call the sub and unsub functions that came in on props, passing the selected resource URI.
  - [WIP]: Will show the appropriate button in a follow up commit.
* In useConnection.ts
  - import ResourceUpdatedNotificationSchema
  - in the connect function,
    - set onNotification as the handler for ResourceUpdatedNotificationSchema
This commit is contained in:
cliffhall
2025-03-08 11:05:13 -05:00
parent 014730fb2f
commit 747c0154c5
4 changed files with 71 additions and 11 deletions

View File

@@ -308,6 +308,31 @@ const App = () => {
setResourceContent(JSON.stringify(response, null, 2));
};
const subscribeToResource = async (uri: string) => {
await makeRequest(
{
method: "resources/subscribe" as const,
params: { uri },
},
z.object({}),
"resources",
);
};
const unsubscribeFromResource = async (uri: string) => {
await makeRequest(
{
method: "resources/unsubscribe" as const,
params: { uri },
},
z.object({}),
"resources",
);
};
const listPrompts = async () => {
const response = await makeRequest(
{
@@ -485,6 +510,14 @@ const App = () => {
clearError("resources");
setSelectedResource(resource);
}}
subscribeToResource={(uri) => {
clearError("resources");
subscribeToResource(uri);
}}
unsubscribeFromResource={(uri) => {
clearError("resources");
unsubscribeFromResource(uri);
}}
handleCompletion={handleCompletion}
completionsSupported={completionsSupported}
resourceContent={resourceContent}

View File

@@ -26,6 +26,8 @@ const ResourcesTab = ({
readResource,
selectedResource,
setSelectedResource,
subscribeToResource,
unsubscribeFromResource,
handleCompletion,
completionsSupported,
resourceContent,
@@ -52,6 +54,8 @@ const ResourcesTab = ({
nextCursor: ListResourcesResult["nextCursor"];
nextTemplateCursor: ListResourceTemplatesResult["nextCursor"];
error: string | null;
subscribeToResource: (uri: string) => void;
unsubscribeFromResource: (uri: string) => void;
}) => {
const [selectedTemplate, setSelectedTemplate] =
useState<ResourceTemplate | null>(null);
@@ -164,14 +168,30 @@ const ResourcesTab = ({
: "Select a resource or template"}
</h3>
{selectedResource && (
<Button
variant="outline"
size="sm"
onClick={() => readResource(selectedResource.uri)}
>
<RefreshCw className="w-4 h-4 mr-2" />
Refresh
</Button>
<>
<Button
variant="outline"
size="sm"
onClick={() => subscribeToResource(selectedResource.uri)}
>
Subscribe
</Button>
<Button
variant="outline"
size="sm"
onClick={() => unsubscribeFromResource(selectedResource.uri)}
>
Unsubscribe
</Button>
<Button
variant="outline"
size="sm"
onClick={() => readResource(selectedResource.uri)}
>
<RefreshCw className="w-4 h-4 mr-2" />
Refresh
</Button>
</>
)}
</div>
<div className="p-4">

View File

@@ -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) {

View File

@@ -1,6 +1,7 @@
import {
NotificationSchema as BaseNotificationSchema,
ClientNotificationSchema,
ServerNotificationSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
@@ -11,9 +12,9 @@ export const StdErrNotificationSchema = BaseNotificationSchema.extend({
}),
});
export const NotificationSchema = ClientNotificationSchema.or(
StdErrNotificationSchema,
);
export const NotificationSchema = ClientNotificationSchema
.or(StdErrNotificationSchema)
.or(ServerNotificationSchema);
export type StdErrNotification = z.infer<typeof StdErrNotificationSchema>;
export type Notification = z.infer<typeof NotificationSchema>;