What this is
No new concepts in this lesson. This is a checkpoint: a small, real server built entirely out of ideas from Lessons 2 through 9, combined into one thing. If you can read the code and explain why every tool, resource, prompt, error path, and log line is there, you've mastered the Beginner tier. If any piece feels unfamiliar, revisit the lesson it came from before continuing to Intermediate.
What it does
A Notes Server: add, list, and delete notes; read a specific note as a resource; ask for a prompt that summarizes everything currently stored. Notes live in a plain Python dict, in memory, keyed by an auto-incrementing id, this mirrors the lifespan/shared-state pattern Lesson 18 formalizes later, for now module-level state is enough.
Where each piece came from
_notes: dict[int, str] = {}_next_id = 1
@mcp.tool()def add_note(text: str) -> int: """Add a note and return its id.""" global _next_id note_id = _next_id _notes[note_id] = text _next_id += 1 return note_idLessons 2-4: add_note, list_notes, and delete_note are tools that change server state, discoverable with tools/list like any other.
@mcp.resource("notes://{note_id}")def get_note(note_id: str) -> str: """Read a specific note's content.""" return _notes.get(int(note_id), "Note not found.")Lesson 5: notes://{note_id} is a resource, reading a note's content without "doing" anything, no tool call required.
@mcp.prompt()def summarize_notes() -> str: """Ask a model to summarize everything currently stored.""" return f"Summarize these notes:\n\n{list(_notes.values())}"Lesson 6: summarize_notes is a reusable prompt template a host or client can pull up, rather than a user needing to phrase the summarization request themselves.
@mcp.tool()def delete_note(note_id: int) -> str: """Delete a note by id.""" if note_id not in _notes: raise ValueError(f"No note with id {note_id}.") del _notes[note_id] return f"Deleted note {note_id}."Lesson 7: deleting a note that doesn't exist raises a normal exception, which the SDK turns into isError=True, not a crash. Lesson 8: all internal bookkeeping goes through logging, never print(), since this is a stdio server.
Checkpoint
- A server exposing tools, a resource, and a prompt together, backed by simple in-memory state.
- Deleting a missing note fails cleanly with
isError=True, the server keeps running for the next call. - Every log line goes through
logging, safe on the stdio transport.
Try this yourself, without looking anything up: point the MCP Inspector at server.py and add, list, and delete a note through the UI. Then read notes://1 as a resource and confirm it matches what list_notes reported.
If this ran cleanly and made sense without looking anything up, you're ready for the Intermediate tier, starting at Lesson 11, connecting a client to your own server, except this time you already know what's on the other end of the connection, because you just built it.