mcp-tools-server

mcp-tools-server

An MCP server that provides developer knowledge and utility tools including local documentation search, text summarization, safe arithmetic calculation, unit conversion, and knowledge-base resources.

Category
Visit Server

README

mcp-tools-server — developer knowledge & utilities over MCP

A small, coherent Model Context Protocol (MCP) server that gives an LLM client a useful toolbox: search a local docs knowledge base, summarize text, do safe arithmetic, and convert units — plus knowledge-base resources and a prompt template. It speaks the stdio transport that Cursor and Claude Desktop use and an optional HTTP/SSE transport (great for docker compose up and reaching it "like an API"), and it ships with a local client harness so you can watch it work without any host and with zero API keys.

python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements-dev.txt
python -m mcp_tools.demo_client     # spins up the server and calls every tool

What is MCP, and why it matters (2026)

Modern AI apps are only as good as the context and tools you can feed the model. Before MCP, every tool integration was a bespoke, per-app, per-vendor affair. The Model Context Protocol is the open standard that fixed that: you write one server that advertises tools (functions the model can call), resources (readable context addressed by URI), and prompts (reusable templates), and any MCP-aware host — Cursor, Claude Desktop, and a growing list of others — can use it. Capability is decoupled from model: a tool you write once works across hosts and across model vendors. That is why, in 2026, MCP has become the common integration layer for AI developer tooling.

Crucially, MCP is a protocol, not an agent framework. There is no LangGraph, no orchestration loop, and no planner in this repo. The server just publishes capabilities and answers JSON-RPC requests; the client's model decides when to call them. (See Design decisions.)

Architecture

                            JSON-RPC 2.0 over stdio
 ┌───────────────────────┐  ───── request  ─▶ stdin ─┐   ┌──────────────────────────────┐
 │   MCP host + client   │                           └──▶│      dev-knowledge-tools       │
 │                       │                               │          MCP server            │
 │  Cursor / Claude      │                               │      (mcp.server.fastmcp)      │
 │  Desktop, or the      │◀─┐                            │                                │
 │  bundled demo_client  │  └── response ◀─ stdout ◀─────│  tools                         │
 └───────────────────────┘                               │   • search_docs ─▶ IDF search  │
                                                         │   • summarize   ─▶ LLM layer   │
   The host launches the server as a                     │   • calculate   (safe AST)     │
   subprocess. Requests go to its stdin,                 │   • convert_units              │
   responses come back on its stdout, one                │  resources                     │
   JSON object per line. The server logs                 │   • kb://index                 │
   to stderr so the protocol stream stays                │   • kb://doc/{name}            │
   clean.                                                 │  prompt                        │
                                                         │   • summarize_document         │
                                                         └───────────────┬────────────────┘
                                                                         │
                                            LLM layer (summarize):  mock ▸ openai/anthropic/ollama
                                            knowledge base:         data/kb/*.md

Repository layout

mcp_tools/
├── server.py        # FastMCP server: registers tools/resources/prompt, runs over stdio  ← entry point
├── tools.py         # the capabilities as plain, tested functions (no protocol here)     ← the core
├── search.py        # CorpusSearch: deterministic IDF keyword search over the KB
├── config.py        # env-driven settings; resolves paths relative to the package
├── demo_client.py   # local MCP client that drives the server over stdio                 ← "see it work"
└── llm/             # tiny provider-agnostic ChatModel layer (mock | openai | anthropic | ollama)
data/kb/*.md         # the sample "developer knowledge" knowledge base (6 docs)
tests/               # hermetic pytest suite (imports the functions directly)

Capability catalog

Tools

Tool Signature What it does
search_docs (query: str, k: int = 4) Ranked snippets from the local KB via IDF keyword scoring.
summarize (text: str, max_sentences: int = 3) Condenses text through the LLM layer (offline mock by default).
calculate (expression: str) Safe arithmetic (+ - * / // % **, parens) via AST allow-list — no eval.
convert_units (value, from_unit, to_unit) Length, mass, time, volume, data, and temperature conversions.

Resources

URI Kind Contents
kb://index static Markdown list of every knowledge-base document.
kb://doc/{name} template The full markdown of one document, by file name.

Prompt — summarize_document(name): a ready-made instruction to summarize a KB document into a few bullet points.

Note: from is a Python keyword, so convert_units uses from_unit / to_unit.

Run it

With the Makefile (creates ./.venv):

make install    # venv + runtime + dev deps
make demo       # start the server, list capabilities, call every tool  ← best first run
make test       # hermetic pytest suite
make run        # start the server on stdio (it waits for a client; Ctrl-C to stop)
make run-http   # start the server on HTTP/SSE at http://0.0.0.0:8084/sse
make demo-http  # exercise a running HTTP server (see "Run over HTTP / Docker")

Or directly, inside an activated virtualenv:

pip install -r requirements-dev.txt
python -m mcp_tools.demo_client     # the harness
python -m mcp_tools.server          # the raw server (stdio)
pytest

make demo connects over stdio, prints the server's tools/resources/prompts, calls each tool (including a deliberately rejected unsafe calculate to show the guard), and reads the resources — all offline.

Use it from Cursor or Claude Desktop

The server is launched by the host as a subprocess. Point the host at this repo's venv Python and set PYTHONPATH to the project root (so the package imports from any working directory — the knowledge base is resolved relative to the package, not the cwd). Replace /ABSOLUTE/PATH/TO/mcp-tools-server with your checkout path.

Cursor — add to ~/.cursor/mcp.json (global) or .cursor/mcp.json (per-project):

{
  "mcpServers": {
    "dev-knowledge-tools": {
      "command": "/ABSOLUTE/PATH/TO/mcp-tools-server/.venv/bin/python",
      "args": ["-m", "mcp_tools.server"],
      "env": { "PYTHONPATH": "/ABSOLUTE/PATH/TO/mcp-tools-server" }
    }
  }
}

Claude Desktop — add to claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "dev-knowledge-tools": {
      "command": "/ABSOLUTE/PATH/TO/mcp-tools-server/.venv/bin/python",
      "args": ["-m", "mcp_tools.server"],
      "env": { "PYTHONPATH": "/ABSOLUTE/PATH/TO/mcp-tools-server" }
    }
  }
}

Prefer uv? --directory sets the working directory, so no PYTHONPATH is needed:

{
  "mcpServers": {
    "dev-knowledge-tools": {
      "command": "uv",
      "args": ["--directory", "/ABSOLUTE/PATH/TO/mcp-tools-server", "run", "python", "-m", "mcp_tools.server"]
    }
  }
}

Restart the host, and the four tools plus the kb:// resources appear.

Run over HTTP / Docker

The same server can serve over HTTP + Server-Sent Events (SSE) instead of stdio, so you can run it in a container and reach it "like an API". The transport is chosen by MCP_TRANSPORT (default stdio, so desktop hosts are unaffected).

With Docker (zero setup):

docker compose up          # builds the image, serves at http://localhost:8084/sse
# in another terminal — watch it work over HTTP, no Cursor/Claude needed:
make demo-http             # or: python -m mcp_tools.demo_client --http
docker compose down        # stop and remove

Without Docker:

make run-http              # MCP_TRANSPORT=sse MCP_HOST=0.0.0.0 MCP_PORT=8084 python -m mcp_tools.server
make demo-http             # in another terminal

Register the HTTP server with an MCP client. Point the client at the SSE URL (no command/args — the server is already running):

{
  "mcpServers": {
    "dev-knowledge-tools-http": {
      "url": "http://localhost:8084/sse"
    }
  }
}

It speaks JSON-RPC, not plain REST. An MCP HTTP endpoint is not a REST API and has no browsable webpage. A bare curl to the SSE URL opens an event stream and hands you the JSON-RPC message endpoint — it does not return HTML:

$ curl -N http://localhost:8084/sse
event: endpoint
data: /messages/?session_id=…

The full exchange (initialize → list tools → call tool, each a JSON-RPC method) is what an MCP client does for you; make demo-http is exactly that client. Use stdio for desktop hosts and HTTP when the server runs elsewhere or behind a port.

A real summarizer (optional)

summarize runs fully offline by default via a deterministic extractive mock (it returns the leading sentences, never inventing facts). Point it at a real model with environment variables — no code changes — via the provider-agnostic ChatModel layer:

export LLM_PROVIDER=ollama   OLLAMA_MODEL=llama3.1          # local & free
export LLM_PROVIDER=openai   OPENAI_API_KEY=sk-...          # hosted
export LLM_PROVIDER=anthropic ANTHROPIC_API_KEY=sk-ant-...  # hosted

All settings live in .env.example and load automatically from a .env. Only the chosen provider's key is ever required.

Testing

pip install -r requirements-dev.txt
pytest

The suite is hermetic and offline: it imports the tool functions and asserts behaviour directly — search returns the relevant hit, the calculator is correct and rejects unsafe input, unit conversions are exact, summarize works against the mock, and the KB resources list/read correctly with path-traversal blocked. One test lists the server's registered tools/resources/prompt in-process. No network, no subprocess, deterministic.

Design decisions & tradeoffs

  • Tools are plain functions; the server is a thin adapter. Every capability lives in tools.py as an ordinary typed function with a docstring (which doubles as the LLM-facing description). server.py just registers them with the SDK. That keeps the logic unit-testable with no protocol in the loop and makes the tool surface obvious.
  • Pinned to the stable FastMCP API (mcp>=1.12,<2). The SDK's 2.0 release replaced mcp.server.fastmcp.FastMCP with a new MCPServer/Client API. This project deliberately targets the widely-adopted 1.x FastMCP interface that every current Cursor/Claude Desktop tutorial uses — maximum compatibility and familiarity — and documents the boundary so migrating to 2.0 is a conscious choice, not a surprise. (This is exactly the case SemVer's <2 constraint is for.)
  • stdio by default, HTTP/SSE on demand. For a local server, stdio needs no ports, sockets, or auth — the OS process boundary is the trust boundary — which is why desktop hosts use it, and why it stays the default. Because tool logic is transport-agnostic, HTTP/SSE (for Docker or "reach it like an API") is a pure MCP_TRANSPORT switch that changes only the one mcp.run(transport=...) call — the tools, tests, and knowledge base are untouched.
  • Safe calculator without eval. calculate parses to an AST and walks a strict allow-list of numeric operations, so __import__('os').system(...) is rejected as an unsupported node rather than executed. A power-exponent cap guards against resource-exhaustion (2 ** 10_000_000).
  • Deterministic IDF search, not embeddings. Keyword/IDF scoring is dependency-free, instant, and reproducible — ideal for a tool an LLM calls and for hermetic tests. The search(query, k) -> results surface is the seam: swap in embeddings + a vector DB (or a hosted web search) without touching the MCP tool.
  • Offline mock LLM behind a provider-agnostic seam. summarize is genuinely useful with zero keys and stays deterministic in tests, while a real provider is a pure LLM_PROVIDER switch. The adapters speak each vendor's HTTP API via httpx — no heavy vendor SDKs.
  • Package-relative paths. A host launches the server from an arbitrary working directory, so config.py resolves the knowledge base relative to the package (__file__), not the cwd. The server runs the same everywhere.
  • No agent framework. MCP is the integration layer, not an agent. There is no LangGraph or planner here by design; orchestration is the client's job.

Not included (by design)

Authentication and multi-tenant concerns (the HTTP mode binds an unauthenticated listener meant for localhost/Docker, not the public internet), TLS termination, streaming tool output, embeddings/vector search, and write tools (everything here is read-only and deterministic). Each is a deliberate, well-scoped extension point rather than scope creep — this repo is a focused, legible demonstration of a clean MCP server.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured