Skip to content

Commit 9157a8e

Browse files
committed
all done
1 parent 834d6ce commit 9157a8e

File tree

10 files changed

+341
-5
lines changed

10 files changed

+341
-5
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,48 @@
11
# List Changed
2+
3+
πŸ‘¨β€πŸ’Ό When people use the Epic Me journal app, they expect the interface to always reflect what's possible right now. If a new journaling prompt becomes available, or a feature is temporarily disabled, users want the UI to update instantly. No confusion, no stale buttons, just a smooth, up-to-date experience.
4+
5+
That's why it's so important for our MCP server to notify the client whenever the set of available prompts, tools, or resources changes. This allows the client app to feel alive and responsive, and ensures users always see the right options for their current context, whether they're a first-time journaler or a seasoned reflection pro.
6+
7+
Let's look at an example of how this is done: imagine a smart vending machine that offers snacks based on the time of day. In the morning, it might offer coffee and bagels; in the afternoon, chips and soda. When the available snacks change, the server should notify the client so the snack menu updates automatically.
8+
9+
Here's how you might enable list change notifications for prompts using the MCP SDK:
10+
11+
```ts
12+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
13+
14+
const server = new McpServer(
15+
{
16+
name: 'SnackBot',
17+
version: '1.0.0',
18+
},
19+
{
20+
capabilities: {
21+
prompts: { listChanged: true },
22+
// ...other capabilities
23+
},
24+
},
25+
)
26+
27+
// When the snack list changes, enable or disable prompts as needed
28+
function updateSnackPrompts() {
29+
if (isMorning()) {
30+
if (!coffeePrompt.enabled) coffeePrompt.enable()
31+
if (chipsPrompt.enabled) chipsPrompt.disable()
32+
} else {
33+
if (!chipsPrompt.enabled) chipsPrompt.enable()
34+
if (coffeePrompt.enabled) coffeePrompt.disable()
35+
}
36+
}
37+
```
38+
39+
<callout-success>
40+
The MCP SDK automatically sends `list_changed` notifications when you enable
41+
or disable prompts, so the client always knows when to refresh its list.
42+
</callout-success>
43+
44+
This approach means users never see a "Make Coffee" button at 3pm, or a "Get Chips" option before breakfast. The UI stays in sync with what's actually available, making the experience feel smart and effortless.
45+
46+
πŸ“œ For more details, see the [Server Spec: Prompts](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts).
47+
48+
The MCP Inspector supports list change notifications, but [does not currently automatically refresh the UI when lists change](https://github.com/modelcontextprotocol/inspector/issues/592). So you'll want to rely on the tests for this one.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,15 @@
11
# List Changed
2+
3+
πŸ‘¨β€πŸ’Ό Fantastic progress! Our app now supports real-time updates for journaling
4+
prompts, thanks to the new `list_changed` notification capability. Whenever a
5+
prompt is enabled or disabled (like when a new journaling feature becomes
6+
available or an old one is retired), the client is instantly notified and
7+
can refresh its list. This means users always see the most relevant options,
8+
with no confusion or stale choices lingering in the interface.
9+
10+
Let's keep building on this momentum to make the experience even better!
11+
12+
πŸ§β€β™€οΈ I'm applying the same pattern we used for prompts to resources and tools.
13+
If you want extra practice, try it yourselfβ€”it's the same approach! Or
14+
just <NextDiffLink>check out my changes</NextDiffLink> if you'd rather skip
15+
ahead.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,39 @@
11
# Resource Template List Changed
2+
3+
πŸ‘¨β€πŸ’Ό When people use our EpicMe journaling app, they expect the list of available resources (like journal entries, tags, or even videos) to always reflect what's actually possible right now. Imagine a user just added a new video reflection or created a fresh tag for their latest entry. They want to see those options appear instantly, without needing to refresh or wonder if something is out of sync.
4+
5+
That's why it's so important for our MCP server to notify the client whenever the set of available resources changes, especially when those resources are managed by templates that can expand or contract as the underlying data changes. This keeps the UI feeling alive and responsive, so users always see the right options for their current context, whether they're a first-time journaler or a seasoned reflection pro.
6+
7+
Let's look at an example of how this is done:
8+
9+
```ts lines=16
10+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
11+
12+
const server = new McpServer(
13+
{ name: 'DuckCollector', version: '1.0.0' },
14+
{
15+
capabilities: {
16+
resources: { listChanged: true },
17+
// ...other capabilities
18+
},
19+
},
20+
)
21+
22+
// Whenever the ducky or tag list changes, notify the client
23+
function updateResourceTemplates() {
24+
// ...logic to check for changes...
25+
server.sendResourceListChanged()
26+
}
27+
```
28+
29+
<callout-success>
30+
The MCP SDK makes it easy to keep resource lists in sync. Just call
31+
`sendResourceListChanged()` whenever your templates expand (items are added),
32+
contract (items are removed), or change metadata.
33+
</callout-success>
34+
35+
To make this work in our app, you'll want to subscribe to changes in your database and any other dynamic sources (like video uploads). When something changes, call `sendResourceListChanged()` so the client can refresh its resource lists and show users the latest and greatest.
36+
37+
πŸ“œ For more details, see the [Server Spec: Resources](https://modelcontextprotocol.io/specification/2025-06-18/server/resources).
38+
39+
The goal is to make resource management feel effortless and immediate for users, so they can focus on their journaling adventuresβ€”not on refreshing the page.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,5 @@
11
# Resource Template List Changed
2+
3+
πŸ‘¨β€πŸ’Ό Outstanding work! Our app now keeps the list of available resourcesβ€”like journal entries, tags, and videosβ€”perfectly in sync with what's actually possible for the user, thanks to the new `list_changed` notification for resource templates. Now, whenever a user adds a new video reflection, creates a fresh tag, or updates their journal, the UI updates instantly.
4+
5+
Let's keep going!
Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,55 @@
11
# Subscriptions
22

3-
{/* add instructions on how to use setRequestHandler */}
3+
πŸ‘¨β€πŸ’Ό When people use our EpicMe journaling MCP, they want the LLM they're using to always have the latest version of their jounraling data. Exposing subscriptions for resources (like a particular entry, tag, or video) allows clients to be notified of changes to specific resources the LLM is using in its context or the client may be using for other purposes.
4+
5+
To make this possible, our MCP server needs to support **resource subscriptions**. This means clients can subscribe to updates for specific resources, and the server will notify them when those resources change.
6+
7+
The key to enabling this with the MCP SDK is the `setRequestHandler` method. This lets the server listen for subscription requests from clients and track which URIs they're interested in. When something changes, the server can send a notification to just those clients.
8+
9+
Here's an example:
10+
11+
```ts
12+
import {
13+
SubscribeRequestSchema,
14+
UnsubscribeRequestSchema,
15+
} from '@modelcontextprotocol/sdk/types.js'
16+
17+
const petSubscriptions = new Set<string>()
18+
19+
// NOTE: the server.server is how the MCP SDK exposes the underlying server
20+
// instance for more advanced APIs like this one.
21+
22+
// Allow clients to subscribe to updates for a specific pet
23+
server.server.setRequestHandler(SubscribeRequestSchema, async ({ params }) => {
24+
petSubscriptions.add(params.uri)
25+
return {} // no specific response data is needed
26+
})
27+
28+
// Allow clients to unsubscribe from updates
29+
server.server.setRequestHandler(
30+
UnsubscribeRequestSchema,
31+
async ({ params }) => {
32+
petSubscriptions.delete(params.uri)
33+
return {} // no specific response data is needed
34+
},
35+
)
36+
37+
// When a pet's status changes, notify subscribed clients
38+
function onPetStatusChange(petId: string, newStatus: string) {
39+
const uri = `pethotel://pets/${petId}`
40+
if (petSubscriptions.has(uri)) {
41+
server.server.notification({
42+
method: 'notifications/resources/updated',
43+
params: { uri, title: `Pet ${petId} status updated to ${newStatus}` },
44+
})
45+
}
46+
}
47+
```
48+
49+
πŸ‘¨β€πŸ’Ό In our journaling MCP, you'll want to use a similar pattern to let clients subscribe to updates for entries, tags, and videos. When any of these resources change, notify the right clients so their UI stays perfectly in sync.
50+
51+
πŸ“œ For more details on `setRequestHandler` and subscriptions, see the [official MCP Server Spec: Resources](https://modelcontextprotocol.io/specification/2025-06-18/server/resources).
52+
53+
The goal is to make real-time updates feel effortless and automatic for users, so they can focus on their journalingβ€”not on hitting the refresh button.
54+
55+
The MCP Inspector supports subscriptions, but [does not currently automatically refresh the UI when a resource changes](https://github.com/modelcontextprotocol/inspector/issues/596). So you'll want to rely on the tests for this one.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
11
# Subscriptions
2+
3+
πŸ‘¨β€πŸ’Ό Way to go! We've just unlocked real-time updates for our users by enabling resource subscriptions. Now, whenever someone is working with a specific journal entry, tag, or video, the app can instantly notify them of any changesβ€”no more stale data or manual refreshes. This means users always have the most up-to-date information at their fingertips, making their journaling experience seamless and trustworthy.
4+
5+
By supporting the `subscribe` capability, we've ensured that our app feels alive and responsive, keeping users in sync with their most important content. This is a huge win for anyone who relies on timely, accurate updatesβ€”whether they're reflecting on their latest entry, organizing tags, or reviewing a video.
6+
7+
What a fantastic way to wrap up our journey through change notifications and real-time collaboration!
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
# Changes
2+
3+
Great job on this exercise. You now know how to make dynamic MCP servers.
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,174 @@
11
# Changes
2+
3+
In a web application, the buttons, links, and data that you see change based on the context. Whether the user is currently logged in, whether there is any data available, what role the user has, etc. MCP servers are no different. The information and tools they provide can change at any time.
4+
5+
MCP provides a robust system for **notifying clients about changes** so that UIs and agents can stay in sync with the server's current capabilities and data.
6+
7+
This exercise will teach you how to implement and respond to change notifications in MCP, including:
8+
9+
- **list_changed notifications** for tools, prompts, and resources
10+
- Special handling for resource template list changes
11+
- Resource update notifications and subscriptions
12+
- Recommended practices for keeping clients and users up-to-date
13+
14+
<callout-info>
15+
Change notifications are essential for building responsive, real-time AI apps.
16+
They let clients update menus, toolbars, and resource lists automatically.
17+
</callout-info>
18+
19+
---
20+
21+
## 1. List Change (Tools, Prompts, and Resources)
22+
23+
Whenever the set of available tools, prompts, or resources changes, the server should send a `list_changed` notification. This tells the client to refresh its list and fetch the latest definitions.
24+
25+
- **Tools:** If a tool is added, removed, or updated, send a `notifications/tools/list_changed` request.
26+
- **Prompts:** If a prompt is enabled, disabled, or updated, send a `notifications/prompts/list_changed` request.
27+
- **Resources:** If a static resource or resource template is registered, unregistered, or its metadata changes, send a `notifications/resources/list_changed` request.
28+
29+
<callout-success>
30+
Clients that support `list_changed` can provide a seamless, always-up-to-date
31+
experience for users, no manual refresh required!
32+
</callout-success>
33+
34+
#### Example: Enabling/Disabling a Tool in Mission Control
35+
36+
Suppose your space station operations console has a "Dock Module" tool that should only be available when a docking port is free. When a new port becomes available, enable the tool and send a `tools/list_changed` notification. If all ports are occupied, disable the tool and notify again.
37+
38+
```ts
39+
// Pseudocode
40+
if (freeDockingPorts.length > 0 && !dockModuleTool.enabled) {
41+
dockModuleTool.enable()
42+
} else if (freeDockingPorts.length === 0 && dockModuleTool.enabled) {
43+
dockModuleTool.disable()
44+
}
45+
```
46+
47+
The TypeScript SDK automatically sends `list_changed` notifications when tools are enabled or disabled. To avoid over-sending notifications, you should check whether the tool is already enabled or disabled before enabling or disabling it.
48+
49+
<callout-info>
50+
[In the
51+
future](https://github.com/modelcontextprotocol/typescript-sdk/pull/746),
52+
`list_changed` will be batched to avoid sending too many notifications.
53+
</callout-info>
54+
55+
## 2. Resource List Change (Templates and Expansions)
56+
57+
Resources are special because there's a difference between what the
58+
ResourceTemplate "expands" into and whether that resource template is available
59+
in the first place:
60+
61+
```ts lines=4-6,17-18
62+
const ingredientsResource = agent.server.registerResource(
63+
'ingredient',
64+
new ResourceTemplate('sandwich://ingredients/{id}', {
65+
list: async () => {
66+
// what this returns can change as a result of database changes etc.
67+
},
68+
}),
69+
{
70+
title: 'Ingredient',
71+
description: 'A single sandwich ingredient by ID',
72+
},
73+
async (uri, { id }) => {
74+
// ...
75+
},
76+
)
77+
78+
// this can also change as a result of user's access
79+
ingredientsResource.enable() // or disable()
80+
```
81+
82+
Resources in MCP can be either static (a single file, a database record) or dynamic via **resource templates** (e.g., a directory of files, a database table, or even a set of modules on a space station πŸš€). Resource templates allow the server to expose a pattern or collection of resources that can change over time.
83+
84+
A `notifications/resources/list_changed` notification is sent when:
85+
86+
- A new resource template is enabled/disabled
87+
- The set of expansions for a template changes (e.g., a new module docks, a new experiment log appears, or a file is added to a directory)
88+
- The metadata for a resource or template changes (e.g., the title of a module changes)
89+
90+
## 3. Resource Subscriptions (Updates to Specific URIs)
91+
92+
While `list_changed` tells clients about changes in what resources are available, **resource subscriptions** are about changes to the content of a specific resource URI. Clients can subscribe to updates for a particular resource (e.g., a specific module or experiment log) and receive a `notifications/resources/updated` notification when its content changes.
93+
94+
- Clients subscribe to resource URIs using the `subscribe` capability
95+
- The server tracks subscriptions and notifies only interested clients
96+
97+
#### Example: Subscribing to Module Status Updates
98+
99+
A client wants to stay updated on a specific space station module:
100+
101+
```json
102+
{
103+
"jsonrpc": "2.0",
104+
"id": 1,
105+
"method": "resources/subscribe",
106+
"params": { "uri": "spacestation://modules/habitat-alpha" }
107+
}
108+
```
109+
110+
When the module's status changes (e.g., a new experiment starts, or the module is undocked), the server sends:
111+
112+
```json
113+
{
114+
"jsonrpc": "2.0",
115+
"method": "notifications/resources/updated",
116+
"params": {
117+
"uri": "spacestation://modules/habitat-alpha",
118+
"title": "Habitat Alpha Module"
119+
}
120+
}
121+
```
122+
123+
<callout-muted>
124+
Subscriptions are for updates to the content of a specific resource URI, not
125+
for changes in the set of available resources. Use `list_changed` for the
126+
latter.
127+
</callout-muted>
128+
129+
---
130+
131+
## Sequence Diagram
132+
133+
This all might work a little bit like this:
134+
135+
```mermaid
136+
sequenceDiagram
137+
participant User
138+
participant HostApp
139+
participant Client
140+
participant Server
141+
142+
User->>HostApp: Start application
143+
HostApp->>Client: Initialize client
144+
Client->>Server: Initialization request
145+
Server-->>Client: Capabilities response
146+
%% ---
147+
Note over User,Server: (Later, when things change...)
148+
Server-->>Client: notifications/tools/list_changed
149+
Server-->>Client: notifications/resources/list_changed
150+
Server-->>Client: notifications/prompts/list_changed
151+
Client-->>HostApp: Forward list_changed notification
152+
HostApp->>Client: Fetch updated definitions
153+
Client->>Server: Fetch updated definitions
154+
Server-->>Client: Updated definitions
155+
Client-->>HostApp: Forward updated definitions
156+
HostApp->>User: Update UI
157+
%% ---
158+
Note over User,Server: (Subscription flow)
159+
HostApp->>Client: Subscribe to resource
160+
Client->>Server: Subscribe to resource
161+
Server-->>Client: Subscription confirmed
162+
Client-->>HostApp: Subscription confirmed
163+
%% ---
164+
Note over User,Server: (Later, when resource changes...)
165+
Server-->>Client: notifications/resources/updated
166+
Client-->>HostApp: Forward resource update
167+
HostApp->>User: Update UI
168+
```
169+
170+
## References
171+
172+
- πŸ“œ [Server Spec: Resources](https://modelcontextprotocol.io/specification/2025-06-18/server/resources)
173+
- πŸ“œ [Server Spec: Tools](https://modelcontextprotocol.io/specification/2025-06-18/server/tools)
174+
- πŸ“œ [Server Spec: Prompts](https://modelcontextprotocol.io/specification/2025-06-18/server/prompts)

β€Žexercises/FINISHED.mdxβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Advanced MCP Features 🐑
22

3-
Hooray! You're all done! πŸ‘πŸ‘
3+
Thank you for joining me for this workshop. I hope you have a much better understanding of the more advanced capabilities of MCP servers!

β€Žpackage.jsonβ€Ž

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
},
1313
"stackBlitzConfig": {
1414
"view": "editor",
15-
"terminalHeight": 0,
16-
"hidedevtools": true,
17-
"hideNavigation": true
15+
"terminalHeight": "0",
16+
"hidedevtools": "1",
17+
"hideNavigation": "1"
1818
},
1919
"product": {
2020
"host": "www.epicai.pro",

0 commit comments

Comments
Β (0)