v4.1.0: DMS email documents, category-specific Nachweis linking, version system
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy (push) Has been cancelled
Code Quality / quality (push) Has been cancelled

- Save cover email body as DMS document with new 'email' context type
- Show email body separately from attachments in email detail view
- Add per-category DMS document assignment in quarterly confirmation
  (Studiennachweis, Einkommenssituation, Vermögenssituation)
- Add VERSION file and context processor for automatic version display
- Add MCP server, agent system, import/export, and new migrations
- Update compose files and production environment template

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
SysAdmin Agent
2026-03-15 18:48:52 +00:00
parent faeb7c1073
commit e0b377014c
49 changed files with 5913 additions and 55 deletions

151
app/mcp_server/server.py Normal file
View File

@@ -0,0 +1,151 @@
"""
MCP Server für die Stiftungsverwaltung.
Startmodus:
python -m mcp_server.server
Konfiguration über Umgebungsvariablen:
MCP_AUTH_TOKEN Aktiver Zugriffstoken (vom MCP-Client gesetzt)
MCP_TOKEN_READONLY Token für readonly-Rolle
MCP_TOKEN_EDITOR Token für editor-Rolle
MCP_TOKEN_ADMIN Token für admin-Rolle
DJANGO_SETTINGS_MODULE Django-Settings (Standard: core.settings)
DB_HOST, DB_PORT, POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD DB-Verbindung
"""
import logging
import os
import sys
# ──────────────────────────────────────────────────────────────────────────────
# Django Standalone-Setup (ORM ohne HTTP-Server)
# ──────────────────────────────────────────────────────────────────────────────
# Pfad zum app/-Verzeichnis in sys.path aufnehmen (damit Imports funktionieren)
_app_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if _app_dir not in sys.path:
sys.path.insert(0, _app_dir)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings")
import django # noqa: E402
django.setup()
# ──────────────────────────────────────────────────────────────────────────────
# Logging
# ──────────────────────────────────────────────────────────────────────────────
logging.basicConfig(
level=logging.WARNING,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
stream=sys.stderr,
)
logger = logging.getLogger("mcp_server")
# ──────────────────────────────────────────────────────────────────────────────
# Auth-Check vor Server-Start
# ──────────────────────────────────────────────────────────────────────────────
from mcp_server.auth import get_current_role, require_role # noqa: E402
_current_role = get_current_role()
try:
require_role(_current_role)
except ValueError as exc:
logger.error("MCP Auth-Fehler: %s", exc)
sys.exit(1)
logger.info("MCP Server startet mit Rolle: %s", _current_role)
# ──────────────────────────────────────────────────────────────────────────────
# MCP Server Initialisierung
# ──────────────────────────────────────────────────────────────────────────────
from mcp.server.fastmcp import FastMCP # noqa: E402
mcp = FastMCP(
"Stiftungsverwaltung",
instructions=(
"MCP-Server der gemeinnützigen Familienstiftung. "
f"Aktive Rolle: {_current_role}. "
"Lese-Zugriff auf alle Stiftungsdaten. "
+ ("Schreib-Zugriff aktiv. " if _current_role in ("editor", "admin") else "")
+ "PII-Felder werden bei readonly/editor maskiert."
),
)
# ──────────────────────────────────────────────────────────────────────────────
# Lese-Tools registrieren (alle Rollen)
# ──────────────────────────────────────────────────────────────────────────────
from mcp_server.tools.lesen import ( # noqa: E402
dashboard,
destinataer_details,
destinataer_suchen,
dokument_details,
dokument_suchen,
globale_suche,
konten_uebersicht,
land_details,
land_suchen,
paechter_suchen,
statistiken,
termine_anzeigen,
transaktionen_suchen,
verwaltungskosten,
)
mcp.tool()(destinataer_suchen)
mcp.tool()(destinataer_details)
mcp.tool()(land_suchen)
mcp.tool()(land_details)
mcp.tool()(paechter_suchen)
mcp.tool()(konten_uebersicht)
mcp.tool()(verwaltungskosten)
mcp.tool()(transaktionen_suchen)
mcp.tool()(dokument_suchen)
mcp.tool()(dokument_details)
mcp.tool()(termine_anzeigen)
mcp.tool()(globale_suche)
mcp.tool()(dashboard)
mcp.tool()(statistiken)
# ──────────────────────────────────────────────────────────────────────────────
# Schreib-Tools registrieren (nur editor/admin)
# ──────────────────────────────────────────────────────────────────────────────
from mcp_server.auth import can_write # noqa: E402
if can_write(_current_role):
from mcp_server.tools.schreiben import ( # noqa: E402
destinataer_aktualisieren,
destinataer_anlegen,
dokument_verknuepfen,
foerderung_anlegen,
land_anlegen,
paechter_anlegen,
termin_anlegen,
unterstuetzung_anlegen,
verpachtung_anlegen,
verwaltungskosten_erfassen,
)
mcp.tool()(destinataer_anlegen)
mcp.tool()(destinataer_aktualisieren)
mcp.tool()(foerderung_anlegen)
mcp.tool()(unterstuetzung_anlegen)
mcp.tool()(land_anlegen)
mcp.tool()(verpachtung_anlegen)
mcp.tool()(paechter_anlegen)
mcp.tool()(verwaltungskosten_erfassen)
mcp.tool()(termin_anlegen)
mcp.tool()(dokument_verknuepfen)
# ──────────────────────────────────────────────────────────────────────────────
# Server starten
# ──────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
mcp.run(transport="stdio")