Homework — Sampling & Elicitation
Lesson 07 · MCP Servers · three short extensions to the lab's PKB server
These three tasks layer on top of the lab. You'll be editing the same
mcp-servers/sample-server/python/pkb/ package — extend the existing
summarize tool, add two new ones, and keep the user-pays-for-tokens
discipline you practised in the lab.
Before you start
Finish the lab first. PKB must already expose echo, search, and
the sampling-powered summarize. Each task below is independently testable
in Inspector — do them in order and reinstall (pip install -e .) only when
Python complains about a missing import.
Task 1 — Add a summarize_all tool
Add a new tool that summarises every note in the store in one call:
@mcp.tool()
async def summarize_all(ctx: Context) -> dict[str, str]:
...
The tool runs sampling once per note in NOTES and returns a mapping
{title: 2-sentence-summary}. Because each call costs the user real tokens,
enforce a hard cap: if len(NOTES) > 5, raise
McpError(INVALID_PARAMS) with a message citing the cap, and don't issue any
sampling requests at all.
✓ Self-check: calling with 3 notes in the store returns a 3-key dict;
padding the store to 6 notes and calling again returns an INVALID_PARAMS
error citing the cap (no consent dialog appears).
Task 2 — Add an elicitation step to write_note
Add a tool that lets the user create a new note:
@mcp.tool()
async def write_note(body: str, ctx: Context) -> str:
...
If the LLM didn't pass a title, use ctx.session.elicit(...) to
ask the user for one (an object schema with a single required title: string
field is enough). On accept, save the new note to NOTES under the supplied
title and return "saved: <title>". On any non-accept action, return
"write cancelled by user" without writing anything.
✓ Self-check: in Inspector, calling write_note with only
body triggers an elicitation form; filling in title=demo and
accepting saves the note (re-list via search to confirm); declining returns
"write cancelled by user" and leaves NOTES untouched.
Task 3 — Cost-aware sampling in summarize
Modify summarize_via_sampling in pkb/tools.py so it
short-circuits when the note body is too small to be worth summarising:
if len(body) < 80, return the body itself plus the suffix
"Note too short to summarize." and never issue a sampling request.
Pick the threshold by characters, not words — token cost correlates with character count much more reliably than word count for short snippets.
✓ Self-check: calling summarize(id="todo") (a 21-char
body) returns the body plus the trailer "Note too short to summarize.", with no
consent dialog and no sampling request observable in Inspector's network/log pane.
You're spending other people's tokens
Every sampling call inside these tools bills the user, not your server. Caps (Task 1) and short-circuits (Task 3) aren't optimisations — they're the contract you owe anyone who installs your server. Test the caps before you ship.