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 numbers
  • get_current_time(timezone?) — returns the current time (optional timezone, defaults to UTC)

Resources:

  • memo://notes — a hardcoded project memo in plain text

Setup

If you have uv installed, the script is self-contained with inline dependencies:

uv run server.py

This 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.py

Running 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.py

Verifying 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_numbers
  • mcp__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_numbers
  • mcp__minimal__get_current_time

Troubleshooting

Server not appearing in /doctor

  1. Check the path in settings.json is absolute and correct:

    ls /absolute/path/to/server.py   # should show the file
  2. Check that python (or uv) is in your PATH:

    which python
    # Use this full path in settings.json if needed
  3. 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:

SectionWhat 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
InitializationOptionsDeclaring server identity and capabilities

Next Steps

Once you have this working, try:

  1. Add a third tool — e.g., multiply_numbers(a, b)
  2. Add a second resource — e.g., config://settings returning a JSON config object
  3. Make the memo://notes resource read from an actual file on disk
  4. See Exercise 3 in exercises/README.md for a guided extension