Skip to main content
Glama
4hmad69
by 4hmad69

SubTrack — трекер подписок и регулярных платежей (MCP-сервер)

Проблема, которую он решает: почти каждый незаметно теряет деньги на подписках и регулярных платежах, о которых забыл, — пробный период стримингового сервиса, который перешел в платный, абонемент в спортзал, ежегодное продление домена, которое списывается раз в год и забывается на 11 месяцев. SubTrack позволяет LLM (Claude или любому MCP-клиенту) отслеживать все это, сообщать, что скоро будет продлеваться, и показывать общие расходы, приведенные к месячному показателю, — даже если ваши подписки списываются еженедельно, ежемесячно, ежеквартально, ежегодно или по произвольному циклу.

Что он делает

Инструмент

Назначение

add_subscription

Добавить подписку/платеж с любым циклом списания

get_subscription

Получить одну запись с вычисленной датой следующего продления

list_subscriptions

Список всех (или фильтр по категории), сначала ближайшие продления

edit_subscription

Обновить любое поле существующей записи

cancel_subscription

Мягкое удаление (помечает неактивной, сохраняет историю)

delete_subscription

Полное удаление

upcoming_renewals

«Что продлевается в ближайшие N дней?»

spending_summary

Общие регулярные расходы, приведенные к месячному/годовому значению, по категориям

Плюс ресурс subtrack://categories (редактируемый список категорий) и шаблон промпта renewal_digest_prompt, который предлагает ассистенту написать дружелюбную сводку о том, что скоро предстоит.

Самая интересная инженерная деталь — next_renewal_date(): она корректно переходит вперед по дневным циклам (еженедельным/раз в две недели/произвольным) с помощью модулярной арифметики, а по календарным циклам (ежемесячным/ежеквартальным/ежегодным) — прибавляя реальные календарные месяцы (поэтому подписка, начавшаяся 31 января, корректно попадает на 28 февраля, а не на «31 день спустя»).

Related MCP server: Kordi MCP Server

Почему инструменты — async def

Каждый инструмент здесь — async def, и весь доступ к SQLite идет через aiosqlite вместо стандартного sqlite3. Стоит понять почему, поскольку это частый источник путаницы:

  • FastMCP по умолчанию запускает обычные def-инструменты в пуле потоков, так что синхронная версия этого сервера не заморозилась бы буквально при небольшой нагрузке.

  • Но async def с блокирующим драйвером (sqlite3) внутри — хуже, чем оставаться синхронным: FastMCP не выносит async def-инструменты в потоки (они выполняются прямо в цикле событий), поэтому блокирующий вызов внутри такого инструмента остановит все остальные параллельные запросы.

  • Итак: либо оставляйте инструменты def и позволяйте фреймворку выносить их в потоки, либо переходите на async def и используйте по-настоящему асинхронный драйвер на всех уровнях. Этот сервер делает второе — это более масштабируемый паттерн для удаленного сервера, который может обрабатывать параллельные вызовы инструментов от нескольких клиентов: он не расходует рабочий поток на каждый выполняющийся запрос к БД и чисто компонуется, если позже вы добавите другой ожидаемый ввод-вывод (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()). Если вы хотите, чтобы ваш собственный фиксированный список категорий переживал повторные деплои, удалите categories.json из .gitignore и закоммитьте отредактированную версию.

Запуск локально (uv)

Ручной шаг с созданием виртуального окружения не нужен — uv run автоматически создает и синхронизирует .venv из uv.lock при первом использовании.

Блок __main__ в server.py запускает сервер по HTTP на 0.0.0.0:8000 по умолчанию (переопределяется переменной окружения 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())

Если вы хотите протестировать с MCP-клиентом, который ожидает stdio вместо этого (например, подключив это к Claude Desktop для локального использования), запустите его через FastMCP CLI, который переопределяет транспорт независимо от того, что находится в __main__:

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

Добавление или обновление зависимостей

Не редактируйте список зависимостей в pyproject.toml вручную — позвольте uv управлять им, чтобы uv.lock оставался синхронизированным:

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, но здесь он вам не нужен). Вы получите URL вида https://<project>.fastmcp.app/mcp, который может вызывать любой MCP-клиент — включая Claude, через пользовательский коннектор.

Примечание о хранилище

Этот сервер использует SQLite на локальном диске для простоты — это отлично подходит для обучения и для развертывания с одним экземпляром. Не гарантируется, что он переживет повторный деплой на большинстве управляемых платформ (свежий деплой обычно означает свежую файловую систему). Когда логика инструментов вас устроит, естественный следующий шаг — и хорошее упражнение для дальнейшего изучения удаленных MCP-серверов — заменить sqlite3 на размещенную базу данных (Turso/libSQL, Postgres через asyncpg, Supabase и т.д.), используя переменную окружения для строки подключения, заданную в панели FastMCP Cloud (os.getenv("DATABASE_URL")).

Идеи для расширения

  • Добавьте mcp.tool(), который отправляет по электронной почте или пушит дайджест, используя вывод renewal_digest_prompt.

  • Добавьте таблицу price_history и инструмент для записи повышений цен с течением времени.

  • Добавьте аутентификацию (FastMCP поддерживает аутентификацию по bearer-токену), когда будете готовы сделать сервер приватным вместо открытого.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables households and small teams to monitor bills and subscriptions, receive evidence-backed saving proposals, and execute actions only after a signed, scope-capped mandate, with every decision and refusal logged in an auditable ledger.
    MIT