A clean, easy-to-understand MCP server showing Data Discovery Elicitation and User Elicitation patterns. Demonstrates elicitation patterns for learning MCP.
This is a STDIO-only educational server with 4 tools. Tool definitions are directly visible in server.py with explicit @mcp.tool() registrations. However, there are significant gaps: (1) Tool schemas are INFERRED from docstrings and function signatures rather than explicitly declared with JSON Schema in the tool registration (pattern:constrained-input violations). (2) Two tools (list_todos, complete_todo) use ctx.elicit() which is a non-standard elicitation pattern not part of the core MCP spec, this blocks standard client compatibility. (3) Parameter descriptions exist but are minimal (10-50 chars average, well below the 72 char baseline). (4) No output schemas are documented, callers must infer response format from docstrings. (5) Error handling is minimal: most errors return plain text without recovery guidance. (6) create_todo and complete_todo require multi-step elicitation that breaks tool composition, they cannot be called programmatically with parameters. These architectural choices prioritize interactive chat learning over production tool design.
Mark todos as completed. Shows available pending todos (optionally filtered by priority), then asks which to complete. Supports completing multiple todos at once.
Create a todo by collecting details step-by-step.
Shows what todos exist with counts and categories.
List todos with filter selection.
Tools use ctx.elicit() which is not part of the MCP spec and breaks standard tool invocation. create_todo and list_todos have NO input parameters, they require asynchronous elicitation from the server to the client. This prevents programmatic tool calls and is incompatible with standard MCP clients.
Input schemas are NOT registered with @mcp.tool() definitions. Schemas are inferred from function signatures and inline Pydantic models (TodoInput, ListFilter, CompleteTodoInput) that are NOT visible in the tool registration metadata. Clients cannot discover valid parameter values (enums, defaults) without parsing the entire source.
Output schemas are completely undocumented. All tools return unstructured markdown strings. No client can parse results reliably without LLM text parsing. Violates pattern:response-shaper (return structured objects, not free text).
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | F | 37 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 49 | - | v1 |
Parameter descriptions are too short (10 - 110 chars, well below 72 char baseline). 'Filter todos by status' does not explain which status values are valid or what the default is. Descriptions lack actionable detail that LLMs need to select correct parameter values.
Error handling is minimal. Most failures return generic strings ('No todos found', 'Cancelled') with no recovery guidance. Violates pattern:recovery-guide, errors should tell the agent what to do next (e.g., 'No pending todos. Try create_todo() to add one.').
get_todos_overview returns a hardcoded 5-item limit on pending todos display without explaining pagination or offering a way to fetch more. Violates pattern:paginated-result, tools returning lists should accept limit/offset and return total counts.
complete_todo requires comma-separated IDs as a string parameter ('1,2,3'). This is fragile (no type safety, vulnerable to parsing errors) and not idiomatic JSON. Should accept an array parameter todo_ids: [1, 2, 3] instead.
create_todo, list_todos, and complete_todo all use async/await with ctx.elicit() but the server.py does not show how the FastMCP framework handles asynchronous context. This may not be compatible with synchronous MCP clients.