Skip to content

Commit d59a94a

Browse files
committed
finish instructions through exercise 4
1 parent 1926518 commit d59a94a

File tree

15 files changed

+327
-0
lines changed

15 files changed

+327
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,14 @@
11
# Organization
2+
3+
👨‍💼 Peter the Product Manager here! As our journaling app grows, it's important
4+
to keep things organized so we can move fast and avoid confusion. Right now, our
5+
tool definitions are all mixed in with the rest of the server code, which will
6+
get messy as we add more features.
7+
8+
For this step:
9+
10+
- Move your tool definitions into a dedicated module.
11+
- Export a function to initialize all tools for the server.
12+
- Set things up so future tools are easy to add and manage.
13+
14+
A clean structure now will save us a lot of headaches as the project evolves!
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,14 @@
11
# Organization
2+
3+
👨‍💼 Nice work—now our codebase is much easier to navigate. With tool definitions
4+
in their own module, it's easier to add new features and keep things tidy as
5+
the project grows.
6+
7+
<callout-info>
8+
In big projects, you'll probably want to organize this similar to other
9+
projects that you've made with proper co-location of features rather than
10+
separating things by category, but in a small project like this one separating
11+
it by category makes perfect sense.
12+
</callout-info>
13+
14+
Onward to building even more powerful tools!
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,16 @@
11
# Class
2+
3+
👨‍💼 Time to level up our architecture! Most real-world MCP servers are deployed
4+
remotely I recommend using Cloudflare's [agent](https://npm.im/agent) module
5+
which exposes a `McpAgent` class that makes it easy to build a remote MCP
6+
server.
7+
8+
We're going to stick with stdio in this workshop, but we'll pattern after that
9+
so it's easy to switch to a remote server with Cloudflare when we're ready.
10+
11+
So, I'd like you to refactor the journaling app to match that pattern so it's
12+
ready for production and easy to maintain.
13+
14+
- Move your server and database setup into a class.
15+
- Add an `init` method for any async setup (since constructors can't be async).
16+
- Make sure your tools are initialized as part of the class lifecycle.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
11
# Class
2+
3+
🦉 Olivia the Owl here! I just wanted to call out the fact that there's no
4+
strict requirement to structure your MCP servers with a class and init method,
5+
but it's a pattern I recommend—especially if you plan to deploy remotely or want
6+
a clean, maintainable codebase.
7+
8+
👨‍💼 Thanks Olivia! Great job. With this refactor, the journaling app is now
9+
easier to extend, test, and deploy in real-world scenarios. You're set up for
10+
success as your project grows!

exercises/03.advanced-tools/04.problem.errors/README.mdx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
# Error Handling
22

3+
👨‍💼 As we add more features, it's inevitable that things will sometimes go
4+
wrong—maybe a user tries to fetch a journal entry that doesn't exist, or there's
5+
a problem saving data. It's important that both users and LLMs get clear,
6+
structured feedback when errors happen.
7+
8+
For this step:
9+
10+
- Update your tools to return error responses in a way that's compatible with
11+
the MCP spec.
12+
- Make sure errors are surfaced in the response, with `isError: true` and a
13+
helpful message in the content.
14+
15+
This will make your journaling app more robust and user-friendly, and help LLMs
16+
handle problems gracefully.
17+
318
Example error response:
419

520
```json
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,9 @@
11
# Error Handling
2+
3+
👨‍💼 Nicely done! Now, when something goes wrong, LLMs get a clear, structured
4+
error message which they can use to generate a proper message for the end user.
5+
This makes the app much more robust and user-friendly, and helps everyone know
6+
exactly what went wrong and why.
7+
8+
With proper error handling in place, your tools are ready for real-world use—no
9+
more silent failures or confusing responses!
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,13 @@
11
# Advanced Tools
2+
3+
👨‍💼 Fantastic work! You've taken your MCP server from simple tools to a robust,
4+
real-world journaling app with database integration, modular organization,
5+
class-based architecture, and proper error handling.
6+
7+
Key takeaways:
8+
9+
- You learned how to structure MCP tools for real applications.
10+
- You made your codebase maintainable and ready for production.
11+
- You're now equipped to build and deploy advanced, user-friendly MCP servers.
12+
13+
Onward to even more powerful features!
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,39 @@
11
# Resources
2+
3+
👨‍💼 Our users really want to know who's responsible for building this amazing
4+
app! (That's you!). In this step, you'll take your first step toward exposing
5+
structured data from your server—not just as tool responses, but as first-class
6+
resources that clients can discover and read.
7+
8+
Your goal:
9+
10+
- Declare the `resources` capability on your server.
11+
- Register a simple resource called "credits" that provides information about
12+
the creator of the app. This resource should be available at the URI
13+
`meta://credits` and return a plain text string with the username of the app's
14+
creator.
15+
16+
This is your first taste of the MCP resources system. No need for dynamic
17+
templates or database integration yet—just get a static resource registered and
18+
returning a simple value.
19+
20+
Here's an example of a resource:
21+
22+
```ts
23+
agent.server.resource(
24+
'hello',
25+
'hello://world',
26+
{ description: 'A simple hello world resource' },
27+
async (uri) => {
28+
return {
29+
contents: [
30+
{
31+
mimeType: 'text/plain',
32+
text: 'Hello, world!',
33+
uri: uri.toString(),
34+
},
35+
],
36+
}
37+
},
38+
)
39+
```
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
11
# Resources
2+
3+
👨‍💼 Well done! You've just registered your very first MCP resource. Now your
4+
server can expose structured data—like credits, files, or database records—as
5+
first-class citizens that clients can discover and read.
6+
7+
Let's keep going!
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,56 @@
11
# Resource Templates
2+
3+
👨‍💼 Now that you've registered a static resource, let's make things more dynamic!
4+
In this step, you'll use MCP's resource templates to expose a whole family of
5+
resources—each with its own unique URI and data.
6+
7+
Resource templates let you define parameterized resources, like `entry://{id}`
8+
or `tag://{id}`. This means clients can discover and read individual entries or
9+
tags by their unique identifiers, just like accessing a file by its path.
10+
11+
Here's a dynamic "hello world" resource template example that reads names from a
12+
database:
13+
14+
```ts
15+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'
16+
17+
agent.server.resource(
18+
'hello',
19+
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+
},
31+
}),
32+
{ description: 'Say hello to anyone by name!' },
33+
async (uri, { name }) => {
34+
return {
35+
contents: [
36+
{
37+
mimeType: 'text/plain',
38+
text: `Hello, ${name}!`,
39+
uri: uri.toString(),
40+
},
41+
],
42+
}
43+
},
44+
)
45+
```
46+
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+
Your goal in this step:
52+
53+
- Use resource templates to expose entries and tags from your database, each
54+
accessible by a unique URI.
55+
- Make sure clients can list all available entries and tags (using the `list`
56+
callback), and read the details for any specific one.

0 commit comments

Comments
 (0)