Troubleshooting: Wenn's mal wieder brennt
Ich habe eine schlechte und eine gute Nachricht. Die schlechte: Es wird schiefgehen. Die gute: Mit System findest Du jeden Fehler. Wenn man sich lange genug mit Entwicklung beschäftigt, sammeln sich einige Troubleshooting-Strategien an. Damit Du das Rad nicht komplett neu erfinden musst, teile ich hier meine Erfahrungen aus vielen Jahren "Warum geht das nicht?"
Die häufigsten Probleme (und ihre Lösungen)
Problem 1: Claude findet den Server nicht
Symptome:
- Claude zeigt keine MCP Tools
- "Server not found" Meldungen
- Tools erscheinen nicht in der Liste
Die systematische Lösung:
# 1. Prüfe ob mcp.json existiert und richtig ist
cat ~/.claude/mcp.json
# Sollte so aussehen:
{
"mcpServers": {
"content-metadata": {
"command": "/var/www/mcp/content-metadata-server/venv/bin/python",
"args": ["/var/www/mcp/content-metadata-server/server.py"],
"env": {
"DATENBANK_CONFIG_DIR": "/var/www/mcp/content-metadata-server/config"
}
}
}
}
# 2. Teste ob der Server direkt startet
cd /var/www/mcp/content-metadata-server
source venv/bin/activate
python server.py
# 3. Prüfe Pfade (müssen absolut sein!)
ls -la /var/www/mcp/content-metadata-server/server.py
ls -la /var/www/mcp/content-metadata-server/venv/bin/python
# 4. Claude neu starten
# Komplett schließen und neu öffnen, nicht nur reload!
Die Pfade in mcp.json müssen ABSOLUT sein. Keine ~, keine relativen Pfade. Das vergesse ich selbst immer wieder.
Problem 2: Database Connection Failed
Der Klassiker. Hier meine Checkliste:
# debug_connection.py
#!/usr/bin/env python3
"""
Systematischer Connection-Debug
"""
import pymysql
import json
import sys
def debug_connection():
print("Database Connection Debugger\n")
print("="*40 + "\n")
# 1. Config laden
try:
with open('config/credentials.json', 'r') as f:
creds = json.load(f)
print("[OK] Credentials geladen")
except Exception as e:
print(f"[FEHLER] Kann credentials.json nicht laden: {e}")
return
# 2. MariaDB läuft?
import subprocess
result = subprocess.run(
['systemctl', 'status', 'mariadb'],
capture_output=True,
text=True
)
if 'active (running)' in result.stdout:
print("[OK] MariaDB läuft")
else:
print("[FEHLER] MariaDB läuft nicht!")
print(" Fix: sudo systemctl start mariadb")
return
# 3. Port offen?
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
result = sock.connect_ex(('127.0.0.1', 3306))
if result == 0:
print("[OK] Port 3306 erreichbar")
else:
print("[FEHLER] Port 3306 nicht erreichbar")
print(" Check: sudo netstat -tlnp | grep 3306")
return
sock.close()
# 4. User existiert?
for user_type in ['content_reader', 'content_writer']:
user_data = creds['database_users'][user_type]
try:
# Als root prüfen ob User existiert
root_conn = pymysql.connect(
host='localhost',
user='root',
password='YOUR_ROOT_PASSWORD'
)
with root_conn.cursor() as cursor:
cursor.execute(
"SELECT User FROM mysql.user WHERE User = %s",
(user_type,)
)
if cursor.fetchone():
print(f"[OK] User {user_type} existiert")
else:
print(f"[FEHLER] User {user_type} existiert nicht!")
print(f" Fix: CREATE USER '{user_type}'@'localhost' IDENTIFIED BY 'password';")
root_conn.close()
except Exception as e:
print(f"[WARNUNG] Kann User {user_type} nicht prüfen: {e}")
# 5. Connection testen
for user_type in ['content_reader', 'content_writer']:
user_data = creds['database_users'][user_type]
print(f"\nTeste {user_type}...")
for host in ['localhost', '127.0.0.1', '::1']:
try:
conn = pymysql.connect(
host=host,
user=user_type,
password=user_data['password'],
database='karlkratz_de'
)
print(f" [OK] {host}: Verbindung OK")
conn.close()
break
except pymysql.Error as e:
print(f" [FEHLER] {host}: {e.args[1]}")
print("\nZusammenfassung:")
print("="*40)
print(" Wenn immer noch Fehler: Check die Passwörter!")
print(" Tipp: Passwörter mit Sonderzeichen in Anführungszeichen")
if __name__ == "__main__":
debug_connection()
Problem 3: Rate Limit Exceeded
Zu viele Requests? So findest Du's raus:
# Check wer wie viele Requests macht
grep "Rate limit exceeded" logs/operations.log | \
awk '{print $NF}' | sort | uniq -c | sort -rn
# Rate Limits temporär erhöhen (config/security.json)
{
"rate_limiting": {
"queries_per_minute": 200, // War 50
"burst_size": 50, // War 20
"cooldown_seconds": 30 // Neu: Cooldown-Zeit
}
}
# Rate Limit Cache leeren (wenn's hakt)
redis-cli FLUSHDB # Falls Du Redis verwendest
# Oder in Python:
from modules.security import SecurityValidator
validator = SecurityValidator('./config')
validator.query_timestamps = {} # Reset
print("Rate limits zurückgesetzt")
Problem 4: Permissions Denied
Die ewige Permission-Hölle:
# permission_fixer.sh
#!/bin/bash
echo "Fixing MCP Server Permissions..."
echo "===================================="
MCP_DIR="/var/www/mcp/content-metadata-server"
# 1. Owner setzen
sudo chown -R www-data:www-data "$MCP_DIR"
echo "[OK] Owner gesetzt"
# 2. Directories
find "$MCP_DIR" -type d -exec chmod 755 {} \;
echo "[OK] Directory permissions"
# 3. Files
find "$MCP_DIR" -type f -exec chmod 644 {} \;
echo "[OK] File permissions"
# 4. Executables
chmod 755 "$MCP_DIR/server.py"
chmod 755 "$MCP_DIR/venv/bin/python"
echo "[OK] Executables"
# 5. Sensible Files
chmod 600 "$MCP_DIR/config/credentials.json"
chmod 600 "$MCP_DIR/config/database.json"
echo "[OK] Sensitive files protected"
# 6. Logs
chmod 755 "$MCP_DIR/logs"
chmod 666 "$MCP_DIR/logs/*.log" 2>/dev/null || true
echo "[OK] Log permissions"
# 7. SELinux (falls aktiv)
if command -v getenforce &> /dev/null && [ "$(getenforce)" != "Disabled" ]; then
sudo semanage fcontext -a -t httpd_sys_rw_content_t "$MCP_DIR/logs(/.*)?"
sudo restorecon -Rv "$MCP_DIR"
echo "[OK] SELinux context"
fi
echo "[OK] All permissions fixed!"
echo "===================================="
Problem 5: Memory Leaks
Server frisst immer mehr RAM? Memory Leak hunting:
# memory_profiler.py
import tracemalloc
import psutil
import gc
import time
class MemoryMonitor:
"""Memory Leak Detector"""
def __init__(self):
tracemalloc.start()
self.snapshots = []
def take_snapshot(self, label=""):
"""Snapshot vom aktuellen Memory"""
snapshot = tracemalloc.take_snapshot()
self.snapshots.append((label, snapshot))
# Process Memory Info
process = psutil.Process()
mem_info = process.memory_info()
print(f"\nMemory Snapshot: {label}")
print("-" * 40)
print(f" RSS: {mem_info.rss / 1024 / 1024:.2f} MB")
print(f" VMS: {mem_info.vms / 1024 / 1024:.2f} MB")
# Top 10 Memory-Fresser
if len(self.snapshots) > 1:
prev_snapshot = self.snapshots[-2][1]
top_stats = snapshot.compare_to(prev_snapshot, 'lineno')
print("\n Top 10 Änderungen:")
for stat in top_stats[:10]:
print(f" {stat}")
def find_leaks(self):
"""Sucht nach nicht freigegebenen Objekten"""
gc.collect()
print("\nLeak Detection:")
print("-" * 40)
# Objekte zählen
objects = gc.get_objects()
type_count = {}
for obj in objects:
obj_type = type(obj).__name__
type_count[obj_type] = type_count.get(obj_type, 0) + 1
# Top 20 Objekttypen
sorted_types = sorted(
type_count.items(),
key=lambda x: x[1],
reverse=True
)
print(" Top 20 Objekttypen:")
for obj_type, count in sorted_types[:20]:
print(f" {obj_type}: {count}")
# Connections prüfen
connections = [
obj for obj in objects
if 'connection' in str(type(obj)).lower()
]
print(f"\n Offene Connections: {len(connections)}")
return type_count
# Verwendung
monitor = MemoryMonitor()
# Baseline
monitor.take_snapshot("Start")
# Nach 100 Queries
for _ in range(100):
db.select(limit=10)
monitor.take_snapshot("Nach 100 Queries")
# Nach 1000 Queries
for _ in range(900):
db.select(limit=10)
monitor.take_snapshot("Nach 1000 Queries")
# Leak-Analyse
monitor.find_leaks()
Problem 6: Slow Queries
Queries dauern ewig? Zeit für Profiling:
# slow_query_analyzer.py
import time
import cProfile
import pstats
from io import StringIO
class QueryProfiler:
"""Findet langsame Queries"""
def __init__(self, db_manager):
self.db = db_manager
self.slow_queries = []
def profile_query(self, query_func, *args, **kwargs):
"""Profiled eine einzelne Query"""
# Zeit messen
start = time.perf_counter()
# Profiling
profiler = cProfile.Profile()
profiler.enable()
try:
result = query_func(*args, **kwargs)
finally:
profiler.disable()
end = time.perf_counter()
duration = end - start
# Wenn zu langsam, speichern
if duration > 0.1: # > 100ms
s = StringIO()
ps = pstats.Stats(profiler, stream=s).sort_stats('cumulative')
ps.print_stats(10)
self.slow_queries.append({
'query': str(query_func),
'args': args,
'duration': duration,
'profile': s.getvalue()
})
print(f"[WARNUNG] Slow Query: {duration*1000:.2f}ms")
print(f" Function: {query_func.__name__}")
return result
def analyze_slow_queries(self):
"""Analysiert alle langsamen Queries"""
if not self.slow_queries:
print("[OK] Keine langsamen Queries gefunden!")
return
print(f"\n{len(self.slow_queries)} langsame Queries gefunden:\n")
for i, sq in enumerate(self.slow_queries, 1):
print(f"{i}. Query: {sq['query']}")
print(f" Duration: {sq['duration']*1000:.2f}ms")
print(f" Profile:\n{sq['profile']}")
print("-" * 50)
# Empfehlungen
print("\nEmpfehlungen:")
print("-" * 40)
print(" 1. Index prüfen: EXPLAIN SELECT ...")
print(" 2. Query optimieren (weniger JOINs?)")
print(" 3. Caching einbauen")
print(" 4. Connection Pooling nutzen")
Der systematische Debug-Prozess
Mein bewährter 7-Schritte-Prozess:
-
Reproduzieren
# Kann ich den Fehler reproduzieren? while true; do python test_query.py if [ $? -ne 0 ]; then echo "Fehler reproduziert!" break fi sleep 1 done -
Isolieren
# Minimales Beispiel erstellen # minimal_test.py from modules.database import DatabaseManager db = DatabaseManager('./config') db.connect() # Nur die eine Zeile die knallt result = db.select(where="id = ?", params=[999999]) -
Loggen
# Logging auf DEBUG import logging logging.basicConfig(level=logging.DEBUG) # Oder gezielt logger = logging.getLogger('modules.database') logger.setLevel(logging.DEBUG) -
Hypothese
# Was könnte es sein? HYPOTHESEN = [ "Connection timeout?", "Falscher Datentyp?", "Race Condition?", "Memory Leak?", "Permission Problem?" ] for h in HYPOTHESEN: print(f"Teste: {h}") # Spezifischer Test -
Testen
# Fix anwenden und testen python -m pytest tests/test_specific_issue.py -v # Regression testen python -m pytest tests/ -v -
Dokumentieren
# known_issues.md ## Issue: Connection Timeout bei großen Queries **Symptom**: Timeout nach 30 Sekunden **Ursache**: Default read_timeout zu klein **Lösung**: read_timeout in database.json erhöhen **Datum**: 2024-01-15 **Gefixed in**: v1.2.3 -
Vorbeugen
# Test schreiben der das verhindert def test_large_query_timeout(): """Regression Test für Timeout-Issue""" # Query die vorher timeout hatte result = db.select(limit=10000) assert result is not None
Emergency Fixes
Wenn's brennt und schnell gehen muss:
# emergency_fix.sh
#!/bin/bash
echo "EMERGENCY FIX PROTOCOL"
echo "========================"
# 1. Backup JETZT
echo "1. Creating emergency backup..."
tar -czf /tmp/mcp_emergency_$(date +%s).tar.gz /var/www/mcp/
# 2. Server stoppen
echo "2. Stopping server..."
sudo systemctl stop mcp-content-metadata
# 3. Rollback zur letzten funktionierenden Version
echo "3. Rolling back..."
cd /var/www/mcp/content-metadata-server
git stash # Aktuelle Änderungen sichern
git checkout HEAD~1 # Eine Version zurück
# 4. Config zurücksetzen
echo "4. Resetting configs..."
cp config/database.json.backup config/database.json
cp config/security.json.backup config/security.json
# 5. Cache leeren
echo "5. Clearing caches..."
rm -rf __pycache__
rm -rf modules/__pycache__
find . -name "*.pyc" -delete
# 6. Server neu starten
echo "6. Restarting server..."
sudo systemctl start mcp-content-metadata
# 7. Health Check
echo "7. Health check..."
sleep 5
curl -f http://localhost:8080/health || {
echo "[FEHLER] Server still broken!"
echo "Rolling back further..."
git checkout HEAD~2
sudo systemctl restart mcp-content-metadata
}
echo "[OK] Emergency fix complete!"
echo "[WARNUNG] Remember to investigate root cause!"
Log-Analyse Tools
Logs sind Gold wert, wenn Du sie lesen kannst:
# log_analyzer.py
#!/usr/bin/env python3
"""
MCP Log Analyzer - Findet Muster in Logs
"""
import re
from collections import Counter, defaultdict
from datetime import datetime
import json
class LogAnalyzer:
def __init__(self, log_file):
self.log_file = log_file
self.errors = []
self.warnings = []
self.patterns = defaultdict(int)
def analyze(self):
"""Hauptanalyse"""
with open(self.log_file, 'r') as f:
for line in f:
self.parse_line(line)
self.print_report()
def parse_line(self, line):
"""Eine Zeile parsen"""
# JSON Logs
if line.startswith('{'):
try:
data = json.loads(line)
if data.get('level') == 'ERROR':
self.errors.append(data)
elif data.get('level') == 'WARNING':
self.warnings.append(data)
except:
pass
# Text Logs
else:
if 'ERROR' in line:
self.errors.append(line)
elif 'WARNING' in line:
self.warnings.append(line)
# Patterns suchen
patterns = [
r'timeout',
r'connection.*failed',
r'rate.*limit',
r'permission.*denied',
r'sql.*injection',
r'memory',
r'disk.*full'
]
for pattern in patterns:
if re.search(pattern, line, re.IGNORECASE):
self.patterns[pattern] += 1
def print_report(self):
"""Report ausgeben"""
print("LOG ANALYSIS REPORT")
print("=" * 50)
print(f"\nErrors: {len(self.errors)}")
if self.errors:
# Top 5 Errors
error_messages = [str(e) for e in self.errors[-5:]]
for i, err in enumerate(error_messages, 1):
print(f" {i}. {err[:100]}...")
print(f"\nWarnings: {len(self.warnings)}")
print(f"\nPattern Matches:")
for pattern, count in sorted(
self.patterns.items(),
key=lambda x: x[1],
reverse=True
):
print(f" {pattern}: {count}x")
# Zeitanalyse
print(f"\nTemporal Analysis:")
self.temporal_analysis()
def temporal_analysis(self):
"""Wann treten Fehler auf?"""
hour_counts = Counter()
for error in self.errors:
# Timestamp extrahieren
if isinstance(error, dict):
timestamp = error.get('timestamp', '')
else:
# Regex für Timestamp
match = re.search(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}', error)
timestamp = match.group() if match else ''
if timestamp:
try:
dt = datetime.fromisoformat(timestamp)
hour_counts[dt.hour] += 1
except:
pass
if hour_counts:
print(" Fehler nach Stunde:")
for hour, count in sorted(hour_counts.items()):
bar = '#' * (count // 2)
print(f" {hour:02d}:00 {bar} {count}")
# Verwendung
if __name__ == "__main__":
analyzer = LogAnalyzer('/var/log/mcp/content-metadata.log')
analyzer.analyze()
Die ultimative Troubleshooting-Checkliste
Wenn gar nichts mehr geht:
- Neustart: Seriously, hast Du's versucht?
- Logs lesen: ALLE Logs, nicht nur einen
- Configs prüfen: Tippfehler? JSON valid?
- Permissions: Linux favorite problem
- Disk voll? df -h
- RAM voll? free -h
- Port belegt? netstat -tlnp
- Firewall? ufw status
- SELinux? getenforce
- DNS? It's always DNS
- Zeit sync? date
- SSL Cert? openssl x509 -in cert.pem -noout -dates
Was ich gelernt habe
Nach tausenden Debug-Sessions:
- Der Fehler ist immer einfacher als Du denkst
- Lies die Fehlermeldung - wirklich lesen!
- Wenn Du 2 Stunden festhängst, mach Pause
- Dokumentiere jeden Fix - Du brauchst ihn wieder
- Ein gutes Logging spart Tage an Debugging
- Tests verhindern dass alte Bugs wiederkommen
Der beste Bug ist der, den Du durch gutes Design von vornherein vermeidest.
Zum Abschluss habe ich noch etwas Praktisches für Dich: Eine vollständige Code-Referenz mit allen Tools, Funktionen und Parametern. Das ist Dein Nachschlagewerk, wenn Du mal wieder vergessen hast, welche Parameter wohin gehören. Mit Quick Reference Cards und allen wichtigen Befehlen auf einen Blick. Zur vollständigen Code-Referenz und API-Dokumentation