Skip to content

Commit 747c015

Browse files
committed
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
1 parent 014730f commit 747c015

File tree

4 files changed

+71
-11
lines changed

4 files changed

+71
-11
lines changed

client/src/App.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,31 @@ const App = () => {
308308
setResourceContent(JSON.stringify(response, null, 2));
309309
};
310310

311+
const subscribeToResource = async (uri: string) => {
312+
313+
await makeRequest(
314+
{
315+
method: "resources/subscribe" as const,
316+
params: { uri },
317+
},
318+
z.object({}),
319+
"resources",
320+
);
321+
};
322+
323+
const unsubscribeFromResource = async (uri: string) => {
324+
325+
await makeRequest(
326+
{
327+
method: "resources/unsubscribe" as const,
328+
params: { uri },
329+
},
330+
z.object({}),
331+
"resources",
332+
);
333+
};
334+
335+
311336
const listPrompts = async () => {
312337
const response = await makeRequest(
313338
{
@@ -485,6 +510,14 @@ const App = () => {
485510
clearError("resources");
486511
setSelectedResource(resource);
487512
}}
513+
subscribeToResource={(uri) => {
514+
clearError("resources");
515+
subscribeToResource(uri);
516+
}}
517+
unsubscribeFromResource={(uri) => {
518+
clearError("resources");
519+
unsubscribeFromResource(uri);
520+
}}
488521
handleCompletion={handleCompletion}
489522
completionsSupported={completionsSupported}
490523
resourceContent={resourceContent}

client/src/components/ResourcesTab.tsx

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const ResourcesTab = ({
2626
readResource,
2727
selectedResource,
2828
setSelectedResource,
29+
subscribeToResource,
30+
unsubscribeFromResource,
2931
handleCompletion,
3032
completionsSupported,
3133
resourceContent,
@@ -52,6 +54,8 @@ const ResourcesTab = ({
5254
nextCursor: ListResourcesResult["nextCursor"];
5355
nextTemplateCursor: ListResourceTemplatesResult["nextCursor"];
5456
error: string | null;
57+
subscribeToResource: (uri: string) => void;
58+
unsubscribeFromResource: (uri: string) => void;
5559
}) => {
5660
const [selectedTemplate, setSelectedTemplate] =
5761
useState<ResourceTemplate | null>(null);
@@ -164,14 +168,30 @@ const ResourcesTab = ({
164168
: "Select a resource or template"}
165169
</h3>
166170
{selectedResource && (
167-
<Button
168-
variant="outline"
169-
size="sm"
170-
onClick={() => readResource(selectedResource.uri)}
171-
>
172-
<RefreshCw className="w-4 h-4 mr-2" />
173-
Refresh
174-
</Button>
171+
<>
172+
<Button
173+
variant="outline"
174+
size="sm"
175+
onClick={() => subscribeToResource(selectedResource.uri)}
176+
>
177+
Subscribe
178+
</Button>
179+
<Button
180+
variant="outline"
181+
size="sm"
182+
onClick={() => unsubscribeFromResource(selectedResource.uri)}
183+
>
184+
Unsubscribe
185+
</Button>
186+
<Button
187+
variant="outline"
188+
size="sm"
189+
onClick={() => readResource(selectedResource.uri)}
190+
>
191+
<RefreshCw className="w-4 h-4 mr-2" />
192+
Refresh
193+
</Button>
194+
</>
175195
)}
176196
</div>
177197
<div className="p-4">

client/src/lib/hooks/useConnection.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
CreateMessageRequestSchema,
1010
ListRootsRequestSchema,
1111
ProgressNotificationSchema,
12+
ResourceUpdatedNotificationSchema,
1213
Request,
1314
Result,
1415
ServerCapabilities,
@@ -247,6 +248,11 @@ export function useConnection({
247248
ProgressNotificationSchema,
248249
onNotification,
249250
);
251+
252+
client.setNotificationHandler(
253+
ResourceUpdatedNotificationSchema,
254+
onNotification,
255+
);
250256
}
251257

252258
if (onStdErrNotification) {

client/src/lib/notificationTypes.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
NotificationSchema as BaseNotificationSchema,
33
ClientNotificationSchema,
4+
ServerNotificationSchema,
45
} from "@modelcontextprotocol/sdk/types.js";
56
import { z } from "zod";
67

@@ -11,9 +12,9 @@ export const StdErrNotificationSchema = BaseNotificationSchema.extend({
1112
}),
1213
});
1314

14-
export const NotificationSchema = ClientNotificationSchema.or(
15-
StdErrNotificationSchema,
16-
);
15+
export const NotificationSchema = ClientNotificationSchema
16+
.or(StdErrNotificationSchema)
17+
.or(ServerNotificationSchema);
1718

1819
export type StdErrNotification = z.infer<typeof StdErrNotificationSchema>;
1920
export type Notification = z.infer<typeof NotificationSchema>;

0 commit comments

Comments
 (0)