Skip to main content
Glama

mcp-project-helper

Servidor MCP (Model Context Protocol) mínimo que proporciona al asistente de IA para programación —en particular, Claude Code— un conjunto pequeño y seguro de herramientas (tools) para trabajar con una única directorio concreto del proyecto: búsqueda en sus archivos, lectura de archivos, búsqueda en la documentación local y ejecución de una verificación previamente aprobada (tests).

El proyecto se ha implementado por etapas (Stage 0 → Stage 3, el historial de prompts está en PROMPTS.md); la etapa actual es Stage 3: finalización. Las cuatro tools están implementadas y cubiertas por tests (Stage 1), el servidor está conectado a Claude Code y verificado manualmente con solicitudes reales a través de Claude Code CLI (Stage 2–3, evidencia en evidence/).

Sobre el proyecto

En lugar de dar al asistente acceso directo al shell o acceso ilimitado al sistema de archivos, el proyecto proporciona una superficie estrecha y fácilmente auditable de cuatro tools:

  • search_project_files — búsqueda de texto, limitada a la raíz del proyecto.

  • read_project_file — lectura de un único archivo, limitada a la raíz del proyecto.

  • get_docs — búsqueda en la documentación local en docs/ de este repositorio.

  • run_project_check — ejecución de una verificación de la lista blanca (actualmente tests), pero nunca un comando de shell arbitrario.

Es un proyecto educativo (tarea) — cuyo objetivo no es cubrir todos los posibles casos de uso, sino mostrar un ejemplo de extremo a extremo, honestamente documentado, de un servidor MCP: desde el esqueleto y los primitivos de seguridad (Stage 0), pasando por la implementación real de las tools (Stage 1), hasta la integración con el agente del IDE y evidencia reproducible de llamadas reales (Stage 2–3).

Related MCP server: GPT Commander

Qué es MCP y cómo funciona la conexión del agente

MCP (Model Context Protocol) — un protocolo abierto basado en JSON-RPC que describe cómo un asistente de IA (cliente/host, por ejemplo Claude Code) descubre e invoca herramientas externas (tools) proporcionadas por un proceso separado (servidor MCP), sin que el asistente tenga acceso directo al shell, la red o el sistema de archivos del host.

En este proyecto se utiliza el transporte stdio — la forma más simple y más común para herramientas locales:

  1. El host (Claude Code) lee su configuración MCP (.mcp.json) y lanza el servidor como un subproceso local normal, con el comando/argumentos especificados y las variables de entorno.

  2. El host y el servidor intercambian mensajes JSON-RPC a través de stdin/stdout de ese subproceso (de ahí el requisito de que stdout está reservado solo para el protocolo — ver la sección "Registro y depuración").

  3. El host llama a initialize() — el servidor responde con su nombre/versión (mcp-project-helper 0.1.0) y sus capacidades.

  4. El host llama a list_tools() — el servidor devuelve la lista de tools registradas con su nombre, descripción y JSON Schema de los parámetros de entrada (inputSchema), generada por el MCP SDK a partir de la firma de la función.

  5. Cuando el usuario (o el propio modelo) decide invocar una de las tools, el host envía call_tool(name, arguments); el servidor ejecuta la función Python correspondiente y devuelve un resultado estructurado (ver "Contrato de salida de las tools" más abajo) o un error a nivel de tool.

  6. No se abre ningún puerto de red: el ciclo de vida del servidor está completamente vinculado al subproceso lanzado por el host — si el host cierra la conexión, el subproceso termina.

Aquí no hay llamadas a ninguna API de LLM/IA (OpenAI, Anthropic, etc.): este servidor solo proporciona tools que invoca el cliente (Claude Code); la "búsqueda" en get_docs es una simple coincidencia determinista de subcadenas sobre secciones de Markdown, sin embeddings/base de datos vectorial. Para ejecutar el servidor no se necesita ninguna clave de API.

Qué se considera tool en este servidor

Tool — es una función Python normal, decorada con @mcp.tool(), que acepta argumentos serializables a JSON y devuelve dict[str, Any]. El MCP SDK automáticamente:

  • genera inputSchema (JSON Schema) a partir de la firma y las anotaciones de tipos de los argumentos de la función — no es necesario describir ese esquema manualmente en ningún sitio;

  • convierte la anotación del valor de retorno -> dict[str, Any] en una salida estructurada de la tool (outputSchema/structuredContent), ver "Contrato de salida de las tools" más abajo;

  • convierte una excepción Python no manejada dentro de la función de la tool en un resultado estructurado con error a nivel de tool (CallToolResult.is_error = True), sin derribar la propia sesión MCP.

Las cuatro registraciones están juntas en server.py:37-58; cada una es un envoltorio fino orientado a MCP (cuyo docstring se convierte en la descripción de la tool visible para el modelo) que delega la llamada a la implementación real en tools/*.py, separando la firma a nivel de protocolo de la lógica.

Stack

  • Python 3.14 (requires-python = ">=3.10" en pyproject.toml — este es el límite inferior real del MCP SDK utilizado, no una afirmación de que solo funciona con 3.14).

  • MCP Python SDK oficial (paquete mcp, versión instalada 2.0.0) — proporciona el framework del servidor (mcp.server.MCPServer), el registro de tools (@mcp.tool()) y el transporte stdio (mcp.run(transport="stdio")).

  • pytest — única dependencia de desarrollo, para el conjunto de tests.

  • No hay integración con API de LLM/IA ni transporte de red (HTTP/SSE no está configurado) — ver la sección anterior.

Arquitectura

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       запуск подпроцесса из белого списка

Cada tool que trabaja con archivos pasa por security.resolve_within_root(root, relative_path) antes de abrir cualquier ruta. config.py define la raíz del proyecto a partir de la variable de entorno MCP_PROJECT_HELPER_ROOT (por defecto ./demo_project), por lo que el servidor puede apuntarse a cualquier proyecto sin cambiar el código.

Tools MCP implementadas

search_project_files(query, path=".", max_results=50)

Busca recursivamente en los archivos de texto bajo path (relativo a la raíz del proyecto; por defecto — toda la raíz) la coincidencia exacta de la subcadena query. Omite los directorios de config.IGNORED_DIR_NAMES (.git, .venv, __pycache__, node_modules, ...) y cualquier directorio *.egg-info. Los archivos se comprueban por contenido binario (byte NUL o UTF-8 no válido en los primeros 4 KB) y se omiten silenciosamente, en lugar de provocar un error. Nunca sigue enlaces simbólicos a directorios o archivos fuera de la raíz — cada ruta candidata se comprueba adicionalmente mediante resolve_within_root, además del comportamiento estándar de os.walk, que no sigue enlaces simbólicos a directorios.

max_results está limitado superiormente por config.SEARCH_RESULTS_CAP (200); las líneas coincidentes más largas que config.SEARCH_MAX_LINE_CHARS (300) se truncan; los archivos mayores que config.SEARCH_MAX_FILE_BYTES (2 MB) se omiten, en lugar de escanearse.

Implementación: tools/search_project_files.py:41-130.

read_project_file(path)

Lee un único archivo de texto en la ruta path (relativa a la raíz del proyecto). Rechaza directorios, archivos inexistentes y contenido binario (byte NUL o UTF-8 no válido). El contenido está limitado por config.READ_MAX_FILE_BYTES (200 KB) — los archivos de mayor tamaño se devuelven truncados, en lugar de rechazarse.

Implementación: tools/read_project_file.py:25-69.

get_docs(query=None, max_results=10)

Busca en docs/*.md (recursivamente), divididos en secciones por los encabezados de Markdown. Con query presente, devuelve las secciones cuyo encabezado o cuerpo contienen la subcadena buscada (sin distinguir mayúsculas), cada una con indicación del archivo de origen y el encabezado. Sin query, devuelve una lista de una sección por archivo — un inventario de qué documentación existe. Limitado solo por config.get_docs_root() — nunca por la raíz del proyecto.

max_results está limitado superiormente por config.DOCS_RESULTS_CAP (50); los fragmentos (snippets) están limitados por config.DOCS_MAX_SNIPPET_CHARS (800 caracteres).

Implementación: tools/get_docs.py:58-114.

run_project_check(check_name)

Ejecuta una verificación de la lista blanca. check_name se busca en config.ALLOWED_CHECKS antes de que se ejecute nada — un nombre desconocido provoca inmediatamente un error, y el subproceso nunca se lanza. El argv de la lista blanca se ejecuta mediante subprocess.run(argv, shell=False, cwd=<raíz del proyecto>, timeout=...): sin shell, con directorio de trabajo fijo, y nada de la parte que llama se añade a la línea de comandos.

Implementación: tools/run_project_check.py:32-90.

Lista blanca

ALLOWED_CHECKS = {
    "tests": [sys.executable, "-m", "pytest", "-q"],
}

Definida en config.py:55-57. Se usa sys.executable (y no simplemente la cadena "pytest") para que la verificación se ejecute siempre con el mismo intérprete/entorno que el propio servidor, independientemente de lo que esté primero en PATH. Aquí deliberadamente no hay una entrada lint: en este repositorio no hay dependencia ni configuración de ruff, por lo que conectar una verificación "lint" sería o una ficción o un engaño. Se puede añadir más adelante (config.ALLOWED_CHECKS["lint"] = [sys.executable, "-m", "ruff", "check", "."]), cuando ruff se convierta en una dependencia real del proyecto con configuración real — el mecanismo de lista blanca ya lo soporta sin ningún otro cambio de código.

El tiempo de espera (config.CHECK_TIMEOUT_SECONDS, por defecto 60 s) y el límite de volumen de salida (config.CHECK_MAX_OUTPUT_CHARS, por defecto 20 000 caracteres por flujo) se aplican a cada ejecución de verificación.

Contrato de salida de las tools

Cada tool devuelve un dict Python normal desde una función con anotación -> dict[str, Any]; el MCP SDK reconoce automáticamente esto como salida estructurada de la tool (rellena CallToolResult.structured_content y emite outputSchema) — aquí no se serializan manualmente los resultados a una cadena JSON en ningún sitio. Las situaciones de error (entrada incorrecta, salida fuera de la ruta, verificación desconocida, archivo no encontrado, contenido binario, etc.) lanzan una excepción Python, en lugar de devolver un dict; el SDK convierte automáticamente esto en un resultado con error a nivel de tool (CallToolResult.is_error = True). La única excepción es el tiempo de espera de la verificación: es un resultado legítimo de la ejecución de una verificación lanzada con éxito, no un error de datos de entrada, por lo que se devuelve como un dict estructurado {"status": "error", ...}, en lugar de lanzar una excepción.

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
}

En caso de tiempo de espera: {"status": "error", "check_name": ..., "error": "check timed out after 60s", "exit_code": null, "stdout": "...", "stderr": "...", "truncated": ...}.

Los nombres de los campos y las convenciones status/count/truncated anteriores se consideran un contrato estable para el futuro, no un detalle de implementación.

Limitaciones de seguridad

  • Limitación de rutas: security.resolve_within_root (security.py:19-50) rechaza rutas absolutas, el ascenso mediante .. (a cualquier profundidad), los bytes NUL y los enlaces simbólicos que apunten fuera de la raíz configurada. Se usa en read_project_file y search_project_files relativo a la raíz del proyecto, y de nuevo en search_project_files para cada archivo candidato durante el recorrido. Está cubierto por unit tests en tests/test_security.py y confirmado manualmente con una prueba negativa real mediante Claude Code (ver "Resultados de la verificación" más abajo, prueba 6).

  • Sin salida de la raíz mediante enlaces simbólicos durante el recorrido: search_project_files y get_docs nunca siguen enlaces simbólicos a directorios (comportamiento predeterminado de os.walk) y omiten por completo los enlaces simbólicos a archivos.

  • Sin comandos shell arbitrarios: run_project_check comprueba el nombre de verificación solicitado contra config.ALLOWED_CHECKS (config.py:55-57) antes de que se ejecute nada; los nombres desconocidos se rechazan de inmediato, y la propia verificación se ejecuta mediante subprocess.run(argv, shell=False, ...) con un cwd fijo y sin ningún argumento añadido por la parte que invoca.

  • Salida limitada en todas partes: cada tool limita el volumen de datos devueltos: max_results + límites estrictos para búsqueda y docs, límite de bytes para lectura de archivos, límite de caracteres + tiempo de espera para la salida de la verificación — de modo que ninguna llamada puede devolver un volumen ilimitado de datos ni ejecutarse indefinidamente.

  • stdout permanece limpio: todo el registro va a través de logging_setup.py a stderr (y opcionalmente a un archivo de log); nada en el servidor escribe a stdout, que está reservado para el enmarcado JSON-RPC del protocolo MCP.

  • Sin secretos en los logs: el servidor no acepta en absoluto claves API ni credenciales. Cada llamada real a un tool registra el nombre del tool, sus parámetros de entrada seguros (cadenas de consulta, rutas, nombres de verificación, cantidad/tamaño de resultados — pero nunca el contenido de archivos) y el status=success/status=error final.

Registro y depuración

Cada llamada real a un tool registra una línea a través del logger común mcp_project_helper (stderr, más un archivo opcional mediante MCP_PROJECT_HELPER_LOG_FILE), por ejemplo (líneas reales de 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

El contenido de los archivos nunca se registra — solo metadatos de la llamada (rutas, cadenas de consulta, tamaños, cantidades, códigos de salida). La configuración del registro es logging_setup.py:20-42.

Para depurar:

  • El nivel de registro se ajusta con MCP_PROJECT_HELPER_LOG_LEVEL (DEBUG, INFO, WARNING, ERROR, CRITICAL; por defecto INFO).

  • El archivo de log se define con MCP_PROJECT_HELPER_LOG_FILE; por defecto (sin esta variable) se escribe solo a stderr. Al iniciarse desde Claude Code (.mcp.json) apunta a evidence/tool-calls.log.

  • Nunca use print() en el código del servidor — stdout está reservado para el protocolo JSON-RPC; cualquier salida extra a stdout rompe el transporte stdio.

  • Para ver qué llamadas ocurrieron realmente en la sesión actual de Claude Code, abra el archivo al que apunta MCP_PROJECT_HELPER_LOG_FILE (evidence/tool-calls.log), o ejecute el servidor manualmente (python -m mcp_project_helper.server) y observe stderr.

Instalación

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

La única dependencia en tiempo de ejecución es el paquete mcp; pytest es una dependencia solo para desarrollo/pruebas (ambas fijadas en pyproject.toml).

Configuración del entorno

La configuración se define mediante variables de entorno — copie .env.example a .env y modifique los valores si es necesario:

Variable

Propósito

Valor predeterminado

MCP_PROJECT_HELPER_ROOT

El único directorio al que tienen acceso las herramientas de archivos (search_project_files, read_project_file, get_docs — solo a su docs/; get_docs opera desde la raíz del repositorio, no desde MCP_PROJECT_HELPER_ROOT).

./demo_project

MCP_PROJECT_HELPER_LOG_FILE

Ruta al archivo de log (ver "Registro y depuración"). Los logs siempre van también a stderr.

no definido (solo stderr)

MCP_PROJECT_HELPER_LOG_LEVEL

Uno de DEBUG/INFO/WARNING/ERROR/CRITICAL.

INFO

El servidor no necesita secretos (claves API, tokens) — .env.example contiene solo ejemplos seguros de rutas y nivel de registro, y .env es ignorado por git (ver "Estructura del proyecto" más abajo).

Ejecución del servidor MCP

Ejecutar el servidor directamente (permanecerá esperando al cliente en stdin — esto es normal para servidores MCP con transporte stdio; salir con Ctrl+C):

python -m mcp_project_helper.server

Ejecutar la suite de pruebas:

pytest -q

Ejecutar directamente las pruebas del propio demo-proyecto (lo que por defecto ejecuta run_project_check("tests"), ya que MCP_PROJECT_HELPER_ROOT por defecto apunta a demo_project):

cd demo_project && pytest -q

Integración con Claude Code

Este repositorio contiene un archivo project-scoped .mcp.json en la raíz del repositorio: la configuración específicamente para Claude Code (a diferencia de .vscode/mcp.json, una configuración separada del host MCP nativo VS Code; ver la comparación detallada más abajo).

Claude Code detecta .mcp.json al abrir la carpeta del proyecto, inicia el servidor como proceso hijo y se comunica con él mediante JSON-RPC a través de stdio — el mismo transporte utilizado en todas las pruebas automatizadas de este proyecto, solo que iniciado por el propio Claude Code, no por un harness de pruebas.

Escenario end-to-end confirmado: Claude Code CLI → MCP server → custom tools. Las 6 solicitudes de verificación (ver "Resultados de la verificación" más abajo) se ejecutaron realmente a través de Claude Code CLI con este servidor conectado mediante .mcp.json — no solo configuradas, sino realmente invocadas, con capturas de pantalla reales y registros en el log del lado del servidor.

Configuración para Claude Code

.mcp.json:

{
  "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} es expandido por el propio Claude Code a la ruta absoluta del directorio en el que se clonó el repositorio, por lo que el archivo no contiene rutas específicas de una máquina concreta y no requiere ediciones después de git clone. Se utiliza precisamente la forma con valor de respaldo ${CLAUDE_PROJECT_DIR:-.}, y no el ${CLAUDE_PROJECT_DIR} a secas: sin :-. la variable no se expandía y Claude Code intentaba ejecutar literalmente ${CLAUDE_PROJECT_DIR}/.venv/bin/python como ruta al ejecutable (este error se observó realmente en la primera versión de la configuración de Stage 2, ver REPORT.md). MCP_PROJECT_HELPER_ROOT está definido explícitamente como ${CLAUDE_PROJECT_DIR:-.}/demo_project, para que la raíz del proyecto pasada al servidor no sea ambigua, independientemente del valor predeterminado propio en config.py.

Nota sobre plataformas: .venv/bin/python es la estructura de venv para Unix (macOS/Linux), utilizada en todo este proyecto. En Windows la ruta equivalente es .venv\Scripts\python.exe; para admitir también esa plataforma, .mcp.json habría requerido una segunda entrada específica para Windows (o un script envoltorio) — no se ha hecho, porque el proyecto se desarrolló y verificó solo en macOS.

Configuración para VS Code

Este repositorio también contiene .vscode/mcp.json, una configuración separada de espacio de trabajo para el host MCP integrado de VS Code (utilizado por el modo agéntico de 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"
      }
    }
  }
}

El mismo servidor stdio mcp-project-helper, con MCP_PROJECT_HELPER_ROOT definido como ${workspaceFolder}/demo_project, y MCP_PROJECT_HELPER_LOG_FILE definido como ${workspaceFolder}/evidence/tool-calls.log.

Por qué dos archivos y no uno: .mcp.json y .vscode/mcp.json siguen esquemas diferentes e incompatibles, y sus variables de sustitución de rutas no son intercambiables entre hosts:

  • .mcp.json (configuración de Claude Code) utiliza la clave de nivel superior mcpServers y expande ${CLAUDE_PROJECT_DIR:-.} a la raíz del repositorio.

  • .vscode/mcp.json (configuración del host MCP nativo de VS Code) utiliza la clave de nivel superior servers, el campo explícito "type": "stdio" y expande en su lugar ${workspaceFolder} a la ruta de la carpeta abierta. El host MCP de VS Code no entiende ${CLAUDE_PROJECT_DIR}: al intentar abrir .mcp.json directamente desde VS Code, la variable se pasa literalmente y el servidor no puede iniciarse (spawn ${CLAUDE_PROJECT_DIR}/.venv/bin/python ENOENT) — este es un error realmente observado, que dio origen al .vscode/mcp.json separado. Almacenar la configuración de cada host en su propio archivo, con su propia variable, evita este error y permite utilizar ambas herramientas con el mismo clon sin que una configuración comprometa la sintaxis de la otra.

.vscode/mcp.json es la única excepción a la regla general de ignorar .vscode/* en .gitignore; el resto del estado local de VS Code (settings.local.json, etc.) no se rastrea.

Estado de verificación de VS Code: .vscode/mcp.json es sintáctica y semánticamente correcto (el mismo servidor, el mismo comando/variables de entorno que la configuración de trabajo de Claude Code) y fue validado como JSON. Además, la captura de pantalla real evidence/vscode_mcp_server_connected.png confirma que el host MCP nativo de VS Code realmente inicia el servidor con esta configuración: Starting server mcp-project-helper → Connection state: Running → Discovered 4 tools, con una línea de confirmación del propio registro de stderr del proceso mcp_project_helper en esa misma salida. Esto no es lo mismo que la confirmación de invocación de custom tools a través de la interfaz de VS Code: ningún escenario de usuario (search_project_files, etc.) se ejecutó a través de esa interfaz ni se afirma como verificado. La única integración con IDE confirmada hasta las invocaciones reales de tools por el usuario (capturas de pantalla + logs del lado del servidor para los 6 escenarios) es Claude Code CLI, ver "Resultados de la verificación" más abajo. De forma independiente a ambos: la integración mediante Claude Code Desktop / extensión de Claude Code dentro de VS Code no se probó en esta sesión en absoluto — no debe confundirse ni con el host MCP nativo de VS Code (esta sección), ni con Claude Code CLI.

Cómo habilitar MCP

Brevemente (los detalles — en las subsecciones anteriores):

Claude Code:

  1. Cree un venv e instale las dependencias (sección "Instalación").

  2. Abra la raíz del repositorio en Claude Code (claude desde la raíz del repositorio).

  3. Claude Code detecta .mcp.json y ofrece una vez confirmar la confianza del espacio de trabajo para el servidor mcp-project-helper: confírmelo.

  4. Ejecute /mcp (o claude mcp list en la terminal) y compruebe que mcp-project-helper está conectado con 4 tools.

VS Code (host MCP nativo, modo agéntico de Copilot Chat):

  1. Cree un venv igual que para Claude Code — .vscode/mcp.json espera el mismo .venv/bin/python.

  2. Abra la raíz del repositorio como carpeta en VS Code.

  3. VS Code detecta .vscode/mcp.json y ofrece iniciar el servidor — inícielo/confirme la confianza.

  4. Compruebe el estado mediante MCP: List Servers.

Ambas opciones suponen una estructura Unix de venv (.venv/bin/python); en Windows — .venv\Scripts\python.exe (no configurado, ver arriba).

Consultas de verificación

Seis escenarios ejecutados realmente a través de Claude Code CLI para confirmar la integración (tabla completa con resultados en evidence/README.md):

  1. Busca a través de MCP todos los lugares donde se usa la función apply_discount en demo_project → se espera search_project_files.

  2. Lee a través de MCP el archivo demo_app/models.py y explica brevemente qué modelos están definidos allí → se espera read_project_file.

  3. Usando la documentación MCP del proyecto, cuenta qué limitaciones de seguridad tiene el servidor MCP → se espera get_docs.

  4. Comprueba a través de la herramienta MCP si pasan los tests de demo_project → se espera run_project_check.

  5. Usando solo las herramientas MCP, encuentra en demo_project la implementación de apply_discount, luego lee el archivo donde está definida y explica sus parámetros/retorno/cálculo del descuento → se espera una cadena de dos tools: search_project_files y luego read_project_file.

  6. (prueba negativa / de seguridad) Intenta leer a través de MCP el archivo ../../../../etc/passwd → se espera denegación de read_project_file con un error estructurado (la ruta excede la raíz permitida).

Resultados de la verificación

Las 6 de 6 consultas se completaron con éxito (en la prueba 5, ambas tools esperadas, en el orden correcto; en la prueba 6, el éxito es la denegación esperada). Cada línea está confirmada tanto con una captura de pantalla real como con una línea independiente en evidence/tool-calls.log. La tabla completa está en evidence/README.md; el análisis detallado con enlaces al código y los registros está en REPORT.md.

№

Tool

Resultado

1

search_project_files

Éxito, 4 coincidencias

2

read_project_file

Éxito, size=397

3

get_docs

Éxito, se encontró la sección «Seguridad»

4

run_project_check

Éxito, exit_code=0, 2/2 tests superados

5

search_project_files → read_project_file

Éxito, cadena de dos tools

6

read_project_file

Denegación exitosa (path traversal bloqueado)

Comprobaciones automatizadas (no sustituyen, sino que complementan las pruebas manuales de IDE anteriores):

  • pytest -q desde la raíz del repositorio — 44 passed.

  • pytest -q dentro de demo_project/ — 2 passed.

  • Handshake stdio programático (initialize() + list_tools()) — el servidor informa mcp-project-helper 0.1.0 y exactamente 4 tools: get_docs, read_project_file, run_project_check, search_project_files.

Estructura del proyecto

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 tools
get_docsC

Search local documentation from the repo's docs/ directory.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNo
max_resultsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

C2.6/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters1/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

B3.2/5.0
Behavior3/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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').

ParametersJSON Schema
NameRequiredDescriptionDefault
check_nameYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

C2.9/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathNo.
queryYes
max_resultsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

C2.7/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

  1. 4 tool updatesv0.1.0
    • First observedget_docs
    • First observedread_project_file
    • First observedrun_project_check
    • First observedsearch_project_files

TDQS

B3.2/5.0

Scored across 4 tools

Disambiguation4/5

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.

Naming Consistency5/5

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.

Tool Count4/5

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.

Completeness3/5

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

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Agent-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 npm
    3
    AGPL 3.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    A 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 npm
    25
    MIT