Labs / Lab 03b

Build a real MCP server

Use the official Python MCP SDK to expose two tools over stdio, then wire them into a real host. By the end you will have called your own tools from an AI assistant.

What this adds

Lab 03 taught the concept. This is the real thing.

The toy protocol from Lab 03 showed you the shape: discovery, schema, invocation. Real MCP uses that same shape, but with a standard SDK, a defined transport (stdio or HTTP), and actual host support.

When this lab works, any MCP-compatible host — Claude Desktop, Cursor, Copilot CLI, Goose — can discover and call your tool without any custom integration code.

Run it

cd ai_ecosystem_labs
python3 03b-real-mcp/server.py
Starting here? Quick setup
git clone https://github.com/BanditF/ai_ecosystem_labs
cd ai_ecosystem_labs
python3 -m pip install "mcp[cli]"
python3 03b-real-mcp/server.py
# In a second terminal:
mcp dev 03b-real-mcp/server.py

Requires Python 3.10+, mcp[cli], and one MCP-compatible host installed (see Step 4).

Time guide. Setup: 5–15 min for the SDK and host wiring. Working through it: 20–40 min depending on whether you already use an MCP-compatible host.

Step 1: Install the SDK

pip install mcp

The MCP Python SDK is the official library from Anthropic. It handles the protocol wire format, tool registration, and transport — you just define your tools.

Step 2: Define two tools

Create server.py. This server exposes two tools: count_words and reverse_text. One counts words. The other reverses a string. That is still small enough to see the whole shape at once, but it makes the registry feel more real.

FastMCP handles discovery, schema generation, and the stdio transport. Each @mcp.tool() decorator registers a callable tool with a name, description (from the docstring), and typed parameters (from the signature). In this example, the host should see both count_words and reverse_text.

What to notice

The docstring becomes the tool description a host shows to the model. That description is how the model decides when to call this tool. Write it as if you are explaining the tool to someone who has never seen your code.

Step 3: Test it locally

Run the server directly to confirm it starts without errors:

python server.py

It will appear to hang — that is correct. The server is running over stdio, waiting for a host to connect. Hit Ctrl+C to stop it.

To inspect what the server exposes without a host, use the MCP CLI:

pip install "mcp[cli]"
mcp dev server.py

This opens the MCP Inspector in your browser — a tool that lets you list tools, call them manually, and see the raw JSON going back and forth.

Step 4: Wire it to a host

Pick one host. Each needs a config entry that tells it where to find your server.

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "word-counter": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

Restart Claude Desktop. The tool will appear in the tool menu.

Cursor

Open Settings → MCP and add a new server entry:

{
  "name": "word-counter",
  "command": "python /absolute/path/to/server.py"
}

Reload the window. The tool will be available in Cursor's agent mode.

Step 5: Call it

In Claude Desktop or Cursor, open a chat and ask:

"How many words are in this sentence: The quick brown fox jumps over the lazy dog?"

The model will decide to call count_words, pass the sentence as text, and return your result. You should see the tool call appear in the chat — most hosts show a collapsed tool-call badge.

If the tool does not appear

  • Check the path in the config — it must be absolute.
  • Confirm python server.py runs without errors.
  • Restart the host after any config change.
  • Check the host's MCP logs: Claude Desktop logs to ~/Library/Logs/Claude/.

Try this

Three things to try before moving on.

  1. Call reverse_text with a long phrase. Try something longer than a one-word test, then compare the result shape to count_words. The operations are different, but the JSON envelope still looks familiar because the protocol layer stays consistent.
  2. Add a third tool to server.py. For example, create a word_count tool that returns the unique-word count for a string, restart the server, and run tools/list again. You should see it appear automatically from the decorator registration.
  3. Point a different MCP host at the same server. If you have Claude Desktop, Cursor, Goose, or another MCP-compatible client, connect it to this exact server.py and call the same tools from there. The useful thing to notice is that the server does not care which host is on the other end.

When to use MCP — and when not to

Use MCP when

  • You want your tool to work across multiple hosts (Cursor, Claude Desktop, Goose, etc.) without custom integration per host
  • You are building a tool server for a team or product
  • You want clean separation between the tool and the model host

Skip MCP when

  • You only need one tool in one app you fully control
  • Your provider's native function-calling already covers the use case
  • The host you are targeting has built-in tools that do the job
  • You want the simplest possible thing first — plain function calling is fine

Read alongside it

The conceptual background is on the protocols page. For the full MCP spec: modelcontextprotocol.io.