Skip to content

Commit b2c5202

Browse files
committed
split list up
1 parent 5d9f74a commit b2c5202

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+2042
-53
lines changed

exercises/03.resources/02.problem.template/README.mdx

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,8 @@ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'
1717
agent.server.resource(
1818
'hello',
1919
new ResourceTemplate('hello://{name}', {
20-
list: async () => {
21-
// Imagine this is a call to your database to get all names
22-
const names = await db.getAllNames()
23-
return {
24-
resources: names.map((name) => ({
25-
name,
26-
uri: `hello://${name}`,
27-
mimeType: 'text/plain',
28-
})),
29-
}
30-
},
20+
// this is required to be specified, but we'll implement it later...
21+
list: undefined,
3122
}),
3223
{ description: 'Say hello to anyone by name!' },
3324
async (uri, { name }) => {
@@ -44,20 +35,10 @@ agent.server.resource(
4435
)
4536
```
4637

47-
Notice how the `list` callback queries the database and returns a resource
48-
listing for each name. This pattern is exactly what you'll use to expose entries
49-
and tags from your own database.
50-
51-
<callout-success>
52-
The reason for the `list` callback is to make it easy for clients to get a
53-
list of available resources without having to know the exact URIs and without
54-
having to request the entire resource (notice we only return the name, URI,
55-
and type in the `list` callback).
56-
</callout-success>
57-
5838
Your goal in this step:
5939

6040
- Use resource templates to expose entries and tags from your database, each
6141
accessible by a unique URI.
62-
- Make sure clients can list all available entries and tags (using the `list`
63-
callback), and read the details for any specific one.
42+
43+
Test this out by creating a new entry or tag in your database and then using the
44+
inspector to see the one you created.

exercises/03.resources/02.problem.template/src/resources.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ export async function initializeResources(agent: EpicMeMCP) {
2525
// 🐨 create two resources with a ResourceTemplate:
2626
// - entry - URI: epicme://entries/{id}
2727
// - tag - URI: epicme://tags/{id}
28-
// 🐨 each should have a list method that returns all the entries and tags (respectively)
2928
// 🐨 each should have a description
3029
// 🐨 each should have a callback that reads the entry or tag for the given id
3130
// 🐨 return contents with mimeType application/json and the entry or tag

exercises/03.resources/02.solution.template/README.mdx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
11
# Resource Templates
22

33
👨‍💼 Fantastic work! You've just leveled up your MCP server by implementing
4-
dynamic resource templates with a list callback. Now your server can expose
5-
entire collections of resources—like entries and tags—making them discoverable
6-
and accessible to clients and LLMs alike.
4+
dynamic resource templates. Now your server can expose entire collections of
5+
resources—like entries and tags—making them discoverable and accessible to
6+
clients and LLMs alike.
77

88
This is a huge step toward building rich, interactive, and context-aware
9-
applications. By leveraging resource templates and the list callback, you've
10-
made it possible for clients to browse, search, and retrieve structured data on
11-
demand.
9+
applications. By leveraging resource templates, you've made it possible for
10+
clients to browse, search, and retrieve structured data on demand.
1211

1312
Key takeaways from this step:
1413

1514
- How to define parameterized resource templates
16-
- How to implement a list callback to dynamically enumerate resources from your
17-
database
1815
- How to make individual resources accessible by unique URIs
1916

2017
You're well on your way to building a truly powerful and flexible MCP server.

exercises/03.resources/02.solution.template/src/resources.ts

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,7 @@ export async function initializeResources(agent: EpicMeMCP) {
2424
agent.server.resource(
2525
'tag',
2626
new ResourceTemplate('epicme://tags/{id}', {
27-
list: async () => {
28-
const tags = await agent.db.getTags()
29-
return {
30-
resources: tags.map((tag) => ({
31-
name: tag.name,
32-
uri: `epicme://tags/${tag.id}`,
33-
mimeType: 'application/json',
34-
})),
35-
}
36-
},
27+
list: undefined,
3728
}),
3829
{ description: 'A single tag' },
3930
async (uri, { id }) => {
@@ -54,16 +45,7 @@ export async function initializeResources(agent: EpicMeMCP) {
5445
agent.server.resource(
5546
'entry',
5647
new ResourceTemplate('epicme://entries/{id}', {
57-
list: async () => {
58-
const entries = await agent.db.getEntries()
59-
return {
60-
resources: entries.map((entry) => ({
61-
name: entry.title,
62-
uri: `epicme://entries/${entry.id}`,
63-
mimeType: 'application/json',
64-
})),
65-
}
66-
},
48+
list: undefined,
6749
}),
6850
{ description: 'A single entry' },
6951
async (uri, { id }) => {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Resource Templates List
2+
3+
👨‍💼 Our users want to be able to see a list of all entries and tags. So let's
4+
implement the `list` callback for our resource templates.
5+
6+
```ts
7+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'
8+
9+
agent.server.resource(
10+
'hello',
11+
new ResourceTemplate('hello://{name}', {
12+
list: async () => {
13+
// Imagine this is a call to your database to get all names
14+
const names = await db.getAllNames()
15+
return {
16+
resources: names.map((name) => ({
17+
name,
18+
uri: `hello://${name}`,
19+
mimeType: 'text/plain',
20+
// note this does not include the contents of the resource
21+
})),
22+
}
23+
},
24+
}),
25+
// ...
26+
)
27+
```
28+
29+
Notice how the `list` callback queries the database and returns a resource
30+
listing for each name. This pattern is exactly what you'll use to expose entries
31+
and tags from your own database.
32+
33+
<callout-success>
34+
The reason for the `list` callback is to make it easy for clients to get a
35+
list of available resources without having to know the exact URIs and without
36+
having to request the entire resource (notice we only return the name, URI,
37+
and type in the `list` callback).
38+
</callout-success>
39+
40+
Your goal in this step:
41+
42+
- Use resource templates to expose entries and tags from your database, each
43+
accessible by a unique URI.
44+
- Make sure clients can list all available entries and tags (using the `list`
45+
callback), and read the details for any specific one.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "exercises_03.resources_02.solution.template",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"dev": "mcp-dev",
7+
"dev:mcp": "tsx src/index.ts",
8+
"test": "vitest",
9+
"typecheck": "tsc",
10+
"inspect": "mcp-inspector"
11+
},
12+
"dependencies": {
13+
"@epic-web/invariant": "^1.0.0",
14+
"@modelcontextprotocol/sdk": "^1.11.0",
15+
"zod": "^3.24.3"
16+
},
17+
"devDependencies": {
18+
"@epic-web/config": "^1.19.0",
19+
"@epic-web/mcp-dev": "*",
20+
"@faker-js/faker": "^9.8.0",
21+
"@modelcontextprotocol/inspector": "^0.13.0",
22+
"@types/node": "^22.15.2",
23+
"tsx": "^4.19.3",
24+
"typescript": "^5.8.3",
25+
"vitest": "^3.1.2"
26+
},
27+
"license": "GPL-3.0-only"
28+
}

0 commit comments

Comments
 (0)