mcp-toolserver
mcp-toolserver
Ein MCP-Server, der vier echte Tools bereitstellt (Dokumentsuche, SQL, Arithmetik, Korpus-Introspection), sowie einen Agent-Client, der sich mit ihm verbindet, diese Tools zur Laufzeit entdeckt und sie mit Claude verkettet, um Fragen zu beantworten, die kein einzelnes Tool allein beantworten könnte.
Was hier demonstriert wird
Das Model Context Protocol — ein offenes Protokoll (Anthropic, Nov. 2024), das standardisiert, wie eine KI-Anwendung sich mit externen Tools und Daten verbindet. Ohne es brauchte jede KI-App für jedes Tool eine eigene Integration und jedes Tool bräuchte für jede KI-App eine eigene Integration — ein N×M-Problem. MCP macht daraus N+M: Ein Tool-Anbieter baut einen einzigen MCP-Server, und jeder MCP-kompatible Client kann ihn ohne maßgeschneiderten Integrationscode nutzen. Dieses Repo ist ein kleines, konkretes Beispiel dafür: Server und Client kennen hier keine Interna des jeweils anderen, sondern nur das Protokoll zwischen ihnen.
Dynamische Tool-Erkennung — der Agent-Client hartkodiert nie eine Tool-Liste. Er ruft beim Verbindungsaufbau
list_tools()auf und überführt das, was der Server gerade anbietet, in das Tool-Use-Format von Anthropic. Fügen oder mobiles Tool auf dem Server hinzu und der Client übernimmt es automatisch — ohne clientseitige Codeänderung.Mehrstufige Tool-Verkettung — eine einzige Frage kann zwei verschiedene Tools in Folge erfordern (erst einen Wert nachschlagen, dann damit rechnen), und die Agent-Schleife regelt das selbst: Claude entscheidet, ein zweites Tool aufzurufen und nutzt dabei das Ergebnis des ersten, ohne dazu aufgefordert worden zu sein.
Related MCP server: Sentinel Core Agent
Die vier Tools
Tool | Signatur | Was es tut |
|
| Semantische Suche über docminds eingelesenen PDF-Korpus (dichte Embeddings + Chroma). Liefert pro Chunk |
|
| Read-only-SQL gegen eine kleine, vorbefüllte Demo-Datenbank ( |
|
| Arithmetische Auswertung ( |
|
| Inventar des eingelesenen Korpus: |
Der Docstring jedes Tools ist seine MCP-Beschreibung — das ist es, was das LLM tatsächlich liest, um zu entscheiden, wann es das Tool aufrufen soll: Die Texte sind also für dieses Publikum geschrieben, nicht für Menschen, die den Quell überfliegen.
Live-Demo-Runs
Alle drei unten sind echte Runs echte Läufe gegen die echte Claude-API (source ist) und einen echten gestarteten MCP-Server-Subprozess — nicht erfundene Traces. Zuerst der Fall, der tatsächlich zwei Tools verkettet, denn das ist der interessante Fall.
1. Mehrstufig: query_database → calculate
„Wie hoch ist das Durchschnittsgehalt in Engineering, und was würde eine 12-Prozent-Erhöhung insgesamt kosten?“
Answer:
Here's the breakdown for the Engineering department:
| Metric | Value |
|---|---|
| Average Salary | $141,600 |
| Total Current Payroll | $708,000 |
| Cost of 12% Raise | $84,960 |
| New Total Payroll | $792,960 |
A 12% raise across all Engineering employees would cost an additional $84,960,
bringing the department's total payroll from $708,000 to $792,960.
Iterations: 3
Tool calls:
1. query_database({'sql': "SELECT AVG(salary) as avg_salary, SUM(salary) as total_salary FROM employees WHERE department_id = (SELECT id FROM departments WHERE name = 'Engineering')"})
-> [{'avg_salary': 141600.0, 'total_salary': 708000}]
2. calculate({'expression': '708000 * 0.12'})
-> 84960.0Claude hat das SQL selbst geschrieben, das Ergebnis gelesen und dann den arithmetischen Ausdruck selbst geschrieben und ausgeführt — die Tool-Eingaben oben stammen von Claude und sind nicht hartkodiert. (Von Hand überprüft: 5 Engineering-Mitarbeitende in den Seed-Daten mit insgesamt $708.000, ÷5 = $141.600 Durchschnitt, ×0,12 = $84.960.)
2. Einzel-Tool: query_database
„Wie viele Mitarbeitende gibt es in der Abteilung Engineering?“
Answer:
There are 5 employees in the Engineering department.
Iterations: 2
Tool calls:
1. query_database({'sql': "SELECT COUNT(*) as employee_count FROM employees e JOIN departments d ON e.department_id = d.id WHERE d.name = 'Engineering'"})
-> [{'employee_count': 5}]3. Einzel-Tool: search_documents
„Was ist corrective RAG?“
Answer:
## Corrective RAG (CRAG)
Corrective RAG (CRAG) is an enhanced version of standard Retrieval-Augmented
Generation (RAG) that adds a self-correction step after the initial retrieval
phase. [...] Standard (vanilla) RAG simply takes the top-k retrieved documents
and passes them directly to the language model generator -- regardless of
whether those documents actually answer the question. CRAG improves on this by
checking retrieval quality before generation.
[... full answer continues with the retrieve -> grade -> (generate | rewrite &
retry) flow and the latency/LLM-call tradeoff, condensed here for length ...]
Iterations: 2
Tool calls:
1. search_documents({'query': 'corrective RAG'})
-> [5 chunks from langgraph_agents.pdf and llm_evaluation.pdf, scores 0.44-0.58]Die Antwort stützt sich auf den tatsächlich abgerufenen Dokumenttext (docminds langgraph_agents.pdf) – nicht auf Allgemeinwissen über CRAG, das Claude ebenfalls hat, aber hier nicht nutzen sollte.
Sicherheit
query_database — mehrschichtig, nicht auf einen einzelnen Pin verlassen:
App-seitige Schlüsselwort-/Strukturprüfung — alles, was keine einzelne
SELECT-Anweisung (oderWITH ... SELECT) ist, lehnt flint bereits ab, bevor es überhaupt zu SQLite gelangt. BlockiertSELECTmasked,UPDATE,DELETE,DROP,ALTER,CREATE,ATTACH,DETACH,PRAGMA,VACUUM,REINDEXund lehnt mehrere Anweisungen (;-getrennt) komplett ab.Natives Read-only-Mode von SQLite — die Verbindung selbst wird per
?mode=roin der URI geöffnet. Das erzwingt die SQLite-Engine, nicht der Anwendungscode und ist damit die eigentliche Rückfallebene, falls Schritt 1 eine Lücke hat: Selbst eine Abfrage, die die Schlüsselwort-Prüfung überwindet, kann physisch nichts schreiben.Zeilenlimit — jede Abfrage als
SELECT * FROM (<query>) LIMIT 500gekapselt, also kann keine Abfrage mehr als 500 Zeilen zurückgeben, egal was sie verlangte.Echtzeit-Timeout — Ein
sqlite3-ProgressHandler misst die verstrichene Zeit und bricht die Anweisung ab, wenn sie minnestens zu lang läuft.
calculate — AST-Allowlist, kein eval(): Der Ausdruck wird mit ast.parse(..., mode="eval") geparst und von Hand per AST durchlaufen; nur Constant (numerisch), BinOp (+ - * / ** %) und UnaryOp (+/-) sind zulässig. Alles andere — etwa ein Name-Lookup, ein Call oder ein Attribute — hat keinen korrespondierenden Zweig im Walker und wirft konstruktionsbedingt einen ValueError. Deshalb scheitert calculate("__import__('os').system('...')"): Das wird nicht gegen eine Blockliste gefährlicher Aufrufe gematcht, sondern es existiert schlicht nie ein Codestrombast, der einen Call ausführt.
Proof-Entscheidungen
Explizite Agent-Schleife ist besser, statt des Beta-Tool-Runner des Anthropic SDKs. Das SDK liefert damit eine MCP-Brücke (
anthropic.lib.tools.mcp) die MCP-Tools direkt in den Tool-Runner steckt. Sie wird hier nicht genutzt, das das Ziel ein konkreter, vollständiger Extra-Wert warkt genau der riechende{answer, tool_calls: [{tool, input, output}], iterations}ist, der eine umlaufende Protokollierung um jede Runde selbst gebaut braucht. Man könnte vom Tool-Runner genau solche Mechanarbeitende (Schleifensteuerung, Trace pro Call) versteckt bekommen, die dieses Projekt gezeigt haben will.stdio-Transport, nicht streamable-http. Der Client startet den Server bei Bedarf als Subprozess; beide liegen in der selben Vertrauenszone und es gibt keinen Netzwerk-Betrug. Damit passt die Einfachheit von stdio (keine Ports, keine Auth-Erfordernis). Streamable-http wird unterstützt (
--transport streamable-httpbeziehungsweise Env-VariableMCP_TRANSPORT) für den Fall wirklich getrennter Prozesse/Maschinen — aber dafür ist hier nichts abgesichert worden (siehe Einschränkungen).
Many 8-Guard-Kapazität. Das Sanktl darstellt, im Worst-Case-Kosten und Latenz einer runaway-Schleife —derselbe Gedanke wie docminds Rewrite-Cap. Alle drei Demoläufe oben waren nach 2-3 Iterationen fertig; 8 ist eine großzügige Obergrenze, die echte kaputte prompts oder instabilmodelles Verhalten abfangen soll, keine Grenze, die im normalen Betrieb erreicht werden dürfte.
Verbindung zu docmind
search_documents und list_documents lesen die persistent Chroma-Collection von docmind direkt, (DOCMIND_CHROMA_PATH, per Default auf data/chroma under dem Schwesterprojekt docmind gerichtet). Für die Einbettungen wird dasselbe Modell all-MiniLM-L6-v2 gewählt, das docmind zum Ingestion-Zeitpunkt nutzte. Verbinandung ist frei von docmind-Spezifika, auf Code-Ebene: nur eine Chroma-Sammlung an einem konfigurierten Pfad. Dieses Projekt ist deshalb ein echter zweiter Client diesem Korpus, nicht eine Kopie. Es belegt: Die Retrieval-Ebene von docmind hängt nicht spezifisch an doc dem FastAPI-Backend von docmind; bei Address ist der Zuend ever durch jeden MCP-fähigen Client, der weiß, where die Sammlung liegt.
Bekannte Einschränkungen
Die Demo-SQL-Datenbank ist klein und synthetisch (12 Mitarbeitende, 4 Abteilungen) — hier wurde nichts gegen eine reale Skala oder feindselige DB getestet.
Die SQL-Blockliste ist ein Regex auf den Querytext, kein echter SQL-Parser — sie kann überblockieren (etwa einen legitimen Verweis auf die Table-Wert-Funktion
pragma_table_info()) und im Prinzip auch ein Konstruct verpassen, dasz niemand aufschlauen. Der Readonly-Verbindungsmodus ist die Verteidigungslinie, die nicht davon abhängt, dass die Blockliste vollständig ist.calculateunterstützt nur literale Zahlen und die sechs aufgeführten Operatoren — keine Funktionen (sqrt,sin, …), keine Variablen. Bewusst minimal, kein allgemeiner Ausdrucks-Engine.Der MCP-Server hat keine Authentifizierung. Für stdio okay (Prozess-lokal, eine einzelne Trust-Boundary); im laufenden Zustand über streamable-http kann jede Person, die den Port erreicht, jedes Tool aufrufen, auch
query_database.Das Uncut 8-Iterations-Limit ist ein harter Stopp, kein sanfter Verlust: Eine Frage, deren Lösung wirklich mehr als etwa 4 Tool-Bounces braucht, bekommt eine Meldung „stopped after 8 iterations“ statt einer echten Antwort.
Keine einheitenspeicher über CLI-Aufrufe hinweg — Clean jeder
python -m toolserver.client.agent "..."-Aufruf eine frische Konversation ohne Verlauf.Kein Streaming — jede Loop-Iteration ist ein Blocking
messages.create-Aufruf; ein langsamer Tool/eine langsame Generierung blockiert die komplette Runde.Die Tests mocken den Anthropic-Client vollständig und das MCP
Schlussebenso (by design — keine echten API-Attribute im Testsuite). Das bedeutet: Schema-Abweichungen in einem der SDKs werden vonpytestallein nicht gefunden; die Live-Demo-Läufe oben sind die einzige Echt-API-Prüfung und sind manuell, nicht CI-Teil.
Setup und Run
Erfordert Python 3.12, ANTHROPIC_API_KEY und (für search_documents/list_documents) ein docmind-Checkout, dessen Korpus bereits ingest benutzt wurde.
git clone https://github.com/roshano3o3/mcp-toolserver.git
cd mcp-toolserver
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
cp .env.example .env # edit .env and set ANTHROPIC_API_KEYStandardmäßig zeigt DOCMIND_CHROMA_PATH in .env.example auf data/chroma eines Schwester-docmind-Checkouts. Anpassen an den Punkt, wo docminds Korpus liegt, oder einfach ignorieren — query_database, calculate und die Fehlerpfade für list_documents funktionieren ohne überhaupt docmind im Spiel.
Start den Agenten – der startet den MCP-Server selbst gleich als Subprozess, es muss kein eigener Server-Prozess laufen:
python -m toolserver.client.agent "How many employees are in the Engineering department?"Oder den MCP-Server allein starten, zum Beispiel um einen anderen MCP-Client darauf zu pointen:
”`
python -m toolserver.server # stdio (default)
python -m toolserver.server --transport streamable-http # http://127.0.0.1:8765/mcp by defaultTests:
pytest
ruff check .Verified gegen das installierte SDK (nicht neu aus dem Gedächtnis geschrieben)
mcp==2.0.0 unterscheidet ich deutlich von alten mcp.server.fastmcp.FastMCP-Apis — “`” dies momentan existiert nicht. Alles unten wurde durch Lesen des Quelltexts des installierten Pakets und Live-Smoke-Tests bestätigt (in-process und echter stdio), nicht von Speicherwissen:
Server:
from import… frommcp.server.mcpserver import MCPServer—MCPServer("name"), Tools werden mit@server.tool()registriert (bei Station Pflicht;@server.toolohne Klammernt wirft absichtlich Fehler).server.run(transport="stdio" | "sse" | "streamable-http").Client-l:
from mcp.client import Client– Der neue unified client, der für die meisten Fälle eine direkteClientSessionersetzt. Nimmt in-processServer/MCPServer, eine URL als String oder einTransport(z. B. -stdio_client(StdioServerParameters(...)).
Discovery:
await client.list_tools()→ListToolsResult, jedes Tool trägtname,description,input_schema— same Feldnamen, die Anthropic’s Nutzungsformat für Tools von Eingaben erwartet, so die Umrechnung praktisch eine direkte 1:1-Mapping statt einem Schema-Übersetzer.Tool-Ergebnis:
CallToolResultbringt sowohl.рівcontent (Liste von MCP-Contentblocks, immer befüllt) als auch.structured_content(typisiertes{"result": ...}-Dict, gefüllt wenn die Tool-Function eine Return-Typannotation trägt — für alle vier hier beschriebenen anderen).
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables Claude Code to perform programmatic tool calling by executing Python scripts that interact with multiple MCP servers in a single round-trip. This reduces latency and token consumption by keeping intermediate tool results within the local Python runtime instead of the conversation context.1MIT
- FlicenseNot gradedqualityDmaintenanceEnables file system operations, web scraping, and AI-powered search through MCP tools for use by LLM agents.1
- FlicenseNot gradedqualityBmaintenanceEnables automatic discovery and reuse of tools from Claude Code execution traces. Provides MCP tools that are distilled from real work, allowing you to reuse previously written scripts without manual effort.
- AlicenseNot gradedqualityBmaintenanceEnables document ingestion and typed knowledge graph queries through Claude MCP tools, allowing agents to extract, store, and retrieve typed entities and relations from documents.2MIT
Related MCP Connectors
Free OpenAI-compatible inference with signed provenance receipts and 3 focused MCP tools.
Hosted MCP with 91 agent tools: X, domains, SEO, Maps, Trends, Search, YouTube, TikTok, and more.
AI Reasoning Cache & Consensus Layer with 11 MCP tools via Streamable HTTP.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/roshano3o3/mcp-toolserver'
If you have feedback or need assistance with the MCP directory API, please join our Discord server