Minimal MCP Server
A minimal MCP server for learning. Exposes two tools and one resource over stdio transport — the standard protocol for Claude Desktop and Claude Code.
What It Exposes
Tools:
add_numbers(a, b)— returns the sum of two numbersget_current_time(timezone?)— returns the current time (optional timezone, defaults to UTC)
Resources:
memo://notes— a hardcoded project memo in plain text
Setup
Option A: uv (recommended)
If you have uv installed, the script is self-contained with inline dependencies:
uv run server.pyThis installs mcp in an isolated environment and runs the server.
Option B: pip + venv
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install "mcp>=1.0.0"
python server.pyRunning the Server
When you run the server directly, it will appear to hang — this is correct. The server is waiting for MCP messages on stdin. In normal usage, Claude Code spawns and manages the process; you never run it manually.
python server.py
# (waits for MCP protocol messages on stdin — Ctrl+C to exit)Connecting to Claude Code
Add the server to your Claude Code settings. Edit ~/.claude/settings.json:
{
"mcpServers": {
"minimal": {
"command": "python",
"args": ["/absolute/path/to/04-tools-and-mcp/examples/mcp_server_minimal/server.py"]
}
}
}Important: Use the absolute path to server.py. Relative paths may not work because Claude Code’s working directory when spawning the process may differ from your shell’s current directory.
If using uv:
{
"mcpServers": {
"minimal": {
"command": "uv",
"args": [
"run",
"--script",
"/absolute/path/to/04-tools-and-mcp/examples/mcp_server_minimal/server.py"
]
}
}
}To find the absolute path:
cd 04-tools-and-mcp/examples/mcp_server_minimal
pwd
# Copy the output and append /server.pyVerifying It Works
After saving settings.json, restart Claude Code (or open a new session).
Method 1: /doctor command
In a Claude Code session, run:
/doctor
Look for the minimal server in the MCP section. It should show as connected. If it shows an error, see the troubleshooting section below.
Method 2: Ask Claude to use the tools
In a Claude Code session, ask:
What MCP tools do you have available?
You should see:
mcp__minimal__add_numbersmcp__minimal__get_current_time
Then test them:
Use the add_numbers tool to add 42 and 58.
What time is it in Tokyo right now?
Method 3: Check for tool names directly
The naming convention is mcp__<server-name>__<tool-name>. Since we named the server "minimal", the tools appear as:
mcp__minimal__add_numbersmcp__minimal__get_current_time
Troubleshooting
Server not appearing in /doctor
-
Check the path in
settings.jsonis absolute and correct:ls /absolute/path/to/server.py # should show the file -
Check that
python(oruv) is in your PATH:which python # Use this full path in settings.json if needed -
Try running the server manually to see if there are import errors:
python /absolute/path/to/server.py # Should hang (waiting for MCP messages), not crash
ModuleNotFoundError: No module named 'mcp'
Install the dependency:
pip install "mcp>=1.0.0"Or switch to the uv configuration which handles this automatically.
Tools appear but calls fail
Check the Claude Code logs. MCP server stderr is usually captured and shown in the logs. Add print statements to stderr for debugging:
import sys
print("Debug: call_tool called with", name, arguments, file=sys.stderr)Understanding the Code
The server is heavily commented to explain each concept. Key sections in server.py:
| Section | What it teaches |
|---|---|
Server("minimal") | Creating a server instance and naming it |
@server.list_tools() | Declaring available tools (called on connect) |
@server.call_tool() | Handling tool invocations |
@server.list_resources() | Declaring available resources |
@server.read_resource() | Returning resource content |
stdio_server() + server.run() | Running the server with stdio transport |
InitializationOptions | Declaring server identity and capabilities |
Next Steps
Once you have this working, try:
- Add a third tool — e.g.,
multiply_numbers(a, b) - Add a second resource — e.g.,
config://settingsreturning a JSON config object - Make the
memo://notesresource read from an actual file on disk - See Exercise 3 in
exercises/README.mdfor a guided extension