Skip to main content
Glama

mcp-project-helper

Ein minimaler MCP-Server (Model Context Protocol), der einem KI-Programmierassistenten – insbesondere Claude Code – einen kleinen, sicheren Satz von Tools für die Arbeit mit einer bestimmten Projektverzeichnis bereitstellt: Suche in dessen Dateien, Datei lesen, Suche in lokaler Dokumentation und Ausführen einer vorab genehmigten Prüfung (Tests).

Das Projekt ist schrittweise umgesetzt (Stage 0 → Stage 3, Prompt-Verlauf in PROMPTS.md); die aktuelle Stufe ist Stage 3: Finalisierung. Alle vier Tools sind implementiert und mit Tests abgedeckt (Stage 1), der Server ist an Claude Code angebunden und manuell mit echten Anfragen über die Claude-Code-CLI verifiziert (Stage 2–3, Belege in evidence/).

Über das Projekt

Statt dem Assistenten direkten Shell-Zugriff oder uneingeschränkten Dateisystemzugriff zu geben, stellt das Projekt eine schmale, leicht prüfbare Oberfläche aus vier Tools bereit:

  • search_project_files – Textsuche, begrenzt auf das Projektverzeichnis.

  • read_project_file – Lesen einer einzelnen Datei, begrenzt auf das Projektverzeichnis.

  • get_docs – Suche in der lokalen Dokumentation unter docs/ dieses Repositories.

  • run_project_check – Ausführen einer Prüfung aus einer Whitelist (derzeit tests), aber niemals einer beliebigen Shell-Kommandozeile.

Dies ist ein Lernprojekt (Hausaufgabe) – dessen Ziel nicht ist, alle möglichen Anwendungsfälle abzudecken, sondern ein durchgängiges, ehrlich dokumentiertes Beispiel eines MCP-Servers zu zeigen: vom Gerüst und den Sicherheitsprimitiven (Stage 0), über die tatsächliche Implementierung der Tools (Stage 1), bis zur Integration mit einem IDE-Agenten und reproduzierbaren Belegen realer Aufrufe (Stage 2–3).

Related MCP server: GPT Commander

Was ist MCP und wie funktioniert die Agentenanbindung

MCP (Model Context Protocol) ist ein offenes Protokoll auf Basis von JSON-RPC, das beschreibt, wie ein KI-Assistent (Client/Host, z. B. Claude Code) externe Tools entdeckt und aufruft, die von einem separaten Prozess (MCP-Server) bereitgestellt werden – ohne dass der Assistent direkten Zugriff auf Shell, Netzwerk oder Dateisystem des Hosts hat.

In diesem Projekt wird der stdio-Transport verwendet – die einfachste und am weitesten verbreitete Methode für lokale Tools:

  1. Der Host (Claude Code) liest seine MCP-Konfiguration (.mcp.json) und startet den Server als gewöhnlichen lokalen Unterprozess, mit der angegebenen Kommandozeile/Argumenten und Umgebungsvariablen.

  2. Host und Server tauschen JSON-RPC-Nachrichten über stdin/stdout dieses Unterprozesses aus (daher die Anforderung, dass stdout ausschließlich für das Protokoll reserviert ist – siehe Abschnitt „Logging und Debugging").

  3. Der Host ruft initialize() auf – der Server antwortet mit seinem Namen/Version (mcp-project-helper 0.1.0) und seinen Fähigkeiten.

  4. Der Host ruft list_tools() auf – der Server gibt die Liste der registrierten Tools mit Name, Beschreibung und JSON-Schema der Eingabeparameter (inputSchema) zurück, das vom MCP SDK aus der Funktionssignatur generiert wird.

  5. Wenn der Benutzer (oder das Modell selbst) beschließt, eines der Tools aufzurufen, sendet der Host call_tool(name, arguments); der Server führt die entsprechende Python-Funktion aus und gibt ein strukturiertes Ergebnis zurück (siehe „Tool-Output-Vertrag" unten) oder einen Fehler auf Tool-Ebene.

  6. Es wird kein Netzwerkport geöffnet: Der Lebenszyklus des Servers ist vollständig an den vom Host gestarteten Unterprozess gebunden – wenn der Host die Verbindung schließt, wird der Unterprozess beendet.

Es gibt hier keine Aufrufe an LLM/AI-APIs (OpenAI, Anthropic usw.): Dieser Server stellt nur Tools bereit, die der Client (Claude Code) aufruft; die „Suche" in get_docs ist ein einfaches deterministisches Teilstring-Matching über Markdown-Abschnitte, ohne Embeddings/Vektordatenbank. Zum Starten des Servers ist kein API-Schlüssel erforderlich.

Was in diesem Server als Tool gilt

Tool ist eine gewöhnliche Python-Funktion, dekoriert mit @mcp.tool(), die JSON-serialisierbare Argumente annimmt und dict[str, Any] zurückgibt. Das MCP SDK:

  • generiert automatisch inputSchema (JSON Schema) aus der Signatur und den Typannotationen der Funktionsargumente – die Schema muss nirgendwo manuell beschrieben werden;

  • wandelt die Rückgabeannotation -> dict[str, Any] in strukturierte Tool-Ausgabe um (outputSchema/structuredContent), siehe „Tool-Output-Vertrag" unten;

  • wandelt eine unbehandelte Python-Ausnahme innerhalb der Tool-Funktion in ein strukturiertes Ergebnis mit Fehler auf Tool-Ebene um (CallToolResult.is_error = True), ohne die MCP-Sitzung selbst zu beenden.

Alle vier Registrierungen befinden sich nebeneinander in server.py:37-58; jede dünne, an MCP gerichtete Wrapper-Funktion (deren Docstring zur für das Modell sichtbaren Tool-Beschreibung wird) delegiert den Aufruf an die eigentliche Implementierung in tools/*.py und trennt so die Signatur auf Protokollebene von der Logik.

Technologie-Stack

  • Python 3.14 (requires-python = ">=3.10" in pyproject.toml – das ist die tatsächliche Untergrenze des verwendeten MCP SDK, keine Aussage, dass nur 3.14 funktioniert).

  • Offizielles MCP Python SDK (Paket mcp, installierte Version 2.0.0) – stellt das Server-Framework (mcp.server.MCPServer), die Tool-Registrierung (@mcp.tool()) und den stdio-Transport (mcp.run(transport="stdio")) bereit.

  • pytest – einzige Dev-Abhängigkeit, für die Testsuite.

  • Keine Integration mit LLM/AI-APIs und kein Netzwerktransport (HTTP/SSE ist nicht konfiguriert) – siehe vorheriger Abschnitt.

Architektur

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

Jedes Tool, das mit Dateien arbeitet, durchläuft security.resolve_within_root(root, relative_path), bevor ein Pfad geöffnet wird. config.py definiert das Projektverzeichnis aus der Umgebungsvariable MCP_PROJECT_HELPER_ROOT (Standard: ./demo_project), sodass der Server ohne Codeänderung auf ein beliebiges Projekt ausgerichtet werden kann.

Implementierte MCP-Tools

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

Sucht rekursiv in Textdateien unter path (relativ zum Projektverzeichnis; standardmäßig das gesamte Verzeichnis) nach exakter Teilstring-Übereinstimmung von query. Überspringt Verzeichnisse aus config.IGNORED_DIR_NAMES (.git, .venv, __pycache__, node_modules, ...) und beliebige *.egg-info-Verzeichnisse. Dateien werden auf binären Inhalt geprüft (NUL-Byte oder ungültiges UTF-8 in den ersten 4 KB) und stillschweigend übersprungen, statt einen Fehler zu verursachen. Folgt niemals symbolischen Links auf Verzeichnisse oder Dateien außerhalb des Projektverzeichnisses – jeder Kandidatenpfad wird zusätzlich über resolve_within_root geprüft, ergänzend zum Standardverhalten von os.walk, das symbolischen Links auf Verzeichnisse nicht folgt.

max_results ist nach oben durch config.SEARCH_RESULTS_CAP (200) begrenzt; übereinstimmende Zeilen länger als config.SEARCH_MAX_LINE_CHARS (300) werden abgeschnitten; Dateien größer als config.SEARCH_MAX_FILE_BYTES (2 MB) werden übersprungen statt gescannt.

Implementierung: tools/search_project_files.py:41-130.

read_project_file(path)

Liest eine einzelne Textdatei unter dem Pfad path (relativ zum Projektverzeichnis). Lehnt Verzeichnisse, nicht existierende Dateien und binären Inhalt ab (NUL-Byte oder ungültiges UTF-8). Der Inhalt ist durch config.READ_MAX_FILE_BYTES (200 KB) begrenzt – größere Dateien werden abgeschnitten zurückgegeben, nicht abgelehnt.

Implementierung: tools/read_project_file.py:25-69.

get_docs(query=None, max_results=10)

Sucht in docs/*.md (rekursiv), aufgeteilt in Abschnitte nach Markdown-Überschriften. Bei vorhandenem query werden Abschnitte zurückgegeben, deren Überschrift oder Text die gesuchte Teilzeichenkette enthält (ohne Beachtung der Groß-/Kleinschreibung), jeweils mit Angabe der Quelldatei und Überschrift. Ohne query wird eine Liste mit einem Abschnitt pro Datei zurückgegeben – eine Übersicht, welche Dokumentation existiert. Begrenzt nur durch config.get_docs_root() – niemals durch das Projektverzeichnis.

max_results ist nach oben durch config.DOCS_RESULTS_CAP (50) begrenzt; Ausschnitte (Snippets) sind durch config.DOCS_MAX_SNIPPET_CHARS (800 Zeichen) begrenzt.

Implementierung: tools/get_docs.py:58-114.

run_project_check(check_name)

Führt eine Prüfung aus der Whitelist aus. check_name wird in config.ALLOWED_CHECKS gesucht, bevor irgendetwas ausgeführt wird – ein unbekannter Name löst sofort einen Fehler aus, der Unterprozess wird dabei niemals gestartet. Die Argv aus der Whitelist wird über subprocess.run(argv, shell=False, cwd=<Projektverzeichnis>, timeout=...) ausgeführt: ohne Shell, mit festem Arbeitsverzeichnis, und nichts von der aufrufenden Seite wird zur Kommandozeile hinzugefügt.

Implementierung: tools/run_project_check.py:32-90.

Whitelist

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

Definiert in config.py:55-57. sys.executable (und nicht einfach die Zeichenkette "pytest") wird verwendet, damit die Prüfung immer mit demselben Interpreter/Umfeld wie der Server selbst ausgeführt wird, unabhängig davon, was in PATH zuerst steht. Hier gibt es bewusst keinen Eintrag lint: In diesem Repository gibt es keine ruff-Abhängigkeit oder -Konfiguration, daher wäre das Anbinden einer „lint"-Prüfung entweder eine Fiktion oder eine Täuschung. Sie kann später hinzugefügt werden (config.ALLOWED_CHECKS["lint"] = [sys.executable, "-m", "ruff", "check", "."]), wenn ruff eine echte Projektabhängigkeit mit echter Konfiguration wird – der Whitelist-Mechanismus unterstützt dies bereits ohne weitere Codeänderungen.

Timeout (config.CHECK_TIMEOUT_SECONDS, Standard 60 s) und Ausgabebegrenzung (config.CHECK_MAX_OUTPUT_CHARS, Standard 20.000 Zeichen pro Stream) gelten für jeden Prüfungslauf.

Tool-Output-Vertrag

Jedes Tool gibt ein gewöhnliches Python-dict aus einer Funktion mit der Annotation -> dict[str, Any] zurück; das MCP SDK erkennt dies automatisch als strukturierte Tool-Ausgabe (füllt CallToolResult.structured_content und gibt outputSchema aus) – Ergebnisse werden hier nirgendwo manuell in eine JSON-Zeichenkette serialisiert. Fehlersituationen (ungültige Eingabe, Pfad außerhalb des Verzeichnisses, unbekannte Prüfung, Datei nicht gefunden, binärer Inhalt usw.) lösen eine Python-Ausnahme aus, statt ein dict zurückzugeben; das SDK wandelt dies automatisch in ein Ergebnis mit Fehler auf Tool-Ebene um (CallToolResult.is_error = True). Die einzige Ausnahme ist der Prüfungs-Timeout: Das ist ein legitimes Ergebnis einer erfolgreich gestarteten Prüfung, kein Eingabefehler, daher wird es als strukturiertes dict {"status": "error", ...} zurückgegeben, statt eine Ausnahme auszulösen.

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
}

Bei Timeout: {"status": "error", "check_name": ..., "error": "check timed out after 60s", "exit_code": null, "stdout": "...", "stderr": "...", "truncated": ...}.

Die Feldnamen und die Konventionen status/count/truncated oben gelten als stabiler Vertrag für die Zukunft, nicht als Implementierungsdetail.

Sicherheitsbeschränkungen

  • Pfadeinschränkung: security.resolve_within_root (security.py:19-50) lehnt absolute Pfade, Traversal über .. (auf beliebiger Tiefe), NUL-Bytes und symbolische Links ab, die über den konfigurierten Root hinausführen. Wird in read_project_file und search_project_files relativ zum Projekt-Root verwendet und erneut in search_project_files für jede Kandidatendatei während der Traversierung. Abgedeckt durch Unit-Tests in tests/test_security.py und manuell durch einen echten Negativtest über Claude Code bestätigt (siehe „Ergebnisse der Prüfung“ unten, Test 6).

  • Kein Entkommen über symbolische Links bei der Traversierung: search_project_files und get_docs folgen niemals symbolischen Links auf Verzeichnisse (Standardverhalten von os.walk) und überspringen symbolische Links auf Dateien vollständig.

  • Keine beliebigen Shell-Befehle: run_project_check prüft den angeforderten Prüfungsnamen gegen config.ALLOWED_CHECKS (config.py:55-57) bevor irgendetwas ausgeführt wird; unbekannte Namen werden sofort abgelehnt, und die Prüfung selbst wird über subprocess.run(argv, shell=False, ...) mit festem cwd und ohne vom Aufrufer hinzugefügte Argumente ausgeführt.

  • Begrenzte Ausgabe überall: Jedes Tool begrenzt die Menge der zurückgegebenen Daten – max_results + strenge Grenzen für Suche und Docs, eine Byte-Grenze für das Lesen von Dateien, eine Zeichenbeschränkung + Timeout für die Prüfungsausgabe – sodass kein Aufruf eine unbegrenzte Datenmenge zurückgeben oder unendlich lange laufen kann.

  • stdout bleibt sauber: Die gesamte Protokollierung läuft über logging_setup.py nach stderr (und optional in eine Logdatei); nichts im Server schreibt nach stdout, das für das JSON-RPC-Framing des MCP-Protokolls reserviert ist.

  • Keine Geheimnisse in den Logs: Der Server nimmt überhaupt keine API-Schlüssel oder Anmeldedaten entgegen. Jeder tatsächliche Tool-Aufruf protokolliert den Tool-Namen, seine sicheren Eingabeparameter (Abfragezeichenfolgen, Pfade, Prüfungsnamen, Anzahl/Größe der Ergebnisse – aber niemals Dateiinhalte) und den endgültigen status=success/status=error.

Logging und Debugging

Jeder tatsächliche Tool-Aufruf protokolliert eine Zeile über den gemeinsamen Logger mcp_project_helper (stderr, plus optional eine Datei über MCP_PROJECT_HELPER_LOG_FILE), zum Beispiel (echte Zeilen aus 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

Dateiinhalte werden niemals protokolliert – nur Metadaten über den Aufruf (Pfade, Abfragezeichenfolgen, Größen, Anzahl, Exit-Codes). Die Protokollierungskonfiguration ist logging_setup.py:20-42.

Zum Debuggen:

  • Das Log-Level wird über MCP_PROJECT_HELPER_LOG_LEVEL gesteuert (DEBUG, INFO, WARNING, ERROR, CRITICAL; Standard ist INFO).

  • Die Logdatei wird über MCP_PROJECT_HELPER_LOG_FILE festgelegt; standardmäßig (ohne diese Variable) wird nur nach stderr geschrieben. Beim Start aus Claude Code (.mcp.json) zeigt sie auf evidence/tool-calls.log.

  • Verwenden Sie niemals print() im Servercode – stdout ist für das JSON-RPC-Protokoll reserviert; jede zusätzliche Ausgabe auf stdout bricht den stdio-Transport.

  • Um zu sehen, welche Aufrufe in der aktuellen Claude-Code-Sitzung tatsächlich stattgefunden haben, öffnen Sie die Datei, auf die MCP_PROJECT_HELPER_LOG_FILE zeigt (evidence/tool-calls.log), oder starten Sie den Server manuell (python -m mcp_project_helper.server) und sehen Sie sich stderr an.

Installation

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

Die einzige Runtime-Abhängigkeit ist das Paket mcp; pytest ist eine Abhängigkeit nur für Entwicklung/Tests (beide sind in pyproject.toml festgehalten).

Umgebungskonfiguration

Die Konfiguration erfolgt über Umgebungsvariablen – kopieren Sie .env.example nach .env und ändern Sie bei Bedarf die Werte:

Variable

Zweck

Standardwert

MCP_PROJECT_HELPER_ROOT

Das einzige Verzeichnis, auf das die Datei-Tools zugreifen können (search_project_files, read_project_file, get_docs – nur auf dessen docs/; get_docs arbeitet vom Repository-Root aus, nicht von MCP_PROJECT_HELPER_ROOT).

./demo_project

MCP_PROJECT_HELPER_LOG_FILE

Pfad zur Logdatei (siehe „Logging und Debugging“). Logs gehen immer zusätzlich nach stderr.

nicht gesetzt (nur stderr)

MCP_PROJECT_HELPER_LOG_LEVEL

Einer von DEBUG/INFO/WARNING/ERROR/CRITICAL.

INFO

Der Server benötigt keine Geheimnisse (API-Schlüssel, Tokens) – .env.example enthält nur sichere Beispiele für Pfade und Log-Level, und .env wird von Git ignoriert (siehe „Projektstruktur“ unten).

MCP-Server starten

Direktstart des Servers (er wartet auf einen Client auf stdin – das ist normal für MCP-Server mit stdio-Transport; Beenden mit Strg+C):

python -m mcp_project_helper.server

Start der Testsuite:

pytest -q

Direktstart der eigenen Tests des Demo-Projekts (das, was run_project_check("tests") standardmäßig ausführt, da MCP_PROJECT_HELPER_ROOT standardmäßig auf demo_project zeigt):

cd demo_project && pytest -q

Integration mit Claude Code

Dieses Repository enthält eine project-scoped Datei .mcp.json im Repository-Root – eine Konfiguration speziell für Claude Code (im Gegensatz zu .vscode/mcp.json – einer separaten Konfiguration für den nativen MCP-Host VS Code; siehe ausführlicher Vergleich unten).

Claude Code erkennt .mcp.json beim Öffnen des Projektordners, startet den Server als Kindprozess und kommuniziert per JSON-RPC über stdio mit ihm – derselbe Transport, der in allen automatisierten Tests dieses Projekts verwendet wird, nur von Claude Code selbst gestartet statt von einem Test-Harness.

Bestätigtes End-to-End-Szenario: Claude Code CLI → MCP-Server → Custom-Tools. Alle 6 Prüfanfragen (siehe „Ergebnisse der Prüfung“ unten) wurden tatsächlich über die Claude Code CLI mit diesem Server ausgeführt, der über .mcp.json verbunden war – nicht nur konfiguriert, sondern tatsächlich aufgerufen, mit echten Screenshots und Einträgen im serverseitigen Log.

Konfiguration für 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} wird von Claude Code selbst in den absoluten Pfad des Verzeichnisses aufgelöst, in das das Repository geklont wurde; die Datei enthält daher keine maschinenspezifischen Pfade und erfordert nach git clone keine Änderungen. Verwendet wird genau die Form mit Fallback-Wert ${CLAUDE_PROJECT_DIR:-.} und nicht das nackte ${CLAUDE_PROJECT_DIR}: Ohne :-. wurde die Variable nicht aufgelöst, und Claude Code versuchte, ${CLAUDE_PROJECT_DIR}/.venv/bin/python wörtlich als Pfad zur ausführbaren Datei zu starten (dieser Fehler wurde tatsächlich bei der ersten Version der Stage-2-Konfiguration festgestellt, siehe REPORT.md). MCP_PROJECT_HELPER_ROOT ist explizit als ${CLAUDE_PROJECT_DIR:-.}/demo_project gesetzt, damit der an den Server übergebene Projekt-Root unabhängig vom eigenen Standardwert in config.py eindeutig ist.

Hinweis zu Plattformen: .venv/bin/python ist die venv-Struktur für Unix (macOS/Linux), die in diesem gesamten Projekt verwendet wird. Unter Windows lautet der entsprechende Pfad .venv\Scripts\python.exe; um auch diese Plattform zu unterstützen, würde .mcp.json einen zweiten, Windows-spezifischen Eintrag (oder ein Wrapper-Skript) erfordern – das wurde nicht getan, da das Projekt nur auf macOS entwickelt und getestet wurde.

Konfiguration für VS Code

Dieses Repository enthält außerdem .vscode/mcp.json – eine separate Arbeitsbereichskonfiguration für den eingebauten MCP-Host von VS Code (der im Agentenmodus von GitHub Copilot Chat verwendet wird):

{
  "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"
      }
    }
  }
}

Derselbe stdio-Server mcp-project-helper, mit MCP_PROJECT_HELPER_ROOT gesetzt auf ${workspaceFolder}/demo_project und MCP_PROJECT_HELPER_LOG_FILE gesetzt auf ${workspaceFolder}/evidence/tool-calls.log.

Warum zwei Dateien und nicht eine: .mcp.json und .vscode/mcp.json folgen unterschiedlichen, inkompatiblen Schemata, und ihre Pfad-Substitutionsvariablen sind zwischen den Hosts nicht austauschbar:

  • .mcp.json (Claude-Code-Konfiguration) verwendet den Top-Level-Schlüssel mcpServers und löst ${CLAUDE_PROJECT_DIR:-.} in den Repository-Root auf.

  • .vscode/mcp.json (Konfiguration für den nativen MCP-Host von VS Code) verwendet den Top-Level-Schlüssel servers, ein explizites Feld "type": "stdio" und löst stattdessen ${workspaceFolder} in den Pfad des geöffneten Ordners auf. Der MCP-Host von VS Code versteht ${CLAUDE_PROJECT_DIR} nicht – beim Versuch, .mcp.json direkt aus VS Code zu öffnen, wird die Variable wörtlich übergeben, und der Server kann nicht starten (spawn ${CLAUDE_PROJECT_DIR}/.venv/bin/python ENOENT) – das ist ein tatsächlich beobachteter Fehler, der zum separaten .vscode/mcp.json geführt hat. Das Speichern der Konfiguration jedes Hosts in einer eigenen Datei mit eigener Variable vermeidet diesen Fehler und erlaubt beiden Werkzeugen, denselben Clone zu verwenden, ohne dass eine Konfiguration die Syntax der anderen beeinträchtigt.

.vscode/mcp.json ist die einzige Ausnahme von der allgemeinen Regel, .vscode/* in .gitignore zu ignorieren; der restliche lokale VS-Code-Zustand (settings.local.json usw.) wird nicht nachverfolgt.

Status der VS-Code-Prüfung: .vscode/mcp.json ist syntaktisch und semantisch korrekt (derselbe Server, dieselbe Befehlszeile/Umgebungsvariablen wie die funktionierende Claude-Code-Konfiguration) und wurde als JSON validiert. Zusätzlich wird durch einen echten Screenshot evidence/vscode_mcp_server_connected.png bestätigt, dass der eingebaute native MCP-Host von VS Code den Server tatsächlich mit dieser Konfiguration startet: Starting server mcp-project-helper → Connection state: Running → Discovered 4 tools, mit einer bestätigenden Zeile aus dem eigenen stderr-Log des Prozesses mcp_project_helper in derselben Ausgabe. Das ist nicht dasselbe wie die Bestätigung des Aufrufs von Custom-Tools über die VS-Code-Oberfläche – kein einziges Benutzerszenario (search_project_files usw.) wurde über diese Oberfläche ausgeführt und wird nicht als geprüft beansprucht. Die einzige IDE-Integration, die bis zu tatsächlichen Tool-Aufrufen durch den Benutzer bestätigt ist (Screenshots + serverseitige Logs für alle 6 Szenarien), ist Claude Code CLI, siehe „Ergebnisse der Prüfung“ unten. Unabhängig von beiden: Die Integration über Claude Code Desktop / die Claude-Code-Erweiterung in VS Code wurde in dieser Sitzung überhaupt nicht geprüft – nicht zu verwechseln weder mit dem nativen MCP-Host von VS Code (dieser Abschnitt) noch mit der Claude Code CLI.

MCP aktivieren

Kurz (Details in den obigen Unterabschnitten):

Claude Code:

  1. Erstellen Sie ein venv und installieren Sie die Abhängigkeiten (Abschnitt „Installation“).

  2. Öffnen Sie den Repository-Root in Claude Code (claude aus dem Repository-Root).

  3. Claude Code erkennt .mcp.json und bietet einmal an, das Workspace-Vertrauen für den Server mcp-project-helper zu bestätigen – bestätigen Sie.

  4. Führen Sie /mcp aus (oder claude mcp list im Terminal) und stellen Sie sicher, dass mcp-project-helper mit 4 Tools verbunden ist.

VS Code (nativer MCP-Host, Agentenmodus von Copilot Chat):

  1. Erstellen Sie ein venv wie für Claude Code – .vscode/mcp.json erwartet dasselbe .venv/bin/python.

  2. Öffnen Sie den Repository-Root als Ordner in VS Code.

  3. VS Code erkennt .vscode/mcp.json und bietet an, den Server zu starten – starten Sie ihn/bestätigen Sie das Vertrauen.

  4. Prüfen Sie den Status über MCP: List Servers.

Beide Varianten setzen eine Unix-venv-Struktur voraus (.venv/bin/python); in Windows — .venv\Scripts\python.exe (nicht eingerichtet, siehe oben).

Testanfragen

Sechs Szenarien, die tatsächlich über die Claude Code CLI ausgeführt wurden, um die Integration zu bestätigen (die vollständige Tabelle mit Ergebnissen finden Sie in evidence/README.md):

  1. Suche über MCP alle Stellen, an denen die Funktion apply_discount in demo_project verwendet wird → erwartet wird search_project_files.

  2. Lies über MCP die Datei demo_app/models.py und erkläre kurz, welche Modelle dort definiert sind → erwartet wird read_project_file.

  3. Erkläre anhand der MCP-Dokumentation des Projekts, welche Sicherheitsbeschränkungen der MCP-Server hat → erwartet wird get_docs.

  4. Prüfe über das MCP-Tool, ob die Tests von demo_project bestehen → erwartet wird run_project_check.

  5. Finde unter ausschließlicher Verwendung der MCP-Tools die Implementierung von apply_discount in demo_project, lies dann die Datei, in der sie definiert ist, und erkläre ihre Parameter/Rückgabe/Rabattberechnung → erwartet wird eine Kette aus zwei Tools: search_project_files, danach read_project_file.

  6. (Negativ-/Security-Test) Versuche, über MCP die Datei ../../../../etc/passwd zu lesen → erwartet wird eine Ablehnung von read_project_file mit einer strukturierten Fehlermeldung (der Pfad liegt außerhalb des erlaubten Stammverzeichnisses).

Prüfungsergebnisse

Alle 6 von 6 Testanfragen wurden erfolgreich ausgeführt (in Test 5 beide erwarteten Tools, in der richtigen Reihenfolge; in Test 6 ist die erwartete Ablehnung der Erfolg). Jede Zeile ist sowohl durch einen echten Screenshot als auch durch eine unabhängige Zeile in evidence/tool-calls.log bestätigt. Die vollständige Tabelle finden Sie in evidence/README.md; die ausführliche Analyse mit Verweisen auf Code und Logs finden Sie in REPORT.md.

№

Tool

Ergebnis

1

search_project_files

Erfolg, 4 Treffer

2

read_project_file

Erfolg, size=397

3

get_docs

Erfolg, Abschnitt „Sicherheit“ gefunden

4

run_project_check

Erfolg, exit_code=0, 2/2 Tests bestanden

5

search_project_files → read_project_file

Erfolg, Kette aus zwei Tools

6

read_project_file

Erfolgreiche Ablehnung (Path Traversal blockiert)

Automatisierte Prüfungen (sie ersetzen nicht, sondern ergänzen das manuelle IDE-Testen oben):

  • pytest -q aus dem Repository-Stammverzeichnis — 44 passed.

  • pytest -q innerhalb von demo_project/ — 2 passed.

  • Programmatischer Stdio-Handshake (initialize() + list_tools()) — der Server meldet mcp-project-helper 0.1.0 und genau 4 Tools: get_docs, read_project_file, run_project_check, search_project_files.

Projektstruktur

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