Skip to main content
Glama

Joplin MCP Server

by dweigend
test.py11 kB
"""CLI für die Joplin API mit interaktivem Menü.""" from datetime import datetime from pathlib import Path from typing import Optional import typer from rich.console import Console from rich.markdown import Markdown from rich.panel import Panel from rich.prompt import Confirm, Prompt, IntPrompt from rich.table import Table from joplin_api import JoplinAPI, JoplinNote, OrderDirection from joplin_utils import get_token_from_env, read_markdown_file # Rich Console Setup console = Console() app = typer.Typer() MENU_OPTIONS = { "1": ("📝 Notizen auflisten", "list_notes"), "2": ("✨ Neue Notiz erstellen", "create_note"), "3": ("👀 Notiz anzeigen", "view_note"), "4": ("✏️ Notiz bearbeiten", "update_note"), "5": ("🗑️ Notiz löschen", "delete_note"), "6": ("🔍 Notizen durchsuchen", "search_notes"), "0": ("👋 Beenden", "exit") } def show_menu() -> str: """Zeigt das Hauptmenü an und gibt die Auswahl zurück.""" console.print("\n[bold blue]🗒️ Joplin Notizen Manager[/]\n") for key, (label, _) in MENU_OPTIONS.items(): console.print(f"[bold cyan]{key}[/] - {label}") choice = Prompt.ask( "\n[bold green]Wähle eine Option[/]", choices=list(MENU_OPTIONS.keys()) ) return MENU_OPTIONS[choice][1] def get_api() -> JoplinAPI: """Initialize and return the Joplin API client. Returns: JoplinAPI instance Raises: typer.Exit: If initialization fails """ try: token = get_token_from_env() return JoplinAPI(token=token) except Exception as e: console.print(f"[red]❌ Fehler beim Initialisieren der API: {e}[/]") raise typer.Exit(1) def create_notes_table(notes: list[JoplinNote]) -> Table: """Erstelle eine formatierte Tabelle für Notizen.""" table = Table(show_header=True, header_style="bold magenta") table.add_column("📌 ID", style="dim") table.add_column("📝 Titel", style="bold") table.add_column("📅 Erstellt", justify="right") table.add_column("🔄 Aktualisiert", justify="right") table.add_column("✅ Todo", justify="center") for note in notes: table.add_row( note.id, note.title, format_date(note.created_time), format_date(note.updated_time), "✓" if note.is_todo else "" ) return table def format_date(dt: datetime | None) -> str: """Formatiere ein Datum für die Anzeige. Args: dt: Das zu formatierende Datum oder None Returns: Formatiertes Datum oder 'N/A' wenn None """ return dt.strftime("%Y-%m-%d %H:%M") if dt else "N/A" def list_notes(): """Liste alle Notizen auf.""" api = get_api() page = IntPrompt.ask("📄 Welche Seite möchtest du sehen?", default=1) limit = IntPrompt.ask("📊 Wie viele Notizen pro Seite?", default=10) ascending = Confirm.ask("⬆️ Aufsteigend sortieren?", default=False) try: with console.status("[bold green]📂 Lade Notizen..."): response = api.get_notes( page=page, limit=limit, order_by="updated_time", order_dir=OrderDirection.ASC if ascending else OrderDirection.DESC ) if not response.items: console.print("[yellow]📭 Keine Notizen gefunden.[/]") return table = create_notes_table(response.items) console.print(table) if response.has_more: console.print("\n[blue]📚 Es gibt weitere Notizen. Nutze eine höhere Seitenzahl für mehr.[/]") except Exception as e: console.print(f"[red]❌ Fehler beim Laden der Notizen: {e}[/]") def create_note(): """Erstelle eine neue Notiz.""" api = get_api() try: title = Prompt.ask("📝 Titel der Notiz") is_todo = Confirm.ask("✅ Als Todo markieren?", default=False) console.print("\n📝 Inhalt (STRG+D zum Beenden):") lines = [] try: while True: line = input() lines.append(line) except EOFError: body = "\n".join(lines) with console.status("[bold green]✨ Erstelle Notiz..."): note = api.create_note(title=title, body=body, is_todo=is_todo) console.print("\n[green]✅ Notiz erfolgreich erstellt:[/]") console.print(Panel( f"[bold]📝 Titel:[/] {note.title}\n" f"[bold]🔑 ID:[/] {note.id}\n" f"[bold]📅 Erstellt:[/] {format_date(note.created_time)}\n" f"[bold]✅ Todo:[/] {'Ja' if note.is_todo else 'Nein'}", title="✨ Neue Notiz", border_style="green" )) except Exception as e: console.print(f"[red]❌ Fehler beim Erstellen der Notiz: {e}[/]") def view_note(): """Zeige eine spezifische Notiz an.""" api = get_api() note_id = Prompt.ask("🔑 Gib die ID der Notiz ein") raw = Confirm.ask("📄 Markdown-Inhalt ohne Formatierung anzeigen?", default=False) try: with console.status("[bold green]📂 Lade Notiz..."): note = api.get_note(note_id) # Debug-Informationen console.print("\n[dim]🔍 Debug: API-Antwort:[/]") console.print(f"[dim]- Type von body: {type(note.body)}[/]") console.print(f"[dim]- Länge von body: {len(note.body) if note.body else 0}[/]") if note.body: console.print(f"[dim]- Erste 50 Zeichen: {note.body[:50]}...[/]") console.print(Panel( f"[bold]📝 Titel:[/] {note.title}\n" f"[bold]🔑 ID:[/] {note.id}\n" f"[bold]📅 Erstellt:[/] {format_date(note.created_time)}\n" f"[bold]🔄 Aktualisiert:[/] {format_date(note.updated_time)}\n" f"[bold]✅ Todo:[/] {'Ja' if note.is_todo else 'Nein'}", title="📋 Notizdetails", border_style="blue" )) if note.body: if raw: console.print("\n[bold]📄 Inhalt:[/]") console.print(note.body) else: console.print("\n[bold]📝 Formatierter Inhalt:[/]") console.print(Markdown(note.body)) else: console.print("\n[yellow]📭 Diese Notiz hat keinen Inhalt.[/]") except Exception as e: console.print(f"[red]❌ Fehler beim Laden der Notiz: {e}[/]") # Bei Fehlern mehr Details anzeigen console.print(f"[dim]🔍 Debug: Exception Details:[/]") import traceback console.print(f"[dim]{traceback.format_exc()}[/]") def update_note(): """Aktualisiere eine existierende Notiz.""" api = get_api() note_id = Prompt.ask("🔑 Gib die ID der Notiz ein") try: with console.status("[bold green]📂 Lade Notiz..."): note = api.get_note(note_id) console.print(f"\n[bold]📝 Aktuelle Notiz:[/] {note.title}") title = Prompt.ask( "📝 Neuer Titel", default=note.title, show_default=True ) console.print("\n📝 Neuer Inhalt (STRG+D zum Beenden):") console.print(f"[dim]Aktuell: {len(note.body or '') } Zeichen[/]") if note.body: console.print(note.body) lines = [] try: while True: line = input() lines.append(line) except EOFError: body = "\n".join(lines) if lines else note.body is_todo = Confirm.ask( "✅ Als Todo markieren?", default=note.is_todo ) with console.status("[bold green]🔄 Aktualisiere Notiz..."): note = api.update_note( note_id=note_id, title=title, body=body, is_todo=is_todo ) console.print("\n[green]✅ Notiz erfolgreich aktualisiert![/]") except Exception as e: console.print(f"[red]❌ Fehler beim Aktualisieren der Notiz: {e}[/]") def delete_note(): """Lösche eine Notiz.""" api = get_api() note_id = Prompt.ask("🔑 Gib die ID der Notiz ein") permanent = Confirm.ask("🗑️ Permanent löschen (nicht in Papierkorb)?", default=False) try: with console.status("[bold green]📂 Lade Notiz..."): note = api.get_note(note_id) console.print(Panel( f"[bold]📝 Titel:[/] {note.title}\n" f"[bold]🔑 ID:[/] {note.id}", title="🗑️ Zu löschende Notiz", border_style="red" )) if not Confirm.ask( "[red]❗ Möchtest du diese Notiz wirklich löschen?[/]", default=False ): console.print("[yellow]↩️ Löschen abgebrochen.[/]") return with console.status( "[bold red]🗑️ Lösche Notiz..." if permanent else "[bold yellow]🗑️ Verschiebe Notiz in Papierkorb..." ): api.delete_note(note_id, permanent=permanent) console.print("\n[green]✅ Notiz erfolgreich gelöscht![/]") except Exception as e: console.print(f"[red]❌ Fehler beim Löschen der Notiz: {e}[/]") def search_notes(): """Durchsuche alle Notizen.""" api = get_api() query = Prompt.ask("🔍 Wonach möchtest du suchen") limit = IntPrompt.ask("📊 Maximale Anzahl von Ergebnissen", default=10) try: with console.status(f'[bold green]🔍 Suche nach "{query}"...'): response = api.search_notes(query=query, limit=limit) if not response.items: console.print("[yellow]📭 Keine Notizen gefunden.[/]") return table = create_notes_table(response.items) console.print(table) if response.has_more: console.print("\n[blue]📚 Es gibt weitere Ergebnisse. Erhöhe die maximale Anzahl für mehr.[/]") except Exception as e: console.print(f"[red]❌ Fehler bei der Suche: {e}[/]") def main(): """Hauptprogramm mit Menüführung.""" console.print("[bold green]🚀 Willkommen beim Joplin Notizen Manager![/]") while True: try: choice = show_menu() if choice == "exit": console.print("[bold blue]👋 Auf Wiedersehen![/]") break # Funktion aus dem Menü aufrufen globals()[choice]() # Warte auf Bestätigung bevor das Menü wieder angezeigt wird Prompt.ask("\n[bold cyan]↩️ Drücke Enter um fortzufahren[/]") except Exception as e: console.print(f"[red]❌ Ein unerwarteter Fehler ist aufgetreten: {e}[/]") if Confirm.ask("[yellow]🔄 Möchtest du es erneut versuchen?[/]", default=True): continue break if __name__ == "__main__": main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dweigend/joplin-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server