MCP Toolbox
# MCP Toolbox — a Model Context Protocol server + client, from scratch
[](https://github.com/zahrafatima9432/mcp-toolbox/actions/workflows/ci.yml)
[](https://www.python.org/)
[](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.

- **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
Scored across 6 tools
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.
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.
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.
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.