Skip to main content
Glama
zahrafatima9432

MCP Toolbox

README.md
# MCP Toolbox — a Model Context Protocol server + client, from scratch

[![CI](https://github.com/zahrafatima9432/mcp-toolbox/actions/workflows/ci.yml/badge.svg)](https://github.com/zahrafatima9432/mcp-toolbox/actions/workflows/ci.yml)
[![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

A clean, well-documented reference implementation of the **Model Context Protocol (MCP)**: a server that exposes three *real* tools, and a client agent that discovers and calls them.

![Demo of the client running every tool over MCP](demo.gif)

- **Document store** — add, search (keyword-ranked), fetch, and list text documents. Persists to disk.
- **Web search** — real web results via DuckDuckGo with a Wikipedia fallback. **No API key required.**
- **Calculator** — safe arithmetic (no `eval`; expressions are parsed and evaluated against a strict whitelist).

The client works in **two modes**:

| Mode | Needs an API key? | What it does |
|------|-------------------|--------------|
| **Offline** (default) | No | Runs a scripted demo that exercises every tool and prints the results. Proves the full MCP round-trip. |
| **LLM** | Yes (`ANTHROPIC_API_KEY`) | Hands the MCP tools to Claude and runs an agent loop; the model decides which tools to call. |

The same client code drives both modes — only *who decides which tool to call* changes. That is the core idea of MCP: the tool interface is uniform, so a simple scripted caller and a smart LLM caller use it identically.

---

## Quickstart (60 seconds, no API key)

You need **Python 3.10+**.

```bash
# 1. From the project folder, create a virtual environment
python -m venv .venv

# 2. Activate it
source .venv/bin/activate          # macOS / Linux
# .venv\Scripts\Activate.ps1        # Windows PowerShell
# .venv\Scripts\activate.bat        # Windows cmd.exe

# 3. Install
pip install -e .

# 4. Run the offline demo — launches the server and calls every tool
python -m mcp_toolbox.client
```

You'll see the client discover the server's tools and then run the calculator, the document store (search → add → fetch), and a web search, printing the structured result of each call.

Just want to see the tools the server exposes?

```bash
python -m mcp_toolbox.client --list
```

---

## Turning on LLM mode (optional)

This lets **Claude** decide which tools to call to answer a question.

```bash
# Install the Anthropic SDK
pip install anthropic

# Add your key (get one at https://console.anthropic.com/ — pay-as-you-go)
cp .env.example .env
# then edit .env and set ANTHROPIC_API_KEY=sk-ant-...

# Ask a question; the agent will use the tools as needed
python -m mcp_toolbox.client --mode llm "What's stored about MCP transports, and what is 23 * 19?"
```

A demo run costs a fraction of a cent. Without a key, everything else still works — the client simply falls back to the offline demo.

---

## Run it with Docker (no Python setup)

If you'd rather not install anything locally, the demo runs in a container:

```bash
docker build -t mcp-toolbox .
docker run --rm mcp-toolbox
```

For LLM mode, pass your key in and override the command:

```bash
docker run --rm -e ANTHROPIC_API_KEY=sk-ant-... mcp-toolbox \
  python -m mcp_toolbox.client --mode llm "What is MCP, and what is 12*9?"
```

---

## Project layout

```
mcp-toolbox/
├── README.md
├── demo.gif                  # the animation shown above
├── Dockerfile                # run the demo in a container
├── pyproject.toml            # packaging + console scripts + pytest config
├── requirements.txt
├── .env.example              # copy to .env for LLM mode
├── run_demo.sh               # convenience script: venv + install + demo
├── claude_desktop_config.example.json   # use the server from Claude Desktop
├── .github/workflows/ci.yml  # runs the tests on every push (Linux + Windows)
├── docs/
│   └── ARCHITECTURE.md       # how MCP works and how this repo maps to it
├── src/mcp_toolbox/
│   ├── server.py             # the MCP server (FastMCP over stdio)
│   ├── client.py             # the client agent (offline + LLM modes)
│   └── tools/
│       ├── calculator.py     # safe AST-based arithmetic
│       ├── document_store.py # in-memory store + keyword search + JSON persistence
│       └── web_search.py     # keyless web search (DuckDuckGo → Wikipedia)
└── tests/
    ├── test_calculator.py
    ├── test_document_store.py
    └── test_server_integration.py   # launches the real server over stdio
```

The tools in `tools/` are **pure Python with no MCP dependency**, so they're unit-testable in isolation and reusable elsewhere. `server.py` is a thin layer that exposes them as MCP tools; `client.py` is a thin layer that consumes them.

---

## The tools in detail

### Calculator — `calculate(expression)`
Parses the expression into Python's AST and walks it, allowing only whitelisted node types, constants (`pi`, `e`, `tau`) and functions (`sqrt`, `sin`, `log`, `factorial`, …). There is **no path to `eval`, imports, or attribute access**, so it's safe to expose to an autonomous agent. Division by zero and malformed input come back as structured errors.

### Document store — `add_document`, `search_documents`, `get_document`, `list_documents`
An in-memory store that persists to a JSON file (`~/.mcp_toolbox/documents.json` by default; override with the `MCP_TOOLBOX_DB` env var). Search uses a transparent keyword scorer — term frequency in the body, with a ×3 boost for title matches and ×2 for tag matches — and returns ranked hits with snippets. It's deliberately simple so the ranking is easy to read; swapping in a vector store later wouldn't change the tool surface. The server seeds a few documents about MCP on first run so search has something to find.

### Web search — `web_search(query, limit)`
Queries the **DuckDuckGo Instant Answer API**, falling back to the **Wikipedia search API**. Both are free and keyless. Network failures are caught and returned as an `error` field rather than raised, so the agent can react gracefully. (Some restricted/corporate networks block these endpoints; the tool degrades cleanly if so.)

---

## Running the tests

```bash
pip install pytest pytest-asyncio
pytest
```

The suite covers the calculator, the document store, and — importantly — an **end-to-end integration test that launches the real MCP server as a subprocess and calls its tools through a real MCP client session**, proving tool discovery, argument passing, and structured results all work over stdio.

---

## Using the server from Claude Desktop (or any MCP host)

Because this is a standard MCP server, any MCP-compatible host can use it — not just the bundled client. See `claude_desktop_config.example.json` for a ready-to-adapt config; point the `command`/`args` at your Python and the `-m mcp_toolbox.server` module, and the three tools show up in the host.

---

## How it works (the short version)

MCP standardizes how an AI application talks to external tools. A **host** runs a **client**, and the client speaks MCP to one or more **servers**; each server advertises **tools** (plus resources and prompts). Here:

- `mcp_toolbox.server` is the **server** — it advertises six tools over the **stdio** transport.
- `mcp_toolbox.client` is the **client** — it launches the server as a subprocess, calls `initialize`, `list_tools`, and `call_tool`, and either scripts the calls or lets an LLM choose them.

For the full walkthrough, see [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).

---

## License

MIT — see [LICENSE](LICENSE).

TDQS

A4.2/5.0

Scored across 6 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: document search, document creation, document retrieval, document listing, web search, and calculation. Even search_documents and list_documents are well differentiated by query-driven search versus full listing.

Naming Consistency4/5

Most tools follow a clear verb_noun pattern (add_document, get_document, list_documents, search_documents). web_search inverts that order and calculate uses only a verb, but the overall naming is still readable and predictable.

Tool Count5/5

Six tools is a reasonable, focused size for a document management helper with optional web search and calculation utilities. Each tool contributes a distinct capability without redundancy or bloat.

Completeness3/5

The document lifecycle is incomplete: add, get, list, and search are covered, but there is no update or delete operation for stored documents, which is a notable gap for a document store. web_search and calculate are one-shot utilities and don't need additional operations.

Maintenance

ActivityMaintained
ResponsivenessNo issues