Skip to main content
Glama
SubTrack
by 4hmad69

SubTrack — サブスクリプション&定期請求トラッカー(MCPサーバー)

**解決する問題:**ほぼすべての人が、忘れていたサブスクリプションや定期請求で、気づかないうちにお金を失っています。無料トライアルが有料に切り替わったストリーミング、ジムの会費、年に一度だけ請求されて11か月間忘れられるドメイン更新などです。SubTrackを使うと、LLM(Claudeや任意のMCPクライアント)がそれらすべてを追跡し、間もなく更新されるものを知らせてくれます。また、サブスクリプションの請求が毎週・毎月・四半期・毎年・カスタムサイクルのいずれであっても、月額に換算した総支出を表示できます。

機能

ツール

目的

add_subscription

任意の請求サイクルでサブスクリプション/請求を追加

get_subscription

1件を取得(次の更新日を計算して表示)

list_subscriptions

すべてを一覧表示(カテゴリでフィルタリング可)、更新が近い順

edit_subscription

既存エントリの任意のフィールドを更新

cancel_subscription

ソフト削除(非アクティブにマークし、履歴を保持)

delete_subscription

完全削除

upcoming_renewals

「次のN日以内に更新されるものは?」

spending_summary

定期支出の合計を月額/年額に換算し、カテゴリ別に表示

さらに、subtrack://categoriesリソース(編集可能なカテゴリリスト)と、今後の予定について親しみやすい要約をアシスタントに作成させるrenewal_digest_promptプロンプトテンプレートも用意されています。

エンジニアリング上の興味深い点はnext_renewal_date()です:日ベースのサイクル(毎週/隔週/カスタム)はモジュロ演算で正しく進め、カレンダーベースのサイクル(毎月/四半期/毎年)は実際のカレンダー月を加算して進めます(1月31日に開始したサブスクリプションは「31日後」ではなく、正しく2月28日に更新されます)。

Related MCP server: Kordi MCP Server

ツールがasync defである理由

ここにあるすべてのツールはasync defで、SQLiteへのアクセスはすべて標準ライブラリのsqlite3ではなくaiosqliteを経由します。よく混乱するポイントなので、なぜなのかを理解しておく価値があります:

  • FastMCPはデフォルトで通常のdefツールをスレッドプールで実行するため、このサーバーの同期版でも軽い負荷で文字通りフリーズすることはありません。

  • しかし、async defの中でブロッキングドライバ(sqlite3)を使うのは、同期のままにするより悪い結果になります。FastMCPはasync defツールをスレッドにオフロードしないため(イベントループ上で直接実行される)、その中のブロッキング呼び出しは他のすべての並行リクエストを停滞させてしまいます。

  • つまり、ツールをdefのままにしてフレームワークにスレッドオフロードさせるか、async defにしてかつ本当に非同期のドライバを一貫して使うかのどちらかです。このサーバーは後者を採用しており、複数のクライアントからの同時ツール呼び出しが発生する可能性のあるリモートサーバーにとって、よりスケーラブルなパターンです。進行中のDB呼び出しごとにワーカースレッドを消費せず、後で他の待機可能なI/O(HTTP呼び出しなど)を追加する場合もきれいに構成できます。

  • 唯一の例外:小さなcategories.jsonの読み取りはプレーンな同期のままです。呼び出しごとに数百バイトを一度読むだけなので、「非同期」にするにはaiofilesを追加することになり、実際の並行性の利点はありません。

プロジェクト構成

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とsubscriptions.dbはコミットされません。server.pyが初回実行時に自動的に作成します(init_categories() / init_db()を参照)。独自の固定カテゴリリストを再デプロイ後も保持したい場合は、.gitignoreからcategories.jsonを削除して、編集したバージョンをコミットしてください。

ローカルで実行する(uv)

手動のvenv手順は不要です。uv runは初回使用時にuv.lockから.venvを自動的に作成して同期します。

server.pyの__main__ブロックは、デフォルトで0.0.0.0:8000上でHTTP経由のサーバーを実行します(PORT環境変数で上書き可能)。これはFastMCP Cloudが本番環境で使用するのと同じトランスポートであり、ローカルでのテストが実際にデプロイする環境と一致します:

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

注:このブロックはファイルを直接実行したときのみ動作します。FastMCP Cloudはこれを完全に無視します。mcpオブジェクトをインポートして自身で配信するため、ここにあるものはデプロイされたサーバーに一切影響しません。

簡単なクライアントスクリプトでテストします(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())

代わりにstdioを期待するMCPクライアントでテストしたい場合(例:ローカル使用のためにClaude Desktopに組み込む場合)は、FastMCP CLI経由で実行してください。__main__の内容に関係なくトランスポートを上書きします:

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

依存関係の追加または更新

pyproject.tomlの依存関係リストを手動で編集しないでください。uv.lockの同期を保つために、uvに管理させましょう:

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

FastMCP Cloudにデプロイする

  1. このフォルダをGitHubリポジトリにプッシュします。pyproject.tomlとuv.lockをコミットします(.venv/はコミットしないでください。gitignoreされています)。

  2. fastmcp.cloudにGitHubでサインインし、リポジトリから新しいプロジェクトを作成します。

  3. エントリポイントを次のように設定します:

    server.py:mcp
  4. デプロイします。FastMCP Cloudはpyproject.tomlから依存関係を自動検出します(プレーンなrequirements.txtも理解しますが、ここでは不要です)。https://<project>.fastmcp.app/mcpのようなURLが発行され、任意のMCPクライアント(カスタムコネクタ経由のClaudeを含む)が呼び出せるようになります。

ストレージに関する注意

このサーバーはシンプルさのためローカルディスク上のSQLiteを使用しており、学習やシングルインスタンスのデプロイに最適です。ただし、ほとんどのマネージドプラットフォームでは再デプロイ後の存続は保証されません(新しいデプロイは通常、新しいファイルシステムを意味します)。ツールのロジックに満足したら、次の自然なステップ—そしてリモートMCPサーバーの学習をさらに進める良い練習—は、sqlite3をホスト型データベース(Turso/libSQL、asyncpg経由のPostgres、Supabaseなど)に置き換え、接続文字列を環境変数で指定し、FastMCP Cloudダッシュボードで設定することです(os.getenv("DATABASE_URL"))。

拡張のアイデア

  • renewal_digest_promptの出力を使ってダイジェストをメール送信/プッシュするmcp.tool()を追加する。

  • price_historyテーブルと、時間の経過に伴う値上げを記録するツールを追加する。

  • サーバーを公開ではなく非公開にする準備ができたら、認証を追加する(FastMCPはベアラートークン認証をサポートしています)。

Related MCP Connectors

Related MCP Servers