Skip to main content
Glama
m-rocafort

week7-mcp-server

by m-rocafort
README.md
# Week 7 — MCP (giving an agent its own tools, the standard way)

Part of my journey leveling up automation skills as the field shifts toward AI.
This week is about **Model Context Protocol (MCP)** — instead of pasting data
into a prompt, the LLM is handed a small set of tools and decides for itself
which ones to call, in which order, to answer a question.

> **Why this week matters:** hardcoding data into a prompt doesn't scale past a
> demo. MCP is the emerging standard for "here are your tools and data sources,
> go" — it's what lets an agent plug into a *live* system instead of a snapshot.

## The architecture

```
[client.py asks a question]
            |
            v
[Claude sees 3 tool definitions — no invoice data in the prompt]
            |  decides which tools to call, with what arguments
            v
[MCP server (server.py): list_invoices / get_invoice / get_purchase_order]
            |  returns live data over stdio
            v
[Claude reasons over the results and answers]
```

**Continuity:** Week 2 *extracted* an invoice from a document. Week 6 *decided*
what to do with an invoice exception, given data the bot already had in hand.
This week, the data itself lives behind an MCP server — an agent fetches its
own facts instead of having them handed to it. Combine this with Week 6's
policy-guarded decision service and you get Portfolio Project #4 on the
roadmap: an agent that both looks things up *and* hands the judgement call to
an auditable rule layer.

## What's here

| File | Role |
|------|------|
| `src/server.py` | The MCP server — exposes `list_invoices`, `get_invoice`, `get_purchase_order` |
| `src/client.py` | Spawns the server and drives it from Claude — no server process to start separately |
| `tests/test_server_tools.py` | Unit tests for the tool functions — no API key needed |

## 1. Run it

```powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txt
# copy .env.example to .env and paste your key

python src/client.py
```

`client.py` spawns `server.py` itself over stdio (that's how MCP typically
works for local tools — no HTTP port to manage, unlike Week 6). Watch the
`[MCP tool call: ...]` lines print as Claude decides what it needs to look up.

Run the unit tests for the tool logic (no API key needed):
```powershell
pytest
```

## 2. The key design idea: tools + data over a standard protocol

Before MCP, "give the model a tool" meant hand-rolling a JSON schema and a
dispatch `if/elif` for every project. MCP standardizes that: `server.py`
declares tools once with `@mcp.tool()`, and *any* MCP-speaking client — this
repo's `client.py`, Claude Desktop, another team's agent — can discover and
call them the same way. That's the "connecting agents to real tools" pitch:
the tool-provider and the agent don't need to be written by the same person.

## 3. What I learned / production-ready vs. not

- MCP separates *who owns the data* from *who reasons about it* — the server
  doesn't know or care which LLM is calling it.
- Model choice matters here too: this is orchestration (deciding which tool to
  call), not hard judgement, so a cheap/fast model (`claude-haiku-4-5`) is the
  right call — save Opus-tier reasoning for the kind of decision Week 6 makes.
- **Not production-ready:** the "ERP" is an in-memory dict, the server has no
  auth, and there's no real MCP resource (only tools) — a production version
  would likely expose invoice records as MCP *resources* too. (Weeks 8+.)

## Stack

Python · **MCP** (official Python SDK) · **Claude** (`claude-haiku-4-5` for
tool-driving) · Anthropic SDK · pytest