Skip to main content
Glama
SubTrack
by 4hmad69

SubTrack – Abo- und wiederkehrender Rechnungs-Tracker (MCP-Server)

Das Problem, das es löst: Fast jeder verliert still und leise Geld durch Abos und wiederkehrende Rechnungen, die er vergessen hat – eine Streaming-Testphase, die in ein kostenpflichtiges Abo übergegangen ist, eine Mitgliedschaft im Fitnessstudio, eine jährliche Domain-Verlängerung, die einmal anfällt und dann elf Monate lang vergessen wird. SubTrack ermöglicht es einer LLM (Claude oder einem anderen MCP-Client), alles zu verfolgen, dir mitzuteilen, was demnächst verlängert wird, und die Gesamtausgaben auf eine monatliche Zahl zu normalisieren – auch wenn deine Abos wöchentlich, monatlich, vierteljährlich, jährlich oder in einem individuellen Zyklus abgerechnet werden.

Was es tut

Tool

Zweck

add_subscription

Ein Abo/eine Rechnung mit beliebigem Abrechnungszyklus hinzufügen

get_subscription

Einzelnes abrufen, mit berechnetem nächstem Verlängerungsdatum

list_subscriptions

Alle auflisten (oder nach Kategorie filtern), zuerst die nächste Verlängerung

edit_subscription

Beliebiges Feld eines vorhandenen Eintrags aktualisieren

cancel_subscription

Weiches Löschen (als inaktiv markieren, Verlauf bleibt erhalten)

delete_subscription

Endgültig löschen

upcoming_renewals

„Was wird in den nächsten N Tagen verlängert?"

spending_summary

Gesamte wiederkehrende Ausgaben, auf monatlich/jährlich normalisiert, nach Kategorie

Außerdem gibt es eine subtrack://categories-Ressource (bearbeitbare Kategorienliste) und eine renewal_digest_prompt-Promptvorlage, die den Assistenten bittet, eine freundliche Zusammenfassung der anstehenden Verlängerungen zu schreiben.

Der interessante technische Teil ist next_renewal_date(): Es schreitet tagesbasierte Zyklen (wöchentlich/zweiwöchentlich/individuell) mit modularer Arithmetik korrekt voran und kalenderbasierte Zyklen (monatlich/vierteljährlich/jährlich), indem echte Kalendermonate addiert werden (so landet ein Abo, das am 31. Januar begonnen hat, korrekt am 28. Februar und nicht „31 Tage später“).

Related MCP server: Kordi MCP Server

Warum die Tools async def sind

Jedes Tool hier ist async def, und der gesamte SQLite-Zugriff läuft über aiosqlite statt über das sqlite3-Modul der Standardbibliothek. Es lohnt sich zu verstehen, warum, da dies ein häufiger Punkt der Verwirrung ist:

  • FastMCP führt einfache def-Tools standardmäßig bereits in einem Thread-Pool aus, sodass eine synchrone Version dieses Servers unter geringer Last nicht buchstäblich einfrieren würde.

  • Aber async def + ein blockierender Treiber (sqlite3) darin ist schlimmer, als synchron zu bleiben – FastMCP lagert async def-Tools nicht in Threads aus (sie laufen direkt auf der Ereignisschleife), sodass ein blockierender Aufruf darin alle anderen gleichzeitigen Anfragen anhalten würde.

  • Also: Entweder die Tools als def belassen und das Framework die Thread-Auslagerung übernehmen lassen, oder async def verwenden und durchgängig einen echten asynchronen Treiber nutzen. Dieser Server tut Letzteres, was das skalierbarere Muster für einen entfernten Server ist, der gleichzeitige Tool-Aufrufe von mehreren Clients erleben kann – er verbraucht keinen Worker-Thread pro laufendem DB-Aufruf und lässt sich sauber erweitern, wenn man später andere awaitbare E/A hinzufügt (HTTP-Aufrufe usw.).

  • Die eine Ausnahme: Das winzige Lesen von categories.json bleibt ganz normal synchron. Es werden ein paar hundert Bytes pro Aufruf gelesen – es „async“ zu machen, würde bedeuten, aiofiles hinzuzufügen, ohne echten Nutzen für die Nebenläufigkeit.

Projektstruktur

subtrack-mcp/
├── server.py          # the whole server — module-level `mcp` object
├── pyproject.toml     # project metadata + deps (managed by uv)
├── uv.lock            # locked, reproducible dependency versions
├── .python-version    # pins the Python version uv uses
├── .gitignore
└── README.md

categories.json und subscriptions.db sind nicht eingecheckt – server.py erstellt sie beim ersten Start automatisch (siehe init_categories() / init_db()). Wenn du möchtest, dass deine eigene feste Kategorienliste erneute Bereitstellungen übersteht, entferne categories.json aus .gitignore und committe deine bearbeitete Version.

Lokal ausführen (uv)

Kein manueller venv-Schritt nötig – uv run erstellt und synchronisiert .venv beim ersten Verwenden automatisch aus uv.lock.

Der __main__-Block in server.py startet den Server standardmäßig über HTTP auf 0.0.0.0:8000 (überschreibbar mit der Umgebungsvariable PORT) – derselbe Transport, den FastMCP Cloud in Produktion verwendet, sodass lokale Tests dem entsprechen, was du tatsächlich bereitstellst:

uv run server.py
# Starting MCP server 'SubTrack' with transport 'http' on http://0.0.0.0:8000/mcp

Hinweis: Dieser Block wird nur ausgeführt, wenn du die Datei direkt ausführst. FastMCP Cloud ignoriert ihn vollständig – es importiert das mcp-Objekt und stellt es selbst bereit, sodass hier nichts den bereitgestellten Server beeinflusst.

Teste es mit einem schnellen Client-Skript (uv run python client_test.py):

import asyncio
from fastmcp import Client

async def main():
    async with Client("http://127.0.0.1:8000/mcp") as client:
        result = await client.call_tool("add_subscription", {
            "name": "Netflix", "amount": 15.99, "billing_cycle": "monthly",
            "start_date": "2026-08-05", "category": "Streaming", "subcategory": "Video"
        })
        print(result.data)

asyncio.run(main())

Wenn du mit einem MCP-Client testen möchtest, der stattdessen stdio erwartet (z. B. um dies für die lokale Nutzung in Claude Desktop einzubinden), führe es über die FastMCP-CLI aus, die den Transport unabhängig davon überschreibt, was in __main__ steht:

uv run fastmcp run server.py:mcp --transport stdio

Abhängigkeiten hinzufügen oder aktualisieren

Bearbeite die Abhängigkeitsliste von pyproject.toml nicht von Hand – lass uv das verwalten, damit uv.lock synchron bleibt:

uv add some-package             # add a new dependency
uv add some-package --upgrade   # bump one dependency
uv lock --upgrade               # re-resolve everything to latest compatible versions

Auf FastMCP Cloud bereitstellen

  1. Schiebe diesen Ordner in ein GitHub-Repository – committe pyproject.toml und uv.lock (committe .venv/ nicht, das ist gitignoriert).

  2. Melde dich bei fastmcp.cloud mit GitHub an und erstelle ein neues Projekt aus dem Repository.

  3. Setze den Einstiegspunkt auf:

    server.py:mcp
  4. Stelle bereit. FastMCP Cloud erkennt Abhängigkeiten automatisch aus pyproject.toml (es versteht auch eine einfache requirements.txt, aber die brauchst du hier nicht). Du erhältst eine URL wie https://<project>.fastmcp.app/mcp, die jeder MCP-Client – einschließlich Claude über einen benutzerdefinierten Connector – aufrufen kann.

Ein Hinweis zum Speicher

Dieser Server verwendet zur Einfachheit SQLite auf der lokalen Festplatte, was sich gut zum Lernen und für eine Einzelinstanz-Bereitstellung eignet. Es ist nicht garantiert, dass es eine erneute Bereitstellung übersteht auf den meisten verwalteten Plattformen (eine frische Bereitstellung bedeutet normalerweise ein frisches Dateisystem). Wenn du mit der Tool-Logik zufrieden bist, ist der nächste logische Schritt – und eine gute Übung, um entfernte MCP-Server weiter kennenzulernen –, sqlite3 durch eine gehostete Datenbank zu ersetzen (Turso/libSQL, Postgres über asyncpg, Supabase usw.), wobei eine Umgebungsvariable für die Verbindungszeichenfolge verwendet wird, die im FastMCP-Cloud-Dashboard festgelegt wird (os.getenv("DATABASE_URL")).

Ideen zur Erweiterung

  • Füge ein mcp.tool() hinzu, das eine Zusammenfassung per E-Mail sendet oder per Push sendet, die die Ausgabe von renewal_digest_prompt verwendet.

  • Füge eine Tabelle price_history und ein Tool hinzu, um Preissteigerungen im Laufe der Zeit zu protokollieren.

  • Füge eine Authentifizierung hinzu (FastMCP unterstützt Bearer-Token-Authentifizierung), sobald du bereit bist, den Server privat statt offen zu machen.

Related MCP Connectors

Related MCP Servers