Du kannst das Paket FastMCP dazu benutzen. Erstelle eine Datei (z.B mcpserver.py) mit folgendem Inhalt.
import os
import re
import subprocess
from typing import Optional, List
import fitz
import requests
from bs4 import BeautifulSoup
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-tools", json_response=True)
@mcp.tool()
def list_files(directory: str) -> List[str]:
"""Listet Dateien und Ordner in einem Verzeichnis."""
d = directory
items = os.listdir(d)
return sorted(items)
@mcp.tool()
def read_file(path: str, start_line: int = 1, max_lines: int = 400, tail_lines: Optional[int] = None) -> str:
"""Liest Textdatei teilweise (zeilenbasiert), um Kontext zu sparen."""
p = path
with open(p, "r", encoding="utf-8", errors="replace") as f:
lines = f.readlines()
if tail_lines and tail_lines > 0:
sel = lines[-tail_lines:]
start_idx = max(0, len(lines) - tail_lines)
else:
start_idx = max(0, start_line - 1)
sel = lines[start_idx : start_idx + max(1, max_lines)]
header = (
f"[read_file] path={p}\n"
f"[read_file] total_lines={len(lines)} selected_lines={len(sel)} "
f"range={start_idx+1}-{start_idx+len(sel)}\n\n"
)
return header + "".join(sel)
@mcp.tool()
def write_file(path: str, content: str, create_dirs: bool = True) -> str:
"""Schreibt eine Datei (überschreibt vorhandene Inhalte)."""
p = path
parent = os.path.dirname(p)
if create_dirs and parent:
os.makedirs(parent, exist_ok=True)
with open(p, "w", encoding="utf-8", errors="replace") as f:
f.write(content)
return f"[write_file] path={p} chars={len(content)}"
@mcp.tool()
def append_file(path: str, content: str, create_dirs: bool = True) -> str:
"""Hängt Text an eine Datei an (legt sie an, falls sie nicht existiert)."""
p = path
parent = os.path.dirname(p)
if create_dirs and parent:
os.makedirs(parent, exist_ok=True)
with open(p, "a", encoding="utf-8", errors="replace") as f:
f.write(content)
return f"[append_file] path={p} appended_chars={len(content)}"
@mcp.tool()
def read_pdf_text(path: str, start_page: int = 1, max_pages: int = 5, max_chars: int = 12000) -> str:
"""Extrahiert Text aus einem PDF (Text-Layer)."""
p = _abspath_safe(path)
doc = fitz.open(p)
n = doc.page_count
sp = max(1, start_page)
ep = min(n, sp + max_pages - 1)
chunks = []
for pno in range(sp - 1, ep):
page = doc.load_page(pno)
txt = page.get_text("text")
chunks.append(f"\n--- Seite {pno+1}/{n} ---\n{txt}")
out = "".join(chunks).strip()
if len(out) > max_chars:
out = out[:max_chars] + "\n…[gekürzt]"
return f"[read_pdf_text] path={p} pages={sp}-{ep}/{n}\n\n{out}"
@mcp.tool()
def fetch_html(url: str, max_chars: int = 8000) -> str:
"""Gibt sichtbaren Text einer http(s)-Seite zurück."""
if not re.match(r"^https?://", url, flags=re.IGNORECASE):
raise ValueError("Only http(s) URLs allowed")
headers = {"User-Agent": "Mozilla/5.0 (compatible; MCP-Tools/1.0)"}
resp = requests.get(url, headers=headers, timeout=25, allow_redirects=True)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
for tag in soup(["script", "style", "noscript", "template"]):
tag.decompose()
text = soup.get_text(separator="\n", strip=True)
text = re.sub(r"\n{3,}", "\n\n", text)
if len(text) > max_chars:
text = text[:max_chars] + "\n…[gekürzt]"
return text
@mcp.tool()
def execute_cmd(command: str) -> str:
"""Führt einen Shell-Befehl aus (standardmäßig deaktiviert!)."""
if not ENABLE_DANGEROUS:
return "execute_cmd ist deaktiviert. Setze MCP_ENABLE_DANGEROUS=1 wenn du das wirklich willst."
r = subprocess.run(command, shell=True, capture_output=True, text=True, encoding="utf-8", errors="replace")
return r.stdout if r.returncode == 0 else f"Fehler: {r.stderr or r.stdout}"
@mcp.tool()
def execute_powershell(command: str) -> str:
"""Führt PowerShell aus (standardmäßig deaktiviert!)."""
if not ENABLE_DANGEROUS:
return "execute_powershell ist deaktiviert. Setze MCP_ENABLE_DANGEROUS=1 wenn du das wirklich willst."
ps = ["powershell", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", command]
r = subprocess.run(ps, capture_output=True, text=True, encoding="utf-8", errors="replace")
return r.stdout if r.returncode == 0 else f"Fehler: {r.stderr or r.stdout}"
if __name__ == "__main__":
# Für LM Studio als "command server" ist stdio typischerweise am einfachsten
mcp.run(transport="stdio")
Dann öffnestdu in LM Studio einen neuen Chat und klickst auf das Zahnrad rechts oben.um die Einstellungen für diesen Chat zu bearbeiten. Wechsle zum Reiter "Program" und klicke auf "Install -> Edit mcp.json". In die JSON Datei schreibst du
{
"mcpServers": {
"my-tools": {
"command": "python",
"args": [
"C:\\pfad\\zu\\mcpserver.py"
],
"env": { }
}
}
}