Files
stiftung-management-system/app/gramps_mcp_server/tools/lesen.py
SysAdmin Agent 69adab2c5e Add GrampsWeb MCP server with Phase 1 read tools (STI-104)
New MCP server (app/gramps_mcp_server/) that exposes the GrampsWeb REST API
as 12 MCP tools for genealogy data access: person_suchen, person_details,
familie_details, ereignis_details, ort_suchen, ort_details, quelle_suchen,
quelle_details, stammbaum_export, stammbaum_info, medien_liste, notiz_details.

Includes HTTP client with auto-login/token management and Docker compose
services for both prod and dev environments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-05 21:26:57 +00:00

271 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Lese-Tools für den GrampsWeb MCP Server (Phase 1).
12 Tools für Lese-Zugriff auf die GrampsWeb REST API:
- person_suchen, person_details
- familie_details
- ereignis_details
- ort_suchen, ort_details
- quelle_suchen, quelle_details
- stammbaum_export, stammbaum_info
- medien_liste
- notiz_details
"""
from __future__ import annotations
import json
from base64 import b64encode
def _fmt(data) -> str:
"""Formatiert Daten als JSON-String."""
return json.dumps(data, ensure_ascii=False, indent=2, default=str)
def _client():
from gramps_mcp_server.client import get_client
return get_client()
# ──────────────────────────────────────────────────────────────────────────────
# Personen
# ──────────────────────────────────────────────────────────────────────────────
def person_suchen(
suchbegriff: str = "",
seite: int = 1,
pro_seite: int = 20,
) -> str:
"""
Sucht Personen im Stammbaum nach Name.
Args:
suchbegriff: Name oder Suchbegriff (Vor-/Nachname)
seite: Seitennummer (ab 1)
pro_seite: Ergebnisse pro Seite (max. 100)
"""
pro_seite = min(pro_seite, 100)
client = _client()
if suchbegriff:
results = client.get(
"/api/search/",
params={
"query": suchbegriff,
"page": seite,
"pagesize": pro_seite,
},
)
else:
results = client.get(
"/api/people/",
params={
"page": seite,
"pagesize": pro_seite,
"sort": "surname",
},
)
return _fmt({"anzahl": len(results) if isinstance(results, list) else 0, "ergebnisse": results})
def person_details(handle: str) -> str:
"""
Gibt vollständige Details einer Person zurück inkl. Ereignisse, Familien, Medien.
Args:
handle: GrampsWeb-Handle der Person (z.B. aus person_suchen)
"""
client = _client()
person = client.get(f"/api/people/{handle}", params={"extend": "all", "profile": "all"})
return _fmt(person)
# ──────────────────────────────────────────────────────────────────────────────
# Familien
# ──────────────────────────────────────────────────────────────────────────────
def familie_details(handle: str) -> str:
"""
Gibt Details einer Familie zurück (Eltern, Kinder, Ereignisse).
Args:
handle: GrampsWeb-Handle der Familie
"""
client = _client()
family = client.get(f"/api/families/{handle}", params={"extend": "all", "profile": "all"})
return _fmt(family)
# ──────────────────────────────────────────────────────────────────────────────
# Ereignisse
# ──────────────────────────────────────────────────────────────────────────────
def ereignis_details(handle: str) -> str:
"""
Gibt Details eines Ereignisses zurück (Geburt, Tod, Heirat, etc.).
Args:
handle: GrampsWeb-Handle des Ereignisses
"""
client = _client()
event = client.get(f"/api/events/{handle}", params={"extend": "all", "profile": "all"})
return _fmt(event)
# ──────────────────────────────────────────────────────────────────────────────
# Orte
# ──────────────────────────────────────────────────────────────────────────────
def ort_suchen(
suchbegriff: str = "",
seite: int = 1,
pro_seite: int = 20,
) -> str:
"""
Sucht Orte im Stammbaum.
Args:
suchbegriff: Ortsname oder Suchbegriff
seite: Seitennummer (ab 1)
pro_seite: Ergebnisse pro Seite (max. 100)
"""
pro_seite = min(pro_seite, 100)
client = _client()
params = {"page": seite, "pagesize": pro_seite}
if suchbegriff:
params["q"] = suchbegriff
results = client.get("/api/places/", params=params)
return _fmt({"anzahl": len(results) if isinstance(results, list) else 0, "orte": results})
def ort_details(handle: str) -> str:
"""
Gibt Details eines Ortes zurück.
Args:
handle: GrampsWeb-Handle des Ortes
"""
client = _client()
place = client.get(f"/api/places/{handle}", params={"extend": "all", "profile": "all"})
return _fmt(place)
# ──────────────────────────────────────────────────────────────────────────────
# Quellen
# ──────────────────────────────────────────────────────────────────────────────
def quelle_suchen(
suchbegriff: str = "",
seite: int = 1,
pro_seite: int = 20,
) -> str:
"""
Sucht Quellen (Kirchenbücher, Urkunden, etc.) im Stammbaum.
Args:
suchbegriff: Quellenname oder Suchbegriff
seite: Seitennummer (ab 1)
pro_seite: Ergebnisse pro Seite (max. 100)
"""
pro_seite = min(pro_seite, 100)
client = _client()
params = {"page": seite, "pagesize": pro_seite}
if suchbegriff:
params["q"] = suchbegriff
results = client.get("/api/sources/", params=params)
return _fmt({"anzahl": len(results) if isinstance(results, list) else 0, "quellen": results})
def quelle_details(handle: str) -> str:
"""
Gibt Details einer Quelle zurück inkl. Zitierungen.
Args:
handle: GrampsWeb-Handle der Quelle
"""
client = _client()
source = client.get(f"/api/sources/{handle}", params={"extend": "all", "profile": "all"})
return _fmt(source)
# ──────────────────────────────────────────────────────────────────────────────
# Stammbaum-Export & Info
# ──────────────────────────────────────────────────────────────────────────────
def stammbaum_export(
format: str = "gedcom",
) -> str:
"""
Exportiert den Stammbaum als GEDCOM oder Gramps-XML.
Args:
format: Export-Format 'gedcom' oder 'gramps' (Gramps-XML)
Gibt den Export als Base64-kodierten String zurück.
"""
allowed = {"gedcom", "gramps"}
if format not in allowed:
return _fmt({"fehler": f"Ungültiges Format '{format}'. Erlaubt: {', '.join(allowed)}"})
client = _client()
# GrampsWeb exporters endpoint
ext = "ged" if format == "gedcom" else "gramps"
data = client.get_raw(f"/api/exporters/{ext}/file")
encoded = b64encode(data).decode("ascii")
return _fmt({
"format": format,
"dateiname": f"stammbaum.{ext}",
"groesse_bytes": len(data),
"inhalt_base64": encoded[:200] + "..." if len(encoded) > 200 else encoded,
"hinweis": "Vollständiger Export als Base64. Bei großen Dateien ggf. abgeschnitten in der Anzeige.",
})
def stammbaum_info() -> str:
"""
Gibt Metadaten und Statistiken des Stammbaums zurück
(Anzahl Personen, Familien, Orte, etc.).
"""
client = _client()
metadata = client.get("/api/metadata/")
return _fmt(metadata)
# ──────────────────────────────────────────────────────────────────────────────
# Medien
# ──────────────────────────────────────────────────────────────────────────────
def medien_liste(
seite: int = 1,
pro_seite: int = 20,
) -> str:
"""
Listet Medienobjekte (Fotos, Dokumente, Scans) im Stammbaum auf.
Args:
seite: Seitennummer (ab 1)
pro_seite: Ergebnisse pro Seite (max. 50)
"""
pro_seite = min(pro_seite, 50)
client = _client()
results = client.get("/api/media/", params={"page": seite, "pagesize": pro_seite})
return _fmt({"anzahl": len(results) if isinstance(results, list) else 0, "medien": results})
# ──────────────────────────────────────────────────────────────────────────────
# Notizen
# ──────────────────────────────────────────────────────────────────────────────
def notiz_details(handle: str) -> str:
"""
Gibt den Inhalt einer Notiz zurück.
Args:
handle: GrampsWeb-Handle der Notiz
"""
client = _client()
note = client.get(f"/api/notes/{handle}")
return _fmt(note)