mcp-project-helper
mcp-project-helper
Минимальный MCP (Model Context Protocol) сервер, который даёт AI-ассистенту для программирования — в частности, Claude Code — небольшой, безопасный набор инструментов (tools) для работы с одной конкретной директорией проекта: поиск по её файлам, чтение файла, поиск по локальной документации и запуск предварительно одобренной проверки (тестов).
Проект реализован поэтапно (Stage 0 → Stage 3, история промптов — в PROMPTS.md); текущая стадия — Stage 3: финализация. Все четыре tool реализованы и покрыты тестами (Stage 1), сервер подключён к Claude Code и проверен вручную реальными запросами через Claude Code CLI (Stage 2–3, evidence — в evidence/).
О проекте
Вместо того чтобы давать ассистенту прямой доступ к shell или неограниченный доступ к файловой системе, проект предоставляет узкую, легко аудируемую поверхность из четырёх tools:
search_project_files— поиск текста, ограниченный корнем проекта.read_project_file— чтение одного файла, ограниченное корнем проекта.get_docs— поиск по локальной документации вdocs/этого репозитория.run_project_check— запуск проверки из белого списка (сейчас этоtests), но никогда произвольной shell-команды.
Это учебный проект (домашнее задание) — цель которого не покрыть все возможные случаи использования, а показать сквозной, honestly-документированный пример MCP-сервера: от каркаса и примитивов безопасности (Stage 0), через реальную реализацию tools (Stage 1), до интеграции с IDE-агентом и воспроизводимых evidence реальных вызовов (Stage 2–3).
Related MCP server: GPT Commander
Что такое MCP и как работает подключение агента
MCP (Model Context Protocol) — открытый протокол на основе JSON-RPC, описывающий, как AI-ассистент (клиент/хост, например Claude Code) обнаруживает и вызывает внешние инструменты (tools), предоставляемые отдельным процессом (MCP-сервером), без того чтобы ассистент имел прямой доступ к shell, сети или файловой системе хоста.
В этом проекте используется stdio-транспорт — самый простой и самый распространённый способ для локальных инструментов:
Хост (Claude Code) читает свою MCP-конфигурацию (
.mcp.json) и запускает сервер как обычный локальный подпроцесс, с указанной командой/аргументами и переменными окружения.Хост и сервер обмениваются JSON-RPC-сообщениями через stdin/stdout этого подпроцесса (отсюда требование, что stdout зарезервирован только под протокол — см. раздел "Логирование и отладка").
Хост вызывает
initialize()— сервер отвечает своим именем/версией (mcp-project-helper 0.1.0) и возможностями.Хост вызывает
list_tools()— сервер возвращает список зарегистрированных tools с их именем, описанием и JSON Schema входных параметров (inputSchema), сгенерированной MCP SDK из сигнатуры функции.Когда пользователь (или сама модель) решает вызвать один из tools, хост отправляет
call_tool(name, arguments); сервер выполняет соответствующую Python-функцию и возвращает структурированный результат (см. "Контракт вывода tools" ниже) либо ошибку уровня tool.Никакого сетевого порта не открывается: жизненный цикл сервера полностью привязан к подпроцессу, запущенному хостом — если хост закрывает подключение, подпроцесс завершается.
Здесь нет обращений ни к одному LLM/AI API (OpenAI, Anthropic и т. д.): этот
сервер только предоставляет tools, которые вызывает клиент (Claude Code);
"поиск" в get_docs — простое детерминированное совпадение подстроки по
секциям Markdown, без embeddings/векторной БД. Для запуска сервера не нужен
никакой API-ключ.
Что считается tool в данном сервере
Tool — это обычная функция Python, декорированная @mcp.tool(),
принимающая JSON-сериализуемые аргументы и возвращающая dict[str, Any]. MCP
SDK автоматически:
генерирует
inputSchema(JSON Schema) из сигнатуры и аннотаций типов аргументов функции — отдельно эту схему нигде не приходится описывать руками;превращает аннотацию возвращаемого значения
-> dict[str, Any]в структурированный вывод tool (outputSchema/structuredContent), см. "Контракт вывода tools" ниже;превращает необработанное исключение Python внутри tool-функции в структурированный результат с ошибкой на уровне tool (
CallToolResult.is_error = True), не роняя саму MCP-сессию.
Все четыре регистрации находятся рядом в
server.py:37-58; каждая
тонкая обёртка, обращённая к MCP (docstring которой становится описанием
tool, видимым модели), делегирует вызов настоящей реализации в
tools/*.py, отделяя сигнатуру уровня протокола от логики.
Стек
Python 3.14 (
requires-python = ">=3.10"вpyproject.toml— это реальная нижняя граница используемого MCP SDK, а не утверждение, что работает только 3.14).Официальный MCP Python SDK (пакет
mcp, установленная версия2.0.0) — предоставляет фреймворк сервера (mcp.server.MCPServer), регистрацию tools (@mcp.tool()) и stdio-транспорт (mcp.run(transport="stdio")).pytest — единственная dev-зависимость, для набора тестов.
Нет интеграции с LLM/AI API и нет сетевого транспорта (HTTP/SSE не настроен) — см. предыдущий раздел.
Архитектура
src/mcp_project_helper/
server.py точка входа: создаёт MCPServer, регистрирует tools, запускает stdio
config.py корень проекта / корень docs / настройки логирования / whitelist проверок / лимиты
security.py resolve_within_root() — единый шлюз ограничения путей
logging_setup.py логирование в stderr (+ опционально файл), не затрагивая stdout
tools/
search_project_files.py поиск текста в пределах корня проекта
read_project_file.py чтение одного файла в пределах корня проекта
get_docs.py поиск по секциям markdown в docs/
run_project_check.py запуск подпроцесса из белого спискаКаждый tool, работающий с файлами, проходит через
security.resolve_within_root(root, relative_path) прежде, чем открыть
какой-либо путь. config.py определяет корень проекта из переменной
окружения MCP_PROJECT_HELPER_ROOT (по умолчанию ./demo_project), поэтому
сервер можно направить на любой проект без изменения кода.
Реализованные MCP tools
search_project_files(query, path=".", max_results=50)
Рекурсивно ищет по текстовым файлам под path (относительно корня проекта;
по умолчанию — весь корень) точное совпадение подстроки query. Пропускает
директории из config.IGNORED_DIR_NAMES (.git, .venv, __pycache__,
node_modules, ...) и любые директории *.egg-info. Файлы проверяются на
бинарный контент (байт NUL или невалидный UTF-8 в первых 4 КБ) и молча
пропускаются, а не приводят к ошибке. Никогда не идёт по символическим
ссылкам на директории или файлы за пределами корня — каждый путь-кандидат
дополнительно проверяется через resolve_within_root в дополнение к
стандартному поведению os.walk, не следующему по символическим ссылкам на
директории.
max_results ограничен сверху значением config.SEARCH_RESULTS_CAP (200);
совпавшие строки длиннее config.SEARCH_MAX_LINE_CHARS (300) обрезаются;
файлы больше config.SEARCH_MAX_FILE_BYTES (2 МБ) пропускаются, а не
сканируются.
Реализация: tools/search_project_files.py:41-130.
read_project_file(path)
Читает один текстовый файл по пути path (относительно корня проекта).
Отклоняет директории, несуществующие файлы и бинарный контент (байт NUL или
невалидный UTF-8). Содержимое ограничено значением
config.READ_MAX_FILE_BYTES (200 КБ) — файлы большего размера возвращаются
обрезанными, а не отклоняются.
Реализация: tools/read_project_file.py:25-69.
get_docs(query=None, max_results=10)
Ищет по docs/*.md (рекурсивно), разбитым на секции по заголовкам
Markdown. При наличии query возвращает секции, чей заголовок или тело
содержат искомую подстроку (без учёта регистра), каждая с указанием
исходного файла и заголовка. Без query возвращает список из одной секции
на файл — перечень того, какая документация существует. Ограничено только
config.get_docs_root() — никогда не корнем проекта.
max_results ограничен сверху значением config.DOCS_RESULTS_CAP (50);
фрагменты (snippets) ограничены значением config.DOCS_MAX_SNIPPET_CHARS
(800 символов).
Реализация: tools/get_docs.py:58-114.
run_project_check(check_name)
Запускает проверку из белого списка. check_name ищется в
config.ALLOWED_CHECKS до того, как что-либо запускается — неизвестное имя
немедленно вызывает ошибку, подпроцесс при этом никогда не запускается.
Argv из белого списка выполняется через
subprocess.run(argv, shell=False, cwd=<корень проекта>, timeout=...): без
shell, с фиксированной рабочей директорией, и ничего от вызывающей стороны
не добавляется в командную строку.
Реализация: tools/run_project_check.py:32-90.
Whitelist
ALLOWED_CHECKS = {
"tests": [sys.executable, "-m", "pytest", "-q"],
}Определено в config.py:55-57.
sys.executable (а не просто строка "pytest") используется, чтобы проверка
всегда выполнялась с тем же интерпретатором/окружением, что и сам сервер,
независимо от того, что первым стоит в PATH. Здесь намеренно нет записи
lint: в этом репозитории нет зависимости или конфигурации ruff, поэтому
подключение проверки "lint" было бы либо фикцией, либо обманом. Добавить её
можно позже
(config.ALLOWED_CHECKS["lint"] = [sys.executable, "-m", "ruff", "check", "."]),
когда ruff станет настоящей зависимостью проекта с реальной конфигурацией —
механизм whitelist уже поддерживает это без каких-либо других изменений кода.
Таймаут (config.CHECK_TIMEOUT_SECONDS, по умолчанию 60 с) и ограничение
объёма вывода (config.CHECK_MAX_OUTPUT_CHARS, по умолчанию 20 000 символов
на поток) применяются к каждому запуску проверки.
Tool outputs contract
Каждый tool возвращает обычный Python dict из функции с аннотацией
-> dict[str, Any]; MCP SDK автоматически распознаёт это как структурированный
вывод tool (заполняет CallToolResult.structured_content и выводит
outputSchema) — здесь нигде вручную не сериализуются результаты в JSON-строку.
Ошибочные ситуации (некорректный ввод, выход за пределы пути, неизвестная
проверка, файл не найден, бинарный контент и т. д.) вызывают исключение
Python, а не возврат dict; SDK автоматически превращает это в результат с
ошибкой на уровне tool (CallToolResult.is_error = True). Единственное
исключение — таймаут проверки: это легитимный результат выполнения
успешно запущенной проверки, а не ошибка входных данных, поэтому он
возвращается как структурированный dict {"status": "error", ...}, а не
вызывает исключение.
search_project_files
{
"status": "success",
"query": "apply_discount",
"path": ".",
"matches": [
{"file": "demo_app/services.py", "line": 12, "text": "def apply_discount(order: Order, percent: float) -> float:"}
],
"count": 4,
"truncated": false
}read_project_file
{
"status": "success",
"file": "demo_app/models.py",
"content": "...",
"size": 397,
"truncated": false
}get_docs
{
"status": "success",
"query": "whitelist",
"results": [
{"file": "architecture.md", "heading": "Whitelist", "snippet": "..."}
],
"count": 1,
"truncated": false
}run_project_check
{
"status": "success",
"check_name": "tests",
"exit_code": 0,
"stdout": "...",
"stderr": "",
"truncated": false
}При таймауте: {"status": "error", "check_name": ..., "error": "check timed out after 60s", "exit_code": null, "stdout": "...", "stderr": "...", "truncated": ...}.
Названия полей и конвенции status/count/truncated выше считаются
устойчивым контрактом на будущее, а не деталью реализации.
Ограничения безопасности
Ограничение путей:
security.resolve_within_root(security.py:19-50) отклоняет абсолютные пути, обход через..(на любую глубину), байты NUL и символические ссылки, ведущие за пределы настроенного корня. Используется вread_project_fileиsearch_project_filesотносительно корня проекта, а также повторно вsearch_project_filesдля каждого файла-кандидата во время обхода. Покрыто unit-тестами вtests/test_security.pyи подтверждено вручную реальным негативным тестом через Claude Code (см. "Результаты проверки" ниже, тест 6).Отсутствие выхода за пределы через символические ссылки при обходе:
search_project_filesиget_docsникогда не идут по символическим ссылкам на директории (поведениеos.walkпо умолчанию) и полностью пропускают символические ссылки на файлы.Отсутствие произвольных shell-команд:
run_project_checkпроверяет запрошенное имя проверки поconfig.ALLOWED_CHECKS(config.py:55-57) до того, как что-либо будет запущено; неизвестные имена отклоняются немедленно, а сама проверка выполняется черезsubprocess.run(argv, shell=False, ...)с фиксированнымcwdи без каких-либо аргументов, добавленных вызывающей стороной.Ограниченный вывод везде: каждый tool ограничивает объём возвращаемых данных —
max_results+ жёсткие пределы для поиска и docs, ограничение по байтам для чтения файлов, ограничение по символам + таймаут для вывода проверки — так что ни один вызов не может вернуть неограниченный объём данных или работать бесконечно.stdout остаётся чистым: всё логирование идёт через
logging_setup.pyв stderr (и опционально в файл лога); ничто в сервере не пишет в stdout, который зарезервирован для JSON-RPC framing протокола MCP.Отсутствие секретов в логах: сервер вообще не принимает никаких API-ключей или учётных данных. Каждый реальный вызов tool логирует имя tool, его безопасные входные параметры (строки запроса, пути, имена проверок, количество/размер результатов — но никогда содержимое файла) и финальный
status=success/status=error.
Логирование и отладка
Каждый реальный вызов tool логирует одну строку через общий логгер
mcp_project_helper (stderr, плюс опциональный файл через
MCP_PROJECT_HELPER_LOG_FILE), например (реальные строки из
evidence/tool-calls.log):
INFO mcp_project_helper: tool=search_project_files query='apply_discount' path='.' max_results=50 matches=4 truncated=False status=success
INFO mcp_project_helper: tool=read_project_file path='demo_app/models.py' size=397 truncated=False status=success
INFO mcp_project_helper: tool=run_project_check check_name='tests' exit_code=0 status=success
INFO mcp_project_helper: tool=read_project_file path='../../../../etc/passwd' status=errorСодержимое файлов никогда не логируется — только метаданные о вызове (пути, строки запроса, размеры, количество, коды завершения). Настройка логирования — logging_setup.py:20-42.
Для отладки:
Уровень логирования регулируется
MCP_PROJECT_HELPER_LOG_LEVEL(DEBUG,INFO,WARNING,ERROR,CRITICAL; по умолчаниюINFO).Файл лога задаётся
MCP_PROJECT_HELPER_LOG_FILE; по умолчанию (без этой переменной) пишется только в stderr. При запуске из Claude Code (.mcp.json) он указывает наevidence/tool-calls.log.Никогда не используйте
print()в коде сервера — stdout зарезервирован под JSON-RPC-протокол; любой лишний вывод в stdout ломает stdio-транспорт.Чтобы посмотреть, какие вызовы реально произошли в текущей сессии Claude Code, откройте файл, на который указывает
MCP_PROJECT_HELPER_LOG_FILE(evidence/tool-calls.log), либо запустите сервер вручную (python -m mcp_project_helper.server) и смотрите stderr.
Установка
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"Единственная runtime-зависимость — пакет mcp; pytest — зависимость
только для разработки/тестов (обе зафиксированы в pyproject.toml).
Настройка окружения
Конфигурация задаётся через переменные окружения — скопируйте
.env.example в .env и при необходимости измените
значения:
Переменная | Назначение | Значение по умолчанию |
| Единственная директория, к которой имеют доступ файловые tools ( |
|
| Путь к файлу лога (см. "Логирование и отладка"). Логи всегда также идут в stderr. | не задано (только stderr) |
| Один из |
|
Секреты (API-ключи, токены) серверу не нужны — .env.example содержит
только безопасные примеры путей и уровня логирования, а .env игнорируется
git'ом (см. "Структура проекта" ниже).
Запуск MCP-сервера
Запуск сервера напрямую (он будет ожидать клиента на stdin — это нормально для MCP-серверов со stdio-транспортом; выход через Ctrl+C):
python -m mcp_project_helper.serverЗапуск набора тестов:
pytest -qЗапуск собственных тестов demo-проекта напрямую (то, что по умолчанию
запускает run_project_check("tests"), так как MCP_PROJECT_HELPER_ROOT по
умолчанию указывает на demo_project):
cd demo_project && pytest -qИнтеграция с Claude Code
Этот репозиторий содержит project-scoped файл
.mcp.json в корне репозитория — конфигурация именно для
Claude Code (в отличие от .vscode/mcp.json — отдельной
конфигурации native MCP host VS Code; см. подробное сравнение ниже).
Claude Code обнаруживает .mcp.json при открытии папки проекта, запускает
сервер как дочерний процесс и общается с ним по JSON-RPC через stdio — тот
же транспорт, что используется во всех автоматизированных тестах этого
проекта, просто запущенный самим Claude Code, а не тестовым harness'ом.
Подтверждённый end-to-end сценарий: Claude Code CLI → MCP server → custom
tools. Все 6 проверочных запросов (см. "Результаты проверки" ниже) были
реально выполнены через Claude Code CLI с этим сервером, подключённым по
.mcp.json — не только сконфигурированы, а фактически вызваны, с реальными
скриншотами и записями в server-side логе.
Конфигурация для Claude Code
{
"mcpServers": {
"mcp-project-helper": {
"command": "${CLAUDE_PROJECT_DIR:-.}/.venv/bin/python",
"args": ["-m", "mcp_project_helper.server"],
"env": {
"MCP_PROJECT_HELPER_ROOT": "${CLAUDE_PROJECT_DIR:-.}/demo_project",
"MCP_PROJECT_HELPER_LOG_FILE": "${CLAUDE_PROJECT_DIR:-.}/evidence/tool-calls.log"
}
}
}
}${CLAUDE_PROJECT_DIR} разворачивается самим Claude Code в абсолютный путь
той директории, в которую был клонирован репозиторий, поэтому файл не
содержит специфичных для конкретной машины путей и не требует правок после
git clone. Используется именно форма с fallback-значением
${CLAUDE_PROJECT_DIR:-.}, а не голое ${CLAUDE_PROJECT_DIR}: без :-.
переменная не разворачивалась, и Claude Code пытался буквально запустить
${CLAUDE_PROJECT_DIR}/.venv/bin/python как путь к исполняемому файлу (эта
ошибка была реально замечена при первой версии конфигурации Stage 2, см.
REPORT.md). MCP_PROJECT_HELPER_ROOT задан явно как
${CLAUDE_PROJECT_DIR:-.}/demo_project, чтобы корень проекта, передаваемый
серверу, был однозначным независимо от собственного значения по умолчанию
в config.py.
Замечание по платформам: .venv/bin/python — это структура venv для
Unix (macOS/Linux), используемая во всём этом проекте. В Windows
эквивалентный путь — .venv\Scripts\python.exe; чтобы поддержать эту
платформу тоже, .mcp.json потребовал бы второй, специфичной для Windows
записи (или скрипта-обёртки) — этого не сделано, так как проект
разрабатывался и проверялся только на macOS.
Конфигурация для VS Code
Этот репозиторий также содержит .vscode/mcp.json —
отдельную конфигурацию рабочей области для встроенного MCP-хоста VS
Code (используемого агентным режимом GitHub Copilot Chat):
{
"servers": {
"mcp-project-helper": {
"type": "stdio",
"command": "${workspaceFolder}/.venv/bin/python",
"args": ["-m", "mcp_project_helper.server"],
"env": {
"MCP_PROJECT_HELPER_ROOT": "${workspaceFolder}/demo_project",
"MCP_PROJECT_HELPER_LOG_FILE": "${workspaceFolder}/evidence/tool-calls.log"
}
}
}
}Тот же stdio-сервер mcp-project-helper, с MCP_PROJECT_HELPER_ROOT,
заданным как ${workspaceFolder}/demo_project, и
MCP_PROJECT_HELPER_LOG_FILE, заданным как
${workspaceFolder}/evidence/tool-calls.log.
Почему два файла, а не один: .mcp.json и .vscode/mcp.json следуют
разным, несовместимым схемам, а их переменные подстановки путей не
взаимозаменяемы между хостами:
.mcp.json(конфигурация Claude Code) использует верхнеуровневый ключmcpServersи разворачивает${CLAUDE_PROJECT_DIR:-.}в корень репозитория..vscode/mcp.json(конфигурация native MCP host VS Code) использует верхнеуровневый ключservers, явное поле"type": "stdio"и разворачивает вместо этого${workspaceFolder}в путь открытой папки. MCP-хост VS Code не понимает${CLAUDE_PROJECT_DIR}— при попытке открыть.mcp.jsonнапрямую из VS Code переменная передаётся буквально, и сервер не может запуститься (spawn ${CLAUDE_PROJECT_DIR}/.venv/bin/python ENOENT) — это реально наблюдавшаяся ошибка, из-за которой и появился отдельный.vscode/mcp.json. Хранение конфигурации каждого хоста в своём файле, со своей переменной, избегает этой ошибки и позволяет использовать оба инструмента с одним и тем же клоном без того, чтобы один конфиг шёл на компромисс с синтаксисом другого.
.vscode/mcp.json — единственное исключение из общего правила
игнорирования .vscode/* в .gitignore; остальное локальное состояние VS
Code (settings.local.json и т. п.) не отслеживается.
Статус проверки VS Code: .vscode/mcp.json синтаксически и семантически
корректен (тот же сервер, та же команда/переменные окружения, что и рабочая
конфигурация Claude Code) и был провалидирован как JSON. Дополнительно
подтверждён реальным скриншотом
evidence/vscode_mcp_server_connected.png
факт, что встроенный native MCP host VS Code реально запускает сервер по
этой конфигурации: Starting server mcp-project-helper → Connection state: Running → Discovered 4 tools, с подтверждающей строкой из собственного
stderr-лога процесса mcp_project_helper в этом же выводе. Это не то же
самое, что подтверждение вызова custom tools через интерфейс VS Code — ни
один пользовательский сценарий (search_project_files и т. д.) через этот
интерфейс не выполнялся и не заявляется как проверенный. Единственная
IDE-интеграция, подтверждённая вплоть до реальных вызовов tools пользователем
(скриншоты + server-side логи для всех 6 сценариев) — Claude Code CLI, см.
"Результаты проверки" ниже. Отдельно от обоих: интеграция через Claude Code
Desktop / расширение Claude Code внутри VS Code не проверялась в этой сессии
вообще — не путать ни с native MCP host VS Code (этот раздел), ни с Claude
Code CLI.
Как включить MCP
Кратко (подробности — в подразделах выше):
Claude Code:
Создайте venv и установите зависимости (раздел "Установка").
Откройте корень репозитория в Claude Code (
claudeиз корня репозитория).Claude Code обнаруживает
.mcp.jsonи один раз предлагает подтвердить доверие рабочей области для сервераmcp-project-helper— подтвердите.Выполните
/mcp(илиclaude mcp listв терминале) и убедитесь, чтоmcp-project-helperподключён с 4 tools.
VS Code (native MCP host, агентный режим Copilot Chat):
Создайте venv так же, как для Claude Code —
.vscode/mcp.jsonожидает тот же.venv/bin/python.Откройте корень репозитория как папку в VS Code.
VS Code обнаруживает
.vscode/mcp.jsonи предлагает запустить сервер — запустите/подтвердите доверие.Проверьте состояние через
MCP: List Servers.
Оба варианта предполагают Unix-структуру venv (.venv/bin/python); в
Windows — .venv\Scripts\python.exe (не настроено, см. выше).
Проверочные запросы
Шесть сценариев, реально выполненных через Claude Code CLI для подтверждения интеграции (полная таблица с результатами — в evidence/README.md):
Найди через MCP все места использования функции
apply_discountвdemo_project→ ожидаетсяsearch_project_files.Прочитай через MCP файл
demo_app/models.pyи кратко объясни, какие модели там определены → ожидаетсяread_project_file.Используя MCP-документацию проекта, расскажи, какие ограничения безопасности есть у MCP-сервера → ожидается
get_docs.Проверь через MCP-инструмент, проходят ли тесты
demo_project→ ожидаетсяrun_project_check.Используя только MCP-инструменты, найди в
demo_projectреализациюapply_discount, затем прочитай файл, где она определена, и объясни её параметры/возврат/расчёт скидки → ожидается цепочка из двух tools:search_project_files, затемread_project_file.(негативный / security-тест) Попробуй через MCP прочитать файл
../../../../etc/passwd→ ожидается отказread_project_fileсо структурированной ошибкой (путь выходит за пределы разрешённого корня).
Результаты проверки
Все 6 из 6 запросов выполнены успешно (в тесте 5 — оба ожидаемых tool, в правильном порядке; в тесте 6 успехом является ожидаемый отказ). Каждая строка подтверждена и реальным скриншотом, и независимой строкой в evidence/tool-calls.log. Полная таблица — evidence/README.md; подробный разбор с ссылками на код и логи — REPORT.md.
№ | Tool | Итог |
1 |
| Успех, 4 совпадения |
2 |
| Успех, |
3 |
| Успех, найден раздел «Безопасность» |
4 |
| Успех, |
5 |
| Успех, цепочка из двух tools |
6 |
| Успешный отказ (path traversal заблокирован) |
Автоматизированные проверки (не заменяют, а дополняют ручное IDE-тестирование выше):
pytest -qиз корня репозитория — 44 passed.pytest -qвнутриdemo_project/— 2 passed.Программный stdio-хендшейк (
initialize()+list_tools()) — сервер сообщаетmcp-project-helper 0.1.0и ровно 4 tools:get_docs,read_project_file,run_project_check,search_project_files.
Структура проекта
mcp-project-helper/
.mcp.json конфигурация MCP для Claude Code (project-scoped)
.vscode/mcp.json конфигурация MCP для native MCP host VS Code
.env.example безопасные примеры переменных окружения (без секретов)
pyproject.toml зависимости, entry point, конфигурация pytest
README.md этот файл
REPORT.md итоговый отчёт по всем стадиям, со ссылками файл:строки
PROMPTS.md история фактически использованных промптов (Этапы 0-3)
docs/
architecture.md документация, которую обслуживает get_docs
src/mcp_project_helper/
server.py точка входа: MCPServer, регистрация tools, stdio
config.py корень проекта/docs, лимиты, whitelist проверок
security.py resolve_within_root() — ограничение путей
logging_setup.py логирование в stderr (+ опционально файл)
tools/
search_project_files.py
read_project_file.py
get_docs.py
run_project_check.py
tests/ unit- и интеграционные тесты mcp_project_helper (44 теста)
demo_project/ демонстрационный проект — цель для файловых tools
demo_app/
models.py Product, Order
services.py apply_discount, OrderBuilder
tests/test_services.py 2 теста, запускаемые run_project_check("tests")
evidence/ реальные доказательства ручного тестирования через Claude Code и VS Code
README.md реестр всех 6 тестов с результатами + доп. evidence по VS Code
tool-calls.log реальный server-side лог всех 6 тестов (закоммичен)
tool-calls.log.example формат строки лога (шаблон)
test1_search_project_files.png … test6_path_traversal.png скриншоты 6 тестов Claude Code CLI (закоммичены)
vscode_mcp_server_connected.png доп. скриншот: native MCP host VS Code подключился, 4 tools (закоммичен)Available Tools
4 toolsget_docsC
Search local documentation from the repo's docs/ directory.
| Name | Required | Description | Default |
|---|---|---|---|
| query | No | ||
| max_results | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. 'Search' implies read-only behavior, but the description does not state read-only safety, permissions, rate limits, or other constraints, and only discloses the local docs/ scope.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler. It is appropriately brief, though it omits useful structure for parameters and usage context.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter search tool with no annotations and 0% schema descriptions, the description leaves core parameter behavior and usage context unspecified. The output schema reduces return-value burden, but invocation guidance remains largely missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description never mentions either parameter. It does not explain that query is optional or what null means, nor what max_results controls.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Search') and resource ('local documentation') with scope ('repo's docs/ directory'). It distinguishes itself by directory scope, but does not explicitly differentiate from sibling tools such as search_project_files or read_project_file.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use guidance, exclusions, or alternatives are provided. The description implies documentation search, but gives no routing advice for choosing this over search_project_files or read_project_file.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_project_fileB
Read one text file inside the allowed project root.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full behavioral burden. It does disclose two useful traits: only 'text' files are handled and paths are confined to the 'allowed project root', implying rejection of out-of-root or binary inputs. It omits error behavior, encoding, and size limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One efficient sentence with the resource and the key constraint front-loaded; nothing is wasted. It is arguably under-specified for a tool with no annotations, but that is a completeness issue rather than verbosity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple (one required param, output schema present so return values need not be explained), and the description covers the basic contract. However, with no annotations it leaves error handling, path resolution rules, and file-type restrictions implicit — the minimum viable level.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single 'path' parameter has no schema description (0% coverage), so the description must compensate. It implies the path is relative to the allowed project root and must point to a text file, but adds no syntax, format, or relative-vs-absolute guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Read) and resource (one text file) plus a scoping constraint (inside the allowed project root), so the agent knows it is a single-file read rather than a search. It does not name the sibling search_project_files or get_docs, so differentiation is implied by 'one text file' rather than explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no when-to-use or when-not-to-use guidance and no mention of the alternatives (search_project_files, get_docs). The 'allowed project root' phrasing hints at a boundary, but the agent must infer when this tool is preferable to its siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_project_checkC
Run a whitelisted project check (e.g. 'tests').
| Name | Required | Description | Default |
|---|---|---|---|
| check_name | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden, and it says nothing about side effects, permissions, runtime, or failure behavior for what is an execution-style tool. The word 'whitelisted' hints at a restricted input set but never explains enforcement or consequences.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with no filler, which is structurally efficient. The brevity is under-specification rather than waste, so it doesn't reach a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values needn't be described, but for an execution tool with no annotations the description should still cover side effects and the allowed input values. Neither is present, leaving the definition materially incomplete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and there are no enums, so the description must compensate; it does add a sample value ('tests') and implies a fixed allowed set via 'whitelisted'. But the actual list of valid check names — the single most useful piece of information — is absent.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Run) and resource (project check), which clearly distinguishes it from the read-oriented siblings (get_docs, search_project_files, read_project_file). However, 'whitelisted project check' remains somewhat abstract — the only concrete anchor is the parenthetical example 'tests'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to invoke this versus the sibling read tools, no prerequisites, and no mention of the whitelist constraint's practical implications (e.g. what happens if the name isn't whitelisted). The agent must infer usage entirely.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_project_filesC
Search for text inside files under the allowed project root.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | . | |
| query | Yes | ||
| max_results | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it discloses only that searching is confined to "the allowed project root." It says nothing about case sensitivity, regex vs literal matching, traversal depth, or result cap behavior, and does not hint that it is a non-destructive read.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single tight sentence with the scope constraint front-loaded and no filler. It is efficiently structured, though its brevity reflects under-specification elsewhere rather than optimal density.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be described, but for a 3-parameter tool with zero schema coverage and no annotations, the description omits parameter meaning and usage context. An agent would call this only by guessing how query, path, and max_results interact.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for path, query, and max_results, and it does not. Only broad implications appear ("text" for query, "project root" for path), leaving the default of "." and the meaning/limits of max_results unexplained.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ("Search for text") and resource ("files under the allowed project root"), which is enough to distinguish it from get_docs, run_project_check, and read_project_file. It is clear but never explicitly contrasts itself with the siblings, so it falls short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use guidance is given, and no alternatives are named. An agent must infer on its own that this is for locating text across many files rather than reading a single file via read_project_file.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.1.0- First observed
get_docs - First observed
read_project_file - First observed
run_project_check - First observed
search_project_files
TDQS
Scored across 4 tools
get_docs and search_project_files both search text, but get_docs is scoped to docs/ while search_project_files covers the entire project root. run_project_check and read_project_file are clearly distinct, so only minor overlap exists.
All four tools follow a consistent snake_case verb_noun pattern: get_docs, run_project_check, search_project_files, read_project_file. The convention is predictable and easy to parse.
Four tools is a reasonable, well-scoped set for a project helper, though it sits at the lower end of the typical 3–15 range. Each tool has a clear purpose, but the set feels slightly thin for broader project assistance.
The surface is read-only and lacks file listing, write/edit operations, and general command execution. Core read/search/check operations are present, but notable gaps remain for a server named 'project helper'.
Maintenance
Related MCP Connectors
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
MCP server for generating rough-draft project plans from natural-language prompts.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceAgent-safe code retrieval MCP server that indexes repositories and provides semantic search, file navigation, call graph analysis, and bounded file reading tools for coding agents.4,964,314 npm3AGPL 3.0
- FlicenseCqualityCmaintenanceA security-first MCP server that provides LLMs with structured tools for filesystem, process, search, build/test/lint, IDE integration, and more.402-
- AlicenseNot gradedqualityCmaintenanceLocal-first MCP server that provides project context, verification gates, and structured tools for coding agents to discover knowledge, run diagnostics, and execute allowlisted commands within a repository.16 npmMIT
- AlicenseNot gradedqualityAmaintenanceA lightweight local coding MCP server that exposes a single project directory to ChatGPT via Streamable HTTP, enabling file operations, command execution, search, and web fetching without authentication.30 npm25MIT