{
"tool_response": [
{
"type": "text",
"text": "{\n \"success\": true,\n \"doc\": {\n \"id\": 156,\n \"parent_id\": 91,\n \"slug\": \"mcp-server-refactoring-plan\",\n \"path\": \"\/prozesse\/mcp-server-refactoring-plan\",\n \"title\": \"MCP Server Refactoring Plan\",\n \"description\": \"Vollständiger Refactoring-Plan für alle 5 MCP-Server nach DRY, SRP, SOLID, Clean Architecture\",\n \"content\": \"<h1>MCP Server Refactoring Plan<\/h1>\\n\\n<p><strong>Version:<\/strong> 1.2<br>\\n<strong>Erstellt:<\/strong> 2025-12-28<br>\\n<strong>Aktualisiert:<\/strong> 2025-12-28 (nach Supervision Runde 2)<br>\\n<strong>Status:<\/strong> Final<br>\\n<strong>Task:<\/strong> #507<\/p>\\n\\n<hr>\\n\\n<h2>1. Executive Summary<\/h2>\\n\\n<h3>Ist-Zustand<\/h3>\\n<ul>\\n<li><strong>5 MCP-Server<\/strong>: mcp-db, mcp-tasks, mcp-contracts, mcp-docs, mcp-code<\/li>\\n<li><strong>91 Python-Dateien<\/strong> (ohne venv)<\/li>\\n<li><strong>9.248 LOC<\/strong> gesamt<\/li>\\n<li><strong>Signifikante Code-Duplikation<\/strong> (~25% redundanter Code)<\/li>\\n<li><strong>Inkonsistente Patterns<\/strong> zwischen Servern<\/li>\\n<\/ul>\\n\\n<h3>Ziel<\/h3>\\n<p>Konsolidierung zu einer modularen, wartbaren Architektur unter Einhaltung von:<\/p>\\n<ul>\\n<li><strong>DRY<\/strong> (Don't Repeat Yourself)<\/li>\\n<li><strong>SRP<\/strong> (Single Responsibility Principle)<\/li>\\n<li><strong>SOLID<\/strong> Principles<\/li>\\n<li><strong>Clean Architecture<\/strong><\/li>\\n<li><strong>KISS<\/strong> (Keep It Simple, Stupid)<\/li>\\n<li><strong>YAGNI<\/strong> (You Aren't Gonna Need It)<\/li>\\n<\/ul>\\n\\n<h3>Erwartete Verbesserungen<\/h3>\\n<ul>\\n<li><strong>~30% weniger Code<\/strong> durch Elimination von Duplikaten<\/li>\\n<li><strong>Einheitliche Patterns<\/strong> für alle Server<\/li>\\n<li><strong>Verbesserte Wartbarkeit<\/strong> durch Modularisierung<\/li>\\n<li><strong>Schnellere Feature-Entwicklung<\/strong> durch shared Components<\/li>\\n<\/ul>\\n\\n<hr>\\n\\n<h2>2. Analyse der Code-Duplikation<\/h2>\\n\\n<h3>2.1 Kritische Duplikate (Sofort beheben)<\/h3>\\n\\n<h4>db_connection.py (4 Dateien, ~240 LOC dupliziert)<\/h4>\\n\\n<table>\\n<tr><th>Server<\/th><th>Zeilen<\/th><th>Bibliothek<\/th><th>Besonderheit<\/th><\/tr>\\n<tr><td>mcp-db<\/td><td>59<\/td><td>mysql.connector + <strong>Pooling<\/strong><\/td><td>Dynamisches DB-Switching via <code>USE<\/code><\/td><\/tr>\\n<tr><td>mcp-tasks<\/td><td>62<\/td><td>pymysql<\/td><td>Festes <code>DB_NAME<\/code><\/td><\/tr>\\n<tr><td>mcp-contracts<\/td><td>60<\/td><td>pymysql<\/td><td>Festes <code>DB_NAME<\/code><\/td><\/tr>\\n<tr><td>mcp-docs<\/td><td>60<\/td><td>pymysql<\/td><td>Festes <code>DB_NAME<\/code><\/td><\/tr>\\n<\/table>\\n\\n<p><strong>Problem:<\/strong> 3 von 4 Dateien sind nahezu identisch (mcp-tasks, mcp-contracts, mcp-docs).<\/p>\\n\\n<p><strong>Lösung:<\/strong> Zentralisieren in <code>shared\/infrastructure\/simple_db_connection.py<\/code><\/p>\\n\\n<blockquote>\\n<p><strong>WICHTIG:<\/strong> Diese Klasse heißt bewusst <code>SimpleDbConnection<\/code>, nicht <code>DatabaseConnection<\/code>. Sie bietet <strong>kein Pooling<\/strong> und ist <strong>nicht für High-Throughput<\/strong> geeignet. Für mcp-db (100+ Queries\/Minute) bleibt die eigene Pooling-Implementierung.<\/p>\\n<\/blockquote>\\n\\n<pre><code class=\\\"language-python\\\"># shared\/infrastructure\/simple_db_connection.py\\n\\\"\\\"\\\"\\nEinfache DB-Verbindung ohne Pooling.\\n\\nNICHT geeignet für:\\n- High-Throughput (> 50 Queries\/Minute)\\n- Connection Pooling Requirements\\n- mcp-db Server\\n\\nFür diese Fälle: Eigene Implementierung mit mysql.connector.pooling\\n\\\"\\\"\\\"\\nfrom contextlib import contextmanager\\nfrom typing import Generator\\n\\nimport pymysql\\nfrom pymysql.connections import Connection\\n\\nfrom shared.config_base import AppDatabaseConfig, LogDatabaseConfig\\n\\n\\nclass SimpleDbConnection:\\n \\\"\\\"\\\"\\n Einfache Datenbankverbindung ohne Pooling.\\n \\n Für Server mit geringem Query-Volumen (< 50\/Minute).\\n \\\"\\\"\\\"\\n \\n @classmethod\\n @contextmanager\\n def get_connection(\\n cls, \\n config: AppDatabaseConfig,\\n database: str | None = None,\\n autocommit: bool = False\\n ) -> Generator[Connection, None, None]:\\n \\\"\\\"\\\"\\n Context Manager für App-DB Connection.\\n \\n ACHTUNG: Keine weiteren Parameter hinzufügen!\\n Bei Bedarf für mehr Logik: Neue Methode erstellen.\\n \\n Args:\\n config: Server-Konfiguration mit DB-Credentials\\n database: Optional - überschreibt config.DB_NAME\\n autocommit: True für Logging, False für Transaktionen\\n \\\"\\\"\\\"\\n conn = None\\n db_name = database or getattr(config, 'DB_NAME', None)\\n \\n try:\\n conn = pymysql.connect(\\n host=config.DB_HOST,\\n port=getattr(config, 'DB_PORT', 3306),\\n user=config.DB_USER,\\n password=config.DB_PASSWORD,\\n database=db_name,\\n charset=\\\"utf8mb4\\\",\\n cursorclass=pymysql.cursors.DictCursor,\\n autocommit=autocommit,\\n )\\n yield conn\\n if not autocommit:\\n conn.commit()\\n except Exception:\\n if conn and not autocommit:\\n conn.rollback()\\n raise\\n finally:\\n if conn:\\n conn.close()\\n \\n @classmethod\\n @contextmanager\\n def get_log_connection(\\n cls, \\n config: LogDatabaseConfig\\n ) -> Generator[Connection, None, None]:\\n \\\"\\\"\\\"Separate Verbindung für Logging (autocommit=True).\\\"\\\"\\\"\\n conn = None\\n try:\\n conn = pymysql.connect(\\n host=config.LOG_DB_HOST,\\n user=config.LOG_DB_USER,\\n password=config.LOG_DB_PASSWORD,\\n database=config.LOG_DB_NAME,\\n charset=\\\"utf8mb4\\\",\\n cursorclass=pymysql.cursors.DictCursor,\\n autocommit=True,\\n )\\n yield conn\\n finally:\\n if conn:\\n conn.close()<\/code><\/pre>\\n\\n<h4>protokoll_logger.py (4 Dateien, ~240 LOC dupliziert)<\/h4>\\n\\n<p><strong>Lösung:<\/strong> Zentralisieren mit strikter Clean Architecture Trennung<\/p>\\n\\n<pre><code class=\\\"language-python\\\"># shared\/domain\/log_entry.py\\n\\\"\\\"\\\"\\nDomain Entity für Log-Einträge.\\n\\nREINE DOMAIN - keine Infrastructure-Abhängigkeiten!\\n\\\"\\\"\\\"\\nfrom dataclasses import dataclass\\nfrom datetime import datetime\\nfrom typing import Optional\\n\\n\\n@dataclass(frozen=True)\\nclass LogEntry:\\n \\\"\\\"\\\"Standardisierter Log-Eintrag für alle MCP-Server.\\\"\\\"\\\"\\n timestamp: datetime\\n client_name: str\\n request: str\\n status: str\\n duration_ms: int\\n error_message: Optional[str] = None\\n tool_name: Optional[str] = None\\n context_id: Optional[int] = None # task_id, contract_id, etc.<\/code><\/pre>\\n\\n<pre><code class=\\\"language-python\\\"># shared\/infrastructure\/protokoll_logger.py\\n\\\"\\\"\\\"\\nInfrastructure Logger für mcp_log Tabelle.\\n\\nImportiert LogEntry aus Domain - definiert ihn NICHT selbst.\\n\\\"\\\"\\\"\\nimport logging\\nimport sys\\nfrom typing import TYPE_CHECKING\\n\\nfrom shared.domain.log_entry import LogEntry # Import aus Domain!\\nfrom shared.infrastructure.simple_db_connection import SimpleDbConnection\\n\\nif TYPE_CHECKING:\\n from shared.config_base import LogDatabaseConfig\\n\\n\\nclass ProtokollLogger:\\n \\\"\\\"\\\"Fail-Safe Logger für mcp_log Tabelle.\\\"\\\"\\\"\\n \\n def __init__(self, client_name: str, config: \\\"LogDatabaseConfig\\\"):\\n self.client_name = client_name\\n self.config = config\\n self._logger = logging.getLogger(f\\\"mcp.{client_name}\\\")\\n \\n def log(self, entry: LogEntry) -> None:\\n \\\"\\\"\\\"Schreibt Log-Eintrag. Fehler gehen nur zu stderr.\\\"\\\"\\\"\\n try:\\n with SimpleDbConnection.get_log_connection(self.config) as conn:\\n with conn.cursor() as cursor:\\n request_str = self._format_request(entry)\\n cursor.execute(\\n \\\"\\\"\\\"INSERT INTO mcp_log\\n (timestamp, client_name, request, status, duration_ms, error_message)\\n VALUES (%s, %s, %s, %s, %s, %s)\\\"\\\"\\\",\\n (\\n entry.timestamp,\\n self.client_name,\\n request_str[:500],\\n entry.status,\\n entry.duration_ms,\\n entry.error_message[:500] if entry.error_message else None,\\n )\\n )\\n except Exception as e:\\n print(f\\\"CRITICAL: {self.client_name} log failed: {e}\\\", file=sys.stderr)\\n \\n def _format_request(self, entry: LogEntry) -> str:\\n parts = []\\n if entry.tool_name:\\n parts.append(f\\\"[{entry.tool_name}]\\\")\\n if entry.context_id:\\n parts.append(f\\\"id={entry.context_id}\\\")\\n parts.append(entry.request[:400] if entry.request else \\\"\\\")\\n return \\\" \\\".join(parts)\\n\\n\\n# === Registry mit Test-Support ===\\n\\n_logger_instances: dict[str, ProtokollLogger] = {}\\n\\n\\ndef get_logger(client_name: str, config: \\\"LogDatabaseConfig\\\") -> ProtokollLogger:\\n \\\"\\\"\\\"Singleton-Factory für Logger.\\\"\\\"\\\"\\n if client_name not in _logger_instances:\\n _logger_instances[client_name] = ProtokollLogger(client_name, config)\\n return _logger_instances[client_name]\\n\\n\\ndef clear_logger_registry() -> None:\\n \\\"\\\"\\\"\\n Setzt die Logger-Registry zurück.\\n \\n NUR FÜR TESTS! Ermöglicht isolierte Test-Ausführung.\\n \\\"\\\"\\\"\\n _logger_instances.clear()<\/code><\/pre>\\n\\n<h4>config.py - Strikte Statische Konfiguration<\/h4>\\n\\n<pre><code class=\\\"language-python\\\"># shared\/config_base.py\\n\\\"\\\"\\\"\\nBasis-Konfiguration für alle MCP-Server.\\n\\nWICHTIG: Diese Klasse wird NICHT instanziiert!\\nAlle Zugriffe erfolgen über Klassenattribute.\\n\\nFalsch: config = BaseConfig()\\nRichtig: host = BaseConfig.DB_HOST\\n\\\"\\\"\\\"\\nimport os\\nfrom typing impo... [TRUNCATED-b765b95ea2ac14c8]"
}
]
}