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: Nexus
Четыре инструмента
Инструмент | Сигнатура | Что делает |
|
| Семантический поиск по индексированному 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 deployed
Maintenance
Related MCP Connectors
AI Visibility and Content Intelligence tools for Claude and MCP-compatible agents.
One MCP endpoint for Claude, GPT & Gemini: 100+ tools + no-code connectors + agent workers.
Multiple MCP tools, persistent graph memory, token-saving data pointers, and more.
- WauldoOAuthcom.wauldo
Stateless agentic tools over MCP: concept extraction, long-context, knowledge graph, planning.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceEnables MCP-compatible clients to perform retrieval-grounded Q&A over FordA dataset notes and safe arithmetic calculations through callable tools.-
- AlicenseNot gradedqualityCmaintenanceEnables models to query enterprise systems—structured data, live GitHub REST API, and unstructured document embeddings—through a single, per-caller scoped MCP tool surface with task-shaped tools and recovery-aware errors.90 npmISC
- FlicenseNot gradedqualityCmaintenanceEnables an LLM to perform safe arithmetic calculations and web searches through a standardized MCP interface, returning results to generate informed responses.-
- AlicenseNot gradedqualityBmaintenanceEnables Claude Code and Codex assistants to research and read/write a team's knowledge base, manage projects and files, and use integrations through MCP tools.Apache 2.0