mcp-toolserver
mcp-toolserver
MCP-сервер, предоставляющий четыре настоящих инструмента (поиск по документам, SQL, арифметика, интроспекция корпуса), а также агентный клиент, который подключается к нему, обнаруживает эти инструменты на лету и выстраивает их в цепочку вместе с Claude, чтобы отвечать на вопросы, на которые ни один инструмент по отдельности ответить не может.
Что это демонстрирует
Модель протокола контекста (Model Context Protocol) — открытый протокол (Anthropic, ноябрь 2024), стандартизирующий то, как ИИ-приложение подключается к внешним инструментам и данным. Без него каждому ИИ-приложению нужна собственная интеграция с каждым инструментом, а каждому инструменту — собственная интеграция с каждым ИИ-приложением: проблема N×M. MCP превращает это в N+M: разработчик инструмента создаёт один MCP-сервер, и любой MCP-совместимый клиент может им пользоваться без единого интеграционного кода. Этот репозиторий — маленький конкретный пример такой схемы: сервер и клиент здесь не знают о внутренностях друг друга, их связывает только протокол.
Динамическое обнаружение инструментов — агентный клиент никогда не зашивает список инструментов. При подключении он вызывает
list_tools()и преобразовывает то, что сервер в данный момент предоставляет, в формат tool-use Anthropic. Добавьте инструмент на сервере или уберите его — клиент подхватит это автоматически, без изменения кода на стороне клиента.Многошаговое связывание инструментов — на один вопрос могут потребоваться два разных инструмента последовательно (сначала найти число, затем вычислить что-то с его помощью), и агентный цикл справляется с этим сам: Claude сам решает вызвать второй инструмент, используя результат первого, без отдельного указания.
Related MCP server: Sentinel Core Agent
Четыре инструмента
Инструмент | Сигнатура | Что делает |
|
| Семантический поиск по индексированному PDF-корпусу docmind (плотные эмбеддинги + Chroma). Возвращает |
|
| Read-only SQL по небольшой демонстрационной базе компании ( |
|
| Вычисление арифметических выражений ( |
|
| Инвентаризация индексированного корпуса: |
Докстринг каждого инструмента и есть его MCP-описание — именно это LLM реально читает, решая, когда вызвать инструмент, поэтому они написаны для этой аудитории, а не для человека, просматривающего исходный код.
Живые демозапуски
Все три примера ниже — реальные запуски против реального Claude API и реального порождённого подпроцесса MCP-сервера, а не сфабрикованные трассы. Начинаю с того, где на самом деле связываются два инструмента, — это как раз интересный случай.
1. Многошаговый: query_database → calculate
«Какова средняя зарплата в отделе Engineering, и сколько будет стоить повышение на 12%?»
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 сам написал SQL, прочитал результат, а затем сам написал и выполнил арифметическое выражение — прведённые выше входные данные инструментов принадлежат Claude, они не захардкожены. (Проверено вручную: 5 сотрудников Engineering в тестовых данных дают в сумме $708,000, ÷5 = $141,600 в среднем, ×0.12 = $84,960.)
2. Один инструмент: query_database
«Сколько сотрудников в отделе 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. Один инструмент: search_documents
«Что такое 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]Ответ опирается на реально извлечённый текст (это файл langgraph_agents.pdf из docmind), а не на общие знания о CRAG, которые у Claude тоже есть, но которые он здесь использовать не был попрошен.
Безопасность
query_database — многоуровневая, а не узловая:
Проверка ключевых слов и формы на уровне приложения — отвергает всё, что не является одним единственным оператором
SELECT(илиWITH ... SELECT), ещё до того, как запрос вообще достигнет SQLite. БлокируетINSERT,UPDATE,DELETE,DROP,ALTER,CREATE,ATTACH,DETACH,PRAGMA,VACUUM,REINDEX, и сразу же отметачает несколько операторов (через;) в принципе.Встроенный режим read-only SQLite — само соединение открывается с
?mode=noв URI. Это предусмотрено движком SQLite, а не кодом приложения, поэтому это по-настоящему нижняя граница, если в п. 1 есть дырка шир: даже запрос, каким-то образом прошедший проверку ключевых слов, физически не может делать запись.Ограничение строк — каждый запрос оборачивается в
SELECT * FROM (<query>) LIMIT 500, поэтому ни один запрос не может вернуть более 500 строк, что бы он ни запрашивал.Таймаут по реальному времени — обработчик прогресса
sqlite3проверяет истёкшее время и прерывает выполнение оператора, если тот выполняется слишком долго.
calculate — AST-allowlist, а не eval(): выражение разбирается через ast.parse(..., mode="eval") и обходится вручную; разрешены только узлы Constant (числовой литерал), BinOp (+ - * / ** %) и UnaryOp (+/-). Всё остальное — обращение по имени Name, вызов Call, доступ к атрибуту Attribute — не имеет подходящей ветки в обходчике и по построению выбрасывает ValueError. Именно поэтому calculate("__import__('os').system('...')") падает: дело не в том, что он сверяется с чёрным списком опасных вызовов, а в том, что просто не существует пути кода, который вообще мог бы выполнить узел Call.
Проектные решения
Явный агентный цикл, а не бета-версия Tool Runner из Anthropic SDK. В SDK vсно есть MCP-мост (
anthropic.lib.tools.mcp), который подключает MCP-инструменты напрямую к Tool Runner. Здесь он не использовался, потому что нужен был конкретный, проверяемый контракт возврата —{answer, tool_calls: [{tool, input, output}], iterations}— а он требует ручной бухгалтерии на каждом ходе. Tool Runner скрыл бы как раз ту механику (управление циклом, трассировку каждого вызова), которую этот проект должен показать.Транспорт stdio, а не streamable-motus. Клиент запускает сервер как собственный подпроцесс по требованию; оба находятся в одной границе доверия и не имеют сетевого перехода, поэтому простота stdio (ни портов, ни механизмов аутентификации не нужно) подходит. streamable-http поддерживается (
--transport streamable-http/ переменная MCP_TRANSPORT) для случая, когда сервер и клиент — действительно отдельные процессы/машины, хотя под этот сценарий ничего здесь не укреплялось (см. «Известные ограничения»).Потолочное ограничение в 8 итераций. Ограничивает стоимость задержек в худшем случае для цикла, вышедшего из-под контроля, — та же логика, что и лимит переписывания в docmind. Все три демозапуска выше завершились за 2–3 итерации; 8 — это щедрый потолок, предназначенный для ловли по-настоящему некорректного запроса или нестабильного поведения модели, а не для обычного режима.
Связь с docmind
search_documents и list_documents читают напрямую сохранённую Chroma-коллекцию самого docmind (DOCMIND_CHROMA_PATH, по умолчанию указывав на data/chroma соседнего проекта docmind), задавая эмбеддинги запросов с той же моделью all-MiniLM-L6-v2, которую docmind использовал при загрузке в корпус. В коде в этом подключении нет ничего специфичного для docmind — это просто коллекция Chroma по настроенному пути, поэтому этот проект является настоящим вторым потребителем этого корпуса, а не его копией. Это небольшое доказательство того, что слой поиска в docmind не вшит именно в FastAPI-бэкенд docmind: он доступен любому MCP-сознательному клиенту, который знает, где расположена коллекция.
Известные ограничения
Демонстрационная SQL-база крошечная и синтетическая (12 сотрудников, 4 отдела) — ничего не тестировалось против реально масштабной или враждебной базы.
Блоклист ключевых слов SQL — это регулярное выражение по тесту запроса, а не настоящий SQL-парсер, поэтому обон может и переблокировать легальную ссылку (например, на табличную функцию
pragma_table_info()), и, in principle, post hyphenate конструкцию, которую никто не подумал тестировать. Режим чтения — это защита, которая не зависит от того, что блоклист полон.calculateподдерживает только числовые литералы и шесть перечисленных операторов — без функций (sqrt,sin, ...), без переменных. Оно намеренно минимально, а не universal expression engine.У MCP-сервера нет аутентификации. Для stdio это нормально (процесс локальный, единая граница доверия); если запускать поэ как через
stream filters/http, в текущем виде любой, кто достигнет порта, может вызвать любой инструмент, включаяquery_database.Лимит в 8 итераций: жёсткий природeking, а notэ elegant̆ное ухудшение — вопрос, которому законно нужно больше ~4 раундов вызовов инструмента, получит сообщение «остановлен после 8 итераций», а не настоящий ответ.
Нет памяти разговора между вызовами CLI — каждый вызов
python -m toolserver.client.agent "..."начинает диалог с чистого листа, без истории.Нетками потоковой передачи — каждый виток цикла блокирующе вызывает
messages.create; медленный инструмент или длинная генерация блокирует весь ход.Тесты полностью мокают Anthropic-клиент и MCP
Client(намеренно: корпусе тестов нет настоящих API-вызовов). Это значит, что, если в каком-то SDK изменяются схематы, одинpytestэтого не поймает в клинике: живая демонстрация выше — единственная проверка на реальном умпе, а она ручная, а не часть CI.
Установка и запуск
Требуются Python 3.12, переменная ANTHROPIC_API_KEY и (для search_documents/list_documents) наличие checkout от docmind с уже проиндексированным корпусом.
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_KEYПо умолчанию DOCMIND_CHROMA_PATH в .env.example указывает на data/chroma в соседнем docmind. Направьте его туда, где реально живёт корпус docmind — или просто он образомignore me: song, and query_database, calculate, а также путь к ошибкам list_documents полностью работают и без чекаута docmind.
Запустите агента напрямую (он сам поднимает MCP-сервер как подпроцесс — отдельный процесс сервера запускать не нужно):
python -m toolserver.client.agent "How many employees are in the Engineering department?"Или запустите MCP-сервер автономно, например, чтобы направить на него другой MCP-клиент:
python -m toolserver.server # stdio (default)
python -m toolserver.server --transport streamable-http # http://127.0.0.1:8765/mcp by defaultТесты:
pytest
ruff check .Проверено на установленном SDK (не по памяти)
mcp==2.0.0 — это достаточно сильно отклонение от старого API mcp.server.fastmcp.FastMCP — такого модуля в этой версии не существует. Всё ниже подтверждено чтением исходников установленного пакета и живыми smokри пакета (внутрипроцессный и реальный stdio-субпроцесс), а не из учебного материала:
Сервер:
from mcp.server.mcpserver import MCPServer— следующийMCPServer("name"), инструменты регистрируются через@server.tool()(скобки обязательны:@server.toolбез них намеренно почему по ошибке падает).server.run(transport="stdio" | "sse" | "streamable-http").Клиент:
from mcp.client import Client— новый единый клиент, заменяющий прямое использованиеClientSessionв большинстве случаев. Принимает внутрипроцессныйServer/MCPServer, строку URL илиTransport(например,stdio_client(StdioServerParameters(...))).Обнаружение:
await client.list_tools()→ListToolsResult, каждый инструмент содержитname,description,input_schema— то же имя поля, которое Anthropic tool-знания формат expects, поэтому конверсия на стороне клиента является почти прямым отображением, а не транслятот схем.Результаты инструментов:
CallToolResultнесёт и.content(список контент-блоков MCP, заполняется всегда), и.structured_content(тированные{"result": ...}словарь, заполнятся, когда функция инструмента имеет аннотацию типа возврата, — это верно для всех четырех инструментов здесь).
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