KIMCPPythonAutomatisierungClaude

MCP-Server mit Python: Wie ich Claude mit lokalen Tools verbunden habe

Ein Wochenendprojekt mit Lernkurve

Seit ein paar Monaten arbeite ich intensiv mit Claude – erst über die Web-Oberfläche, dann mit Claude Code. Irgendwann wollte ich verstehen, wie das unter der Haube funktioniert. Was ist dieses Model Context Protocol, von dem alle reden? Ein Wochenende später hatte ich meinen eigenen MCP-Server am Laufen. Überraschung: Gar nicht so kompliziert.

Stolpersteine am Anfang

Zwei Dinge haben mich anfangs ausgebremst:

stdio vs. HTTP: MCP-Server können über Standard-Input/Output kommunizieren oder als Web-Service laufen. Die meisten Beispiele zeigen HTTP, aber Claude Desktop will stdio. Simpler, aber man muss es erstmal durchschauen.

Claude Desktop unter Linux: Offiziell gibt es das nur für Mac und Windows. Für Linux braucht man einen Workaround – bei mir läuft es in einer Electron-Umgebung. Nicht schön, aber läuft.

Sobald das klar war, ging der Rest flott.

Was MCP macht

Die Idee:

Claude Desktop ←→ MCP Client ←→ MCP Server ←→ Lokale Tools

Der MCP-Server stellt Tools bereit (Dateien lesen, Befehle ausführen, was auch immer). Claude ruft sie auf. Alles läuft lokal – nichts geht in die Cloud.

Mein erster Server

Voraussetzungen: Python 3.10+ und das mcp Package (pip install mcp).

# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server

mcp = Server("my-tools")

@mcp.tool()
def hello(name: str) -> str:
    """Begrüßt eine Person."""
    return f"Hallo, {name}!"

async def main():
    async with stdio_server() as (read, write):
        await mcp.run(read, write)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

Die Registrierung in Claude Desktop erfolgt über ~/.config/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "my-tools": {
      "command": "python",
      "args": ["/pfad/zu/server.py"]
    }
  }
}

Nach einem Neustart von Claude Desktop taucht das Tool auf. Claude bitten, jemanden zu begrüßen – funktioniert.

Die Tools, die ich tatsächlich nutze

Dateien lesen und schreiben

from pathlib import Path

WORKSPACE = Path.home() / "Workspace"

@mcp.tool()
def read_file(path: str) -> str:
    """Liest eine Datei aus dem Workspace."""
    full_path = WORKSPACE / path
    if not full_path.exists():
        return f"Fehler: {path} nicht gefunden"
    return full_path.read_text()

@mcp.tool()
def write_file(path: str, content: str) -> str:
    """Schreibt Inhalt in eine Datei."""
    full_path = WORKSPACE / path
    full_path.parent.mkdir(parents=True, exist_ok=True)
    full_path.write_text(content)
    return f"Geschrieben: {path}"

Shell-Befehle

import subprocess

@mcp.tool()
def shell(command: str, timeout: int = 30) -> str:
    """Führt ein Shell-Kommando aus."""
    try:
        result = subprocess.run(
            command,
            shell=True,
            capture_output=True,
            text=True,
            timeout=timeout,
            cwd=WORKSPACE
        )
        output = result.stdout
        if result.stderr:
            output += f"\n[stderr]\n{result.stderr}"
        return output or "(keine Ausgabe)"
    except subprocess.TimeoutExpired:
        return f"Fehler: Timeout nach {timeout}s"

Verzeichnisse auflisten

@mcp.tool()
def list_dir(path: str = ".", pattern: str = "*") -> str:
    """Listet Dateien in einem Verzeichnis."""
    full_path = WORKSPACE / path
    if not full_path.is_dir():
        return f"Fehler: {path} ist kein Verzeichnis"
    
    files = sorted(full_path.glob(pattern))
    return "\n".join(f.name for f in files[:50])

Sicherheit – nicht vergessen

Ein MCP-Server mit Shell-Zugriff kann alles, was der Benutzer kann. Ein paar Vorsichtsmaßnahmen:

ALLOWED_PATHS = [
    Path.home() / "Workspace",
    Path.home() / "Documents",
]

def is_allowed(path: Path) -> bool:
    resolved = path.resolve()
    return any(
        resolved.is_relative_to(allowed) 
        for allowed in ALLOWED_PATHS
    )

Außerdem: Timeouts für alle Befehle. Und Logging – man will wissen, was passiert ist, wenn was schiefgeht.

Was ich damit mache

Der Server läuft bei mir täglich:

  • “Zeig mir alle Python-Dateien mit TODO-Kommentaren” – Claude führt grep aus und fasst zusammen
  • “Führe die Tests aus und erklär mir die Fehler” – Claude sieht die echte pytest-Ausgabe
  • “Erstelle eine README für dieses Projekt” – Claude liest die Struktur und schreibt die Datei

Der Unterschied zu vorher: Kein Copy-Paste mehr. Claude arbeitet direkt im Dateisystem.

Erkenntnisse

  • Gute Docstrings – Claude nutzt sie, um zu verstehen, was ein Tool macht
  • Kleine, spezialisierte Tools schlagen große generische
  • Fehler abfangen – eine unbehandelte Exception bricht die Kommunikation ab

Weiterführendes


Disclaimer: Der Code hier ist ein Proof of Concept. Wer einen MCP-Server mit Shell-Zugriff betreibt, sollte wissen, was er tut.

GitHub: cuber-it/mcp_shell_tools


Aktualisiert: 2025-01-06

Hinweis: Dieses Projekt nutze ich täglich und entwickle es entsprechend weiter. Der Stand auf GitHub kann daher schon etwas weiter sein als hier beschrieben.