Module 04 Exercises: Tools and MCP
These five exercises progress from design thinking to hands-on implementation to interview simulation. Complete them in order — each builds on the previous.
Exercise 1: Design Tool Schemas for a Todo App Agent
Type: Design / Schema writing
Time: 30–45 minutes
Prereq: Read Section 1 and 2 of ../README.md
Task
Design the complete tool layer for an AI assistant that can manage a todo list. The agent should be able to: create todos, list todos with filters, update a todo’s status and text, delete todos, and organize todos into projects.
Deliverables
Write the full JSON schema for each tool. For each tool, include:
- A well-designed name (verb_noun)
- A complete description (what, when, returns, edge cases)
- A typed
input_schemawith proper use of enum, optional params, and descriptions - Which parameters are required
Starter
Here is a skeleton to fill in. Aim for 5–7 tools.
[
{
"name": "create_todo",
"description": "...",
"input_schema": {
"type": "object",
"properties": {
"title": { "type": "string", "description": "..." },
"priority": { ... },
"due_date": { ... },
"project_id": { ... }
},
"required": [...]
}
},
{
"name": "list_todos",
...
},
...
]Questions to Answer
After writing the schemas, answer these:
- Did you create separate tools for
create_todoandupdate_todo, or a single “upsert” tool? Why? - Which of your tools are idempotent? Which are not?
- If the agent calls
delete_todoby mistake, how would you make that recoverable? (Hint: think about what the delete tool returns, or whether there’s a soft-delete pattern.) - What would a
list_todostool return when the filter matches no items — an error or an empty list? Why does that choice matter?
Stretch Goal
Write a second version of your tool set where each tool returns a typed JSON object (not a plain string). Document the return schema in the tool description.
Exercise 2: Build an MCP Server Wrapping JSONPlaceholder
Type: Implementation
Time: 60–90 minutes
Prereq: Read Section 4 of ../README.md; have mcp installed
Task
Build a working MCP server that wraps the JSONPlaceholder API — a free, public fake REST API for testing.
Requirements
Your server must expose these tools:
| Tool | JSONPlaceholder endpoint |
|---|---|
list_posts(user_id?) | GET /posts?userId={id} |
get_post(post_id) | GET /posts/{id} |
list_todos(user_id?, completed?) | GET /todos?userId={id}&completed={bool} |
get_user(user_id) | GET /users/{id} |
And these resources:
| Resource | Content |
|---|---|
jsonplaceholder://users | All users as JSON |
jsonplaceholder://posts/recent | First 10 posts |
Hints
- Use
urllib.requestorhttpx(if you add it as a dep) to make HTTP calls - JSONPlaceholder returns JSON — return it as a formatted string with
json.dumps(..., indent=2) - Handle HTTP errors (404, 500) gracefully — return an error message, don’t let the exception propagate
- Remember to make your tool descriptions answer: what it does, when to use it, what it returns
Verification
Register the server in ~/.claude/settings.json and ask Claude Code:
Can you find the most recent posts by user 1 and tell me their titles?
Claude should call list_posts with user_id=1 and return the titles from the response.
Stretch Goal
Add a create_post(user_id, title, body) tool. JSONPlaceholder accepts POST requests (though it doesn’t persist them — it returns a mock created ID). Practice handling the creation response and returning it in a structured way.
Exercise 3: Add a File Resource to the Minimal MCP Server
Type: Implementation (extend existing code)
Time: 20–30 minutes
Prereq: Complete the minimal server setup from ../examples/mcp_server_minimal/
Task
Extend ../examples/mcp_server_minimal/server.py to add a resource that reads the contents of an actual file from disk.
Requirements
Add a resource with URI file://notes that:
- Reads content from a file at a configurable path (use a constant
NOTES_FILE_PATHat the top of the file) - Returns the file contents as a string
- Handles the case where the file doesn’t exist — return a helpful message, not an exception
- Shows in
list_resources()with an accurate description
Steps
- Add
NOTES_FILE_PATHconstant pointing to a local text file (create the file too) - In
list_resources(), add the new resource to the returned list - In
read_resource(), add a branch for"file://notes"that reads the file - Test: ask Claude Code to read the
file://notesresource
What You’ll Learn
- How to make resources dynamic (contents change when the file changes)
- How to handle file I/O errors gracefully in an MCP server
- The difference between a static hardcoded resource and a live resource
Stretch Goal
Make the resource URI parameterized: file:///path/to/any/file. Update list_resources() to list files in a configured directory, and read_resource() to handle any file:// URI. (Be careful about path traversal security — constrain to a specific root directory.)
Exercise 4: Connect the Minimal Server to Claude Code and Verify
Type: Integration / DevOps
Time: 20–30 minutes
Prereq: Have Claude Code installed; minimal server code working
Task
Go through the full integration flow end-to-end:
- Register the minimal server in
~/.claude/settings.json - Verify the connection with
/doctor - Confirm the tools appear with their MCP prefixed names
- Run 3 test queries that exercise each tool and the resource
Step-by-Step
Step 1: Add to ~/.claude/settings.json:
{
"mcpServers": {
"minimal": {
"command": "python",
"args": ["/absolute/path/to/server.py"]
}
}
}Step 2: Open a new Claude Code session. Run:
/doctor
Look for minimal server. Note whether it shows “connected” or an error.
Step 3: Ask Claude:
List all the MCP tools you have access to right now.
Verify you see mcp__minimal__add_numbers and mcp__minimal__get_current_time.
Step 4: Run these test queries:
Add the numbers 17 and 25 using the add_numbers tool.
What's the current time in London?
Read the memo://notes resource and summarize it.
Step 5: Document any issues you hit and how you resolved them.
Troubleshooting Checklist
- Path to
server.pyis absolute -
pythonin PATH (check withwhich python) -
mcppackage installed in Python environment Claude Code uses - No syntax errors in
server.py(test:python server.pyshould hang, not crash) -
settings.jsonis valid JSON (test withpython -m json.tool ~/.claude/settings.json)
Exercise 5: Interview Simulation — Design the GitHub Workflow Assistant
Type: System design / Interview prep
Time: 45–60 minutes (recommended: time-box to 45 min)
Prereq: Read all of ../README.md
The Prompt
“Design the tool layer for an AI assistant that can manage a developer’s GitHub workflow. The assistant should be able to handle day-to-day GitHub tasks: reviewing PRs, managing issues, checking CI status, and helping with releases. Walk me through your tool design decisions.”
This is a real interview question at companies building AI developer tools.
What a Strong Answer Covers
1. Requirements clarification (2–3 minutes)
Before designing tools, clarify scope:
- Is this read-only (review, report) or read-write (create, merge)?
- Single repo or multi-repo?
- Personal workflow or team workflow?
- What level of GitHub access does the agent need?
2. Tool inventory (10–15 minutes)
Design tools for these capability areas:
| Area | Operations |
|---|---|
| Pull Requests | list, get details, review, approve, merge, get diff |
| Issues | list, create, comment, close, label, assign |
| CI/CD | get workflow runs, get job logs, trigger workflow |
| Repository | list branches, get file contents, search code |
| Releases | list releases, create release, get changelog |
For each tool:
- Name (verb_noun)
- Key parameters and their types
- What it returns
- Whether it’s idempotent
3. Design principles you applied (5 minutes)
Explain your decisions:
- Why you separated
list_pull_requestsfromget_pull_request(minimal surface area) - Why you used enum for
stateparameter (open,closed,merged) vs free string - How you handle the PR diff tool — does it return raw diff or structured hunks?
- What you did about dangerous operations (
merge_pull_request,delete_branch)
4. MCP vs raw API tool use (3 minutes)
Would you build this as an MCP server or embed tools in the application?
- If it’s a reusable GitHub integration for any Claude project → MCP server
- If it’s specific to one internal tool → raw API tool use
- What’s the right transport? (stdio for local dev tools, SSE for shared team server)
5. Security considerations (3 minutes)
- GitHub token scope: request only the permissions needed (no
adminscope if you only needrepo) - Read operations vs write operations: separate tools, not a mode parameter
- Audit logging: every write operation should log what was changed
- Rate limiting: GitHub API has limits — how does your tool handle
429responses?
Deliverable
Write up your answer covering all five areas. Include:
- A complete list of tool names with brief descriptions (one sentence each)
- 3 full JSON tool schemas (your choice of which ones — pick the most interesting)
- A paragraph on whether you’d build this as MCP or raw tool use, and why
- One thing you would do differently if this were a team tool vs a personal tool
Evaluation Rubric
| Dimension | What to Look For |
|---|---|
| Naming | snake_case, verb_noun, specific |
| Minimal surface area | Separate create/update/delete; no god tools |
| Schema quality | Enum for constrained strings, clear descriptions, proper required |
| Return value design | Structured data, error handling, not raw strings |
| Security thinking | Mentions token scopes, dangerous op handling |
| MCP knowledge | Can explain when MCP adds value vs complexity |
| Trade-offs | Acknowledges design choices and their consequences |