Completing the server trio
A server that only exposes tools is a server missing two-thirds of its vocabulary. Resources are read-only content the app pulls into context - a catalog, a policy doc. Prompts are templates the user picks explicitly, slash-command style. Same decorators you already know, different control model. By the end of tonight server.py is complete and identical to the project reference.
monthly_report prompt whose argument becomes a form field in the host, a firm grip on the model/app/user control triangle, and a daybreak-mcp server that matches the running project line for line.
Resources: context with a URI 7 min live
A resource is read-only content the application decides to attach to a conversation. You register one with @mcp.resource("uri"): the decorator argument is the URI, the function returns the content, and the return type hint sets the mime type. The URI scheme is yours to design - daybreak:// here - and it is how the app addresses the content.
LiveThe catalog resource, line by line4 min▶
Daybreak's catalog is computed from the database every time it is read - not a static file. That is the point of a resource function: it can run code. Here it queries products and formats a markdown list.
@mcp.resource("daybreak://catalog")- the URI is the argument; the function namecatalogbecomes the resource name.- Returns
str, so the mime type is text. Return bytes or set a type for binary content. - Computed live - add a product to the db and the next read reflects it. No caching to invalidate.
LiveURI design taste - nouns, hierarchies, no verbs3 min▶
The scheme is yours, so design it like an API surface, not a dumping ground. Daybreak uses daybreak://catalog and daybreak://policy/refunds - notice the second one nests under a policy/ namespace.
- Nouns, not verbs. Resources are things you read.
daybreak://catalog, notdaybreak://get-catalog. Verbs are for tools. - Hierarchies group.
policy/refunds,policy/shipping- a namespace that scales as you add docs. - Stable + addressable. The app stores the URI; renaming it breaks references. Choose once.
- Your scheme, your rules.
daybreak://means nothing to anyone else - and that is fine. It only has to be consistent within your server.
Prompts: playbooks users pick 7 min live
A prompt is a server-authored template the user chooses on purpose - it shows up as a slash command or menu item in the host. Register it with @mcp.prompt(); the function's parameters become the form fields the host renders. Daybreak ships one: monthly_report(month), the founder's standing ops report.
Livemonthly_report, walked through4 min▶
The prompt returns the text that will be handed to the model when the user picks it. Its month parameter becomes a field the host asks the user to fill - which is why the wire message carries an arguments declaration.
month: strbecomes a required argument the host renders as a field - the same wire mechanism as a tool's input schema.- The template tells the model to use the tools - a prompt orchestrates your own primitives. Pick
monthly_reportfor 2026-03 and the model will callquery_sales, see $150.00, and write the dip up. - "do not estimate" - the prompt is where you encode the house rules for a recurring task.
Self-studyWhen a prompt beats a system-prompt doc3 min read▶
You could paste "here is how we write the monthly report" into a system prompt and be done. Sometimes that is right. But a prompt primitive wins when:
- The user should trigger it explicitly. A slash command is discoverable and intentional - the user knows they are running the report, versus hoping the model remembers a buried instruction.
- It takes arguments.
monthis a parameter, not a copy-paste edit. The host collects it cleanly. - It lives with the server. Update the report format once, on the server, and every host that connects gets the new version - no chasing pasted system prompts across teammates.
Rule of thumb: reusable, parameterized, user-triggered workflows are prompts. One-off context is a system prompt. Recurring reference material is a resource.
Round out the server 5 min live
The server trio is complete. Two utilities exist that you should know by name even though Daybreak does not need them yet, and then we confirm your file matches the reference exactly.
Self-studyCompletion and pagination, briefly and honestly2 min read▶
Two protocol utilities you will meet in bigger servers, named here so they are not a surprise:
- Completion - the server can offer autocomplete suggestions for a prompt or resource argument (like a month picker suggesting valid months). Nice UX, optional, skipped here.
- Pagination - when a
listresponse is long, the server returns a cursor and the client asks for the next page. Daybreak lists three tools and two resources, so there is nothing to paginate. You will care about this when a server exposes hundreds of resources.
Neither changes how you write tools, resources, or prompts - they are protocol plumbing the SDK handles when you need them.
LiveThe finished b3 server matches the project exactly3 min▶
At this point your server.py is the running-project reference. Bottom of the file, unchanged since b2:
Restart the Inspector and confirm all six capabilities appear: three tools, two resources, one prompt. That is a complete MCP server, and it is yours.
Ship the catalog resource ★ 10 min · everyone builds
Add the first resource and read it through the Inspector's Resources tab.
Paste the catalog resource from Part 1 into server.py, below your tools.
Restart the Inspector: uv run mcp dev server.py.
Open the Resources tab. You should see daybreak://catalog listed - that is resources/list.
Read it. Click the URI; the Inspector fires resources/read and shows the markdown catalog, priced high to low.
Ship monthly_report + the refund policy ★ 10 min · build
Add the second resource and the prompt, and test the prompt in the Inspector - watch it ask you for the month.
Paste refund_policy (Part 3) and monthly_report (Part 2) into server.py.
Restart the Inspector and open the Prompts tab. monthly_report appears with a month field.
Fill month=2026-03 and get the prompt. Read the returned template - it instructs the model to use your three tools and not estimate.
Confirm the full set: 3 tools, 2 resources, 1 prompt. Your server now matches the project reference exactly.
Try it yourself - this week ◐ 25-35 min total
- Add a computed resource
daybreak://stats/monthlythat returns each month's completed revenue as a small table - one line per month, so a host can read the whole trend without callingquery_salestwelve times. Note thestats/namespace choice. - Read your new resource in the Inspector and confirm 2026-03 shows $150.00 and 2026-02 shows $271.00 - the dip, now in a resource.
- Write one more prompt,
churn_review()with no arguments, that tells the model to usesubscription_statusand flag anything worrying. Notice a zero-argument prompt renders no form. - Decide, for your own dream server from b1, which items are tools, which are resources, and which are prompts. Bring the mapping to b4.
Official sources covered
This session teaches the working core of the resource and prompt material across the official docs and the major courses, on the running project. Certificates and videos stay official. This page covers:
Three questions before you go 🎯 ◐ 90 seconds
1 · In @mcp.resource("daybreak://catalog"), who defines the daybreak:// scheme?
The scheme is yours. daybreak:// means nothing outside your server, and that is fine - it just has to be a stable, addressable name the app can store and read from.
2 · What do a prompt function's parameters become?
A prompt's parameters (like monthly_report's month) travel as an arguments declaration on the wire, which is exactly what lets the host render a form and collect the user's input.
3 · Who controls when a resource enters the conversation?
The control triangle: model controls tools, user controls prompts, app controls resources. The application decides which read-only content to attach; the server only offers it.