mcp-toolserver
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-toolserverWhat's our refund policy after 20 days, and what would 14 months of Pro cost with a 15% discount?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-toolserver — an MCP server built the way a production one should be
A Model Context Protocol server exposing four tools to an LLM: a grounded document search, a document fetch, a sandboxed calculator and date arithmetic.
The tools themselves are small on purpose. What this repository actually demonstrates is the part that decides whether tool calling works in production: strict schemas, errors written for the model, and a protocol layer thin enough that the logic underneath stays testable.
git clone https://github.com/talhayme/mcp-toolserver
cd mcp-toolserver
pip install -e ".[dev]"
pytest -q # 50 tests, no SDK and no API key requiredConnect it to Claude Desktop
pip install -e ".[mcp]"Then add to claude_desktop_config.json:
{
"mcpServers": {
"toolserver": {
"command": "python",
"args": ["-m", "mcp_toolserver.server"]
}
}
}Restart Claude Desktop and ask it something like "What's our refund policy
after 20 days, and what would 14 months of Pro cost with a 15% discount?" —
it will call search_documents and then calculate.
Related MCP server: rag-tool-agent
The tools
Tool | Purpose |
| Ranked excerpts from the knowledge base, with a grounding instruction attached |
| Full text of one document by id |
| Arithmetic evaluated exactly, in a sandbox |
| Days between two ISO dates |
The last two exist because they are precisely what language models get wrong unaided: arithmetic and date maths.
Three decisions worth explaining
1. Errors are written for the model, not for a log
Every failure path raises ToolError with a message the model can act on:
search_documents("refund", team="marketing")
# ToolError: unknown team 'marketing'; known teams are: engineering, people, sales, support
dispatch("calculate", {"expr": "2+2"})
# ToolError: invalid arguments for 'calculate': calculate() got an unexpected keyword argument 'expr'A model that receives "known teams are: engineering, people, sales, support" fixes its next call. A model that receives a stack trace retries the same mistake, or gives up and invents an answer. The protocol layer returns the message as tool output rather than raising, so the conversation continues instead of failing.
2. The calculator is a sandbox, not an eval
eval() on a model-generated string is remote code execution with extra
steps. This implementation applies four separate restrictions:
calculate("__import__('os').system('rm -rf /')") # ToolError: forbidden token
calculate("().__class__.__bases__[0].__subclasses__()") # ToolError: forbidden token
calculate("open('/etc/passwd').read()") # ToolError: name 'open' is not allowed
calculate("1 / 0") # ToolError: division by zeroA character allow-list, a __ ban, compile() with every name checked
against an explicit maths allow-list, and eval with __builtins__
emptied. Five escape attempts are in the test suite as regression tests.
3. Schemas are strict, so the model gets it right first time
{
"limit": { "type": "integer", "minimum": 1, "maximum": 20, "default": 3 },
"team": { "type": "string", "enum": ["support", "sales", "engineering", "people"] },
"additionalProperties": false
}additionalProperties: false stops the model inventing parameters. Bounds
and enums turn a class of runtime errors into something the client catches
before the call is made. A test asserts that every tool has a description,
every property is documented, and every schema is strict — vague schemas
cause retry loops that cost tokens and latency.
Grounding travels with the data
search_documents returns an instruction alongside its results:
{
"count": 0,
"results": [],
"note": "No documents matched. Say so rather than answering from memory."
}An empty search is the moment a model is most likely to hallucinate. Putting the instruction in the payload means it cannot drift out of sync with the system prompt, and it is right where the model is looking.
Architecture
mcp_toolserver/
tools.py # pure functions, zero MCP imports — all the logic lives here
server.py # schemas, dispatch, ToolError -> model-readable output. Thin.tools.py imports nothing from the MCP SDK, which is why the test suite runs
without it and why the same functions could be served over HTTP tomorrow
without a rewrite. build_server() imports the SDK lazily and fails with an
actionable message when it is missing:
RuntimeError: The MCP SDK is not installed. Install it with:
pip install 'mcp-toolserver[mcp]'
The tool logic and its tests run without it.Tests
pytest tests/ -q --cov=mcp_toolserver --cov-report=term-missing
# 50 passed — 84% coverageCovering: ranking and prefix matching, the empty-result grounding path, every validation branch, five sandbox-escape attempts, leap-year date handling, dispatch with wrong and missing arguments, and a schema completeness check.
CI runs the suite on Python 3.10/3.11/3.12, then installs the real MCP SDK, builds the server, asserts the registered tool names match the declared schemas, and makes a live call through the protocol layer — including a failing one, to confirm errors come back readable rather than as a crash.
That job earned its place: the first version of this server was written against an older SDK decorator API and CI caught it on the first push.
Why this exists
I build custom MCP servers connecting LLMs to internal tools and services as part of my AI-implementation consulting work. This is a standalone, readable extract of the patterns I use — not production code, and deliberately small enough to read in one sitting.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Public web tools for agents: product extraction, claim checks, webpage QA and ranked audits.
Pay-per-call agent tools: PDF, OCR, translation, news search, prediction markets, web, chain reads.
Pay-per-call tools for AI agents, paid in USDC on Base or Solana. Web search, page and PDF reading, crypto news, Hyperliquid perps, market pulse, Chainlink price history, token checks, SEC filings, weather and more. Free calls every day, and a failed call costs nothing. Works with Tollkit Wallet.
Search Fragments — two tools for the queries an agent can't place, both built to decline rather than guess. resolve_fragment takes a half-remembered, cross-source query ("a musician who became famous for stopping performing") and returns a grounded answer, ranked web sources to confirm by eye, or an explicit no-resolution. verify_claim takes a specific factual assertion and returns supported, partially_supported, insufficient_evidence, or unsupported, with cited evidence and a stated_limits field that is always present. There is no confidence score — insufficient_evidence fires freely, and unsupported requires a source that explicitly contradicts, never mere absence of confirmation. Every verdict is decide-by-eye: "supported" means current web sources confirm it, not that the claim is true. Calibrated against 18 known claims before release. Free, no signup. Streamable HTTP (MCP 2025-11-25). Read-only.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceProvides exact, deterministic tools for math, dates, units, validation, and more to AI agents, returning precise answers with explicit assumptions and warnings instead of model guesses.MIT
- FlicenseNot gradedqualityCmaintenanceEnables MCP-compatible clients to perform retrieval-grounded Q&A over FordA dataset notes and safe arithmetic calculations through callable tools.-
- AlicenseAqualityBmaintenanceEnables AI agents to perform exact, reproducible mechanical computation — text diffing, hashing, JSON querying, calendar arithmetic, deadline-bounded regex extraction, CSV parsing, similarity scoring, unit and base conversion — instead of relying on probabilistic guessing. Runs dependency-free, read-only and offline over stdio, with schema validation and hard caps on every operation so no call can hang the client.1538 npmMIT
- FlicenseAqualityCmaintenanceEnables an LLM client to perform basic arithmetic on two numbers — addition, subtraction, multiplication, and division — plus reverse a supplied text string, through five simple tools exposed over stdio. Inputs and structured text results follow JSON schemas, with invalid operations such as division by zero surfaced as tool errors.5-