mcp-toolkit-server
README.md
# mcp-toolkit-server
A custom [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server exposing a small, composable registry of tools, resources, and prompts — the pattern behind "wrap it once, every agent gets access" enterprise tool integration.
[](https://github.com/varunram3232-glitch/mcp-toolkit-server/actions/workflows/ci.yml)


## Why this exists
MCP is the standardized layer that lets an agent framework (LangGraph, Claude Agent SDK, a custom orchestrator) discover and call tools without bespoke integration code per agent. I've built MCP server implementations against internal enterprise systems (knowledge bases, policy document APIs, compliance tools) in production; this project is a small, self-contained MCP server built from scratch to show the same pattern — a tool/resource/prompt registry with real JSON Schema contracts — in a form that's inspectable end to end.
## What it exposes
MCP defines three primitive types. This server implements all three:
| Type | Name | What it does |
|---|---|---|
| **Tool** | `calculate` | Evaluates a numeric expression via a whitelisted AST walk (not `eval`) |
| **Tool** | `search_knowledge_base` | Keyword-overlap search over a bundled document set |
| **Tool** | `text_stats` | Character/word/sentence counts and estimated reading time |
| **Resource** | `kb://documents` | Lists available knowledge-base document names |
| **Resource** | `kb://document/{name}` | Fetches one document's full text (URI template) |
| **Prompt** | `summarize_document` | A reusable, parameterized prompt template |
## Installation
```bash
git clone https://github.com/varunram3232-glitch/mcp-toolkit-server.git
cd mcp-toolkit-server
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
```
## Running the server
Over stdio (the transport Claude Desktop and most local MCP clients use):
```bash
mcp-toolkit-server
```
With the MCP Inspector, for interactive development:
```bash
mcp dev src/mcp_toolkit/server.py
```
**Connecting it to Claude Desktop** — add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"toolkit": {
"command": "/absolute/path/to/.venv/bin/mcp-toolkit-server"
}
}
}
```
## Example: calling it programmatically
Tools are callable through the FastMCP server object directly (useful for testing, or for embedding this server's logic in another Python process without a subprocess transport):
```python
import asyncio
from mcp_toolkit.server import mcp
async def main():
result = await mcp.call_tool("calculate", {"expression": "2 * (3 + 4) / 7"})
print(result[0].text) # "2.0"
docs = await mcp.call_tool("search_knowledge_base", {"query": "tool schema"})
print(docs[0].text)
asyncio.run(main())
```
## Design decisions
- **A real AST walk for `calculate`, never `eval`.** Tool arguments come from a language model's interpretation of a user prompt — treating that as trusted input to `eval()` is a textbook injection risk. `calculator.py` parses the expression into an AST and only evaluates a fixed whitelist of numeric operators; anything else (`__import__`, attribute access, comprehensions, name lookups) is rejected before it ever executes.
- **The description field is the real interface.** A tool's JSON Schema tells a model what arguments are *valid*; the natural-language description is what tells it *when to call the tool at all*. Every tool and the server's top-level `instructions` are written to be specific about that ("use `calculate` instead of computing it yourself") rather than a generic one-liner.
- **Resources vs. tools, used for what each is for.** The knowledge base is exposed as a *resource* (`kb://document/{name}`) so a client can attach a specific document to context deliberately (like a file picker), separately from `search_knowledge_base`, which is a *tool* the model decides to invoke based on the conversation. Collapsing these into one mechanism is a common MCP design mistake this repo deliberately avoids.
- **Dependency-free knowledge base.** Search here is keyword overlap, not embeddings — this repo is about the MCP server/tool-registry pattern, not retrieval quality. See [agentic-rag-assistant](https://github.com/varunram3232-glitch/agentic-rag-assistant) for a real embedding-based RAG pipeline that a production version of this tool would call into.
## Testing
```bash
pip install -e ".[dev]"
pytest -v
ruff check src tests
```
39 tests, split across two layers:
- **Unit tests** for the pure logic (`test_calculator.py`, `test_knowledge_base.py`, `test_text_stats.py`) — including a dedicated set of injection-attempt expressions (`__import__`, `open(...)`, list comprehensions) that the calculator must reject.
- **Protocol-level integration tests** (`test_server_integration.py`) that call the real FastMCP server object's `list_tools` / `call_tool` / `list_resources` / `read_resource` / `list_prompts` / `get_prompt` — verifying the MCP contract itself, not just the functions behind it.
## Project structure
```
src/mcp_toolkit/
├── server.py # FastMCP instance — tool/resource/prompt registration
├── tools/
│ ├── calculator.py # AST-walking safe expression evaluator
│ ├── knowledge_base.py # In-memory document store + keyword search
│ └── text_stats.py # Text analysis
└── data/ # Bundled knowledge-base documents
```
## Roadmap
- [ ] Streamable HTTP transport for remote deployment
- [ ] Auth middleware example (API key / OAuth) for a non-stdio deployment
- [ ] A tool that calls out to agentic-rag-assistant for embedding-based search
## License
MIT — see [LICENSE](LICENSE).
TDQS
A4.1/5.0
Scored across 3 tools
Disambiguation5/5
Each tool performs a completely distinct function: arithmetic evaluation, knowledge base search, and text statistics. There is no overlap or ambiguity between them.
Naming Consistency4/5
Names are mostly verb_noun (search_knowledge_base) or single verb (calculate), but text_stats breaks the pattern as noun_noun. Still, all are snake_case and clearly readable.
Tool Count4/5
With 3 tools, the server is on the low end of typical scope but appropriate for a small utility toolkit. Each tool is useful and not redundant.
Completeness3/5
The server lacks a coherent domain, covering arithmetic, knowledge search, and text stats. While each tool is self-contained, the set feels arbitrary and could benefit from more utility categories.
Maintenance
ActivitySlowing
ResponsivenessNo issues