Skip to main content
Glama
yoruuuchan

yoru-studio-mcp

by yoruuuchan

Yoru Studio

Самостоятельно размещаемая студия для работы одного творца.

Читать на упрощённом китайском.

Yoru Studio — это рабочее пространство одного человека для превращения идей в готовые творческие работы: поймать искру во входящих, перенести её в материнский проект, разбить на подпроекты (видео / фотоочерк / длинная статья / поставка материалов), спланировать раскадровки, провести полевые съёмки с офлайн-очередью, зафиксировать, что произошло на самом деле, и замкнуть цикл ретроспективой. Результаты живут в версиях под платформы, так что один и тот же монтаж можно выпустить как Douyin / Bilibili / набор изображений для Xiaohongshu без дублирования самого проекта.

Он написан для одного человека — творца — и работает на его собственном компьютере. Никакой мультитенантности, никаких командных мест, никакого SaaS-бэкенда. После установки вся система живёт на одной машине, которой вы управляете.

Что здесь есть

Чтение исходного кода — авторитетный ответ на вопрос «что это на самом деле делает?» — приведённое ниже резюме — это карта, а не территория.

  • Входящие → Материнский проект → Подпроект → Версия платформы: полный хребет творческого рабочего процесса, с быстрым путём, позволяющим хорошей идее сразу перейти в активный подпроект, когда вы знаете, куда она относится.

  • Раскадровки с представлением списком и представлением доски, перетаскиванием для изменения порядка, эталонными изображениями для каждого кадра, экспортом в XLSX и печатной версией для использования на площадке.

  • Расписание и календарь: точное время, события на весь день и размытые окна («на этой неделе», «на выходных») сосуществуют в одном календаре; просроченное вычисляется, а не запоминается, так что ничто тихо не гниёт.

  • Напоминания и внутрисайтовые уведомления, дедуплицированные на уровне базы данных, чтобы перезапуск никогда не срабатывал дважды на одно и то же предупреждение.

  • Записи о выполнении для съёмок / пересъёмок / записи экрана / писательских сессий — прикрепите запись к тому слою проекта, который действительно соответствует тому, что вы сделали.

  • Ретроспективы, лёгкие или полные; каждое поле необязательно. Структура существует, чтобы напомнить вам, а не требовать.

  • Вложения четырёх видов: загруженные изображения (с миниатюрами), указатели путей (например, NAS/2026/Aug shoots/), внешние ссылки и текстовые фрагменты. Мягко удалённые файлы лежат в корзине 30 дней.

  • Полевой режим: мобильная страница для использования на площадке. Если сеть пропадает, правки ставятся в очередь в IndexedDB и синхронизируются при восстановлении соединения.

  • Полный экспорт: JSON + пакет загрузок для выноса данных, либо из CLI, либо со страницы настроек.

  • Резервные копии: онлайн-резервное копирование SQLite по расписанию и опциональные скрипты restic для зашифрованных внешних копий с настоящим средством прогона восстановления.

  • Канал MCP для ИИ-агентов (Claude, ChatGPT, Codex, …) — см. раздел ниже.

Related MCP server: todos

Принципы дизайна

  • Один пользователь, одна учётная запись. Модель данных несёт колонку рабочего пространства, чтобы будущая многопользовательская версия не перестраивала схему, но всё в поставляемом коде предполагает ровно одного пользователя.

  • Никаких внешних сервисов. SQLite на диске, файлы на диске. Никакого Redis, никакой очереди сообщений, никакой сторонней аутентификации. Можно запустить на VPS за $5/месяц.

  • Малый след. Цель — приложение 512 МиБ / планировщик 256 МиБ / (опционально) боковой контейнер обратного прокси 128 МиБ. Виртуальной машины на 2 ГиБ достаточно.

  • Сервер не собирает фронтенд. Vite-сборка выполняется локально (или в CI) и поставляется как предварительно собранные файлы. На машинах развёртывания Node не нужен.

  • Содержание важнее церемоний. В форме ретроспективы нет обязательных полей — схема существует, чтобы напомнить вам, о чём подумать, а не чтобы блокировать сохранение.

Технологический стек

  • Бэкенд: Python 3.12, FastAPI, SQLite (с uv для управления зависимостями).

  • Фронтенд: React 19 + TypeScript, собирается с помощью Vite.

  • Развёртывание: Docker Compose (один хост). Обратное проксирование и TLS — на ваше усмотрение — подойдут Cloudflare Tunnel, Caddy, Nginx, Tailscale Funnel или просто SSH-туннели для локального использования.

  • Тестирование: pytest для бэкенда, vitest для фронтенда.

Канал MCP: подключение ИИ-агентов

Yoru Studio предоставляет сервер Model Context Protocol (MCP), чтобы агенты, говорящие на MCP — Claude Desktop, ChatGPT desktop, Codex CLI, Claude Code и другие — могли читать из вашей студии и (дополнять её) без копипасты.

Всего восемь инструментов, все ограничены записью-добавлением с идемпотентностью:

Чтение (5):

  • list_projects — список материнских проектов со счётчиками.

  • get_project — полные детали одного материнского проекта, включая его подпроекты.

  • get_sub_project — один подпроект со встроенными раскадровкой / записями о выполнении / ретроспективой.

  • get_schedule — предстоящие (14 или 30 дней), все просроченные, ближайшие размытые события.

  • get_inbox — ожидающие или отклонённые элементы входящих.

Запись (3, все только-добавление, все идемпотентные):

  • capture_inspiration — бросить искру во входящие.

  • append_storyboard_shots — атомарно добавить N кадров в раскадровку видеоподпроекта.

  • append_execution_record — зафиксировать съёмку / писательскую сессию / тест.

Каждый инструмент записи принимает idempotency_key. Повтор с тем же ключом возвращает первый результат; другая полезная нагрузка с тем же ключом — жёсткий конфликт. Ничто из того, что делает агент, не может молча перезаписать уже имеющуюся у вас работу.

Два пути аутентификации за одной конечной точкой /mcp (спецификация §5.1 в docs/spec/ в исходниках):

  • Статический Bearer-токен для личного / одноагентного использования. Вы создаёте одну длинную случайную строку, сохраняете её sha256 в окружении и передаёте токен агенту.

  • OAuth 2.0 с PKCE + динамическая регистрация клиентов для коннекторов, которые этого ожидают (коннектор ChatGPT — текущий случай, который этого требует).

Оба пути могут сосуществовать. Оба необязательны — оставьте оба не заданными, и маршрут /mcp никогда не смонтируется.

Быстрый старт

Два пути в зависимости от того, как вы хотите запускать: из исходников (для разработки или если предпочитаете управлять Python самостоятельно) или через Docker Compose (для стабильной установки на одном хосте).

Из исходников

Требуется Python 3.12 и uv, плюс Node 20+ для фронтенда.

# 1. Install Python deps and set up the venv
uv sync

# 2. Initialize / migrate the database (creates ./data/studio.sqlite3)
uv run studio init-db

# 3. Start the API server on http://127.0.0.1:8000 (local mode — no auth)
uv run studio serve

# 4. In another terminal, run the frontend dev server
cd frontend
npm install
npm run dev        # http://localhost:5173, proxies to the API

Локальный режим привязывается к loopback и пропускает аутентификацию для удобства разработчика. Чтобы попробовать поток аутентификации локально, следуйте разделу «Включение удалённого режима» ниже.

Другие команды CLI:

uv run studio db-backup            # verified online SQLite backup
uv run studio db-restore <path> --confirm-database ./data/studio.sqlite3
uv run studio export               # full JSON + uploads takeout
uv run studio schedule-tick        # run the periodic maintenance jobs once
uv run studio hash-password        # interactively hash a password for STUDIO_AUTH_PASSWORD_HASH

Запустите тесты:

uv run pytest                      # backend
cd frontend && npm test            # frontend

Docker Compose

Файл docker-compose.yml в этом репозитории определяет три сервиса: app (FastAPI + собранный SPA), scheduler (цикл с тиком в 60 секунд, выполняющий резервное копирование, напоминания, хранение) и cloudflared (эталонный боковой контейнер обратного прокси — замените его на то, что подходит вашей инфраструктуре).

Обратное проксирование / TLS намеренно вне рамок приложения: выбирайте своё. Разумные варианты включают:

  • Cloudflare Tunnel (эталонный сервис cloudflared в docker-compose.yml со скриптом подготовки в scripts/provision-cloudflare-tunnel.py).

  • Caddy или Nginx как обратный прокси на уровне хоста с завершением TLS на ваших собственных сертификатах.

  • Tailscale Funnel для приватного размещения.

  • Просто SSH-форвард -L 8000, если вам нужно только на своей машине.

Если вы используете Cloudflare Tunnel, либо отредактируйте, либо удалите сервис cloudflared и снимите STUDIO_TRUSTED_PROXY_IPS в .env.production. Если вы используете другой прокси, задайте STUDIO_TRUSTED_PROXY_IPS как IP вашего прокси, чтобы реальные IP клиентов попадали в журнал аудита.

Шаги развёртывания (когда ваш Docker-хост готов):

# 1. Build the frontend locally — the server never builds it.
cd frontend && npm ci && npm run build && cd ..

# 2. Copy the env template and fill in the required secrets.
cp deploy/env.production.example .env.production
chmod 600 .env.production
$EDITOR .env.production

# 3. Generate a scrypt-hashed password for STUDIO_AUTH_PASSWORD_HASH.
uv run studio hash-password
# Paste the "password_hash" value into .env.production, single-quoted.

# 4. Build and start.
docker compose --env-file .env.production build
docker compose --env-file .env.production up -d

В docs/deploy.md есть более подробное руководство, охватывающее эталонную компоновку, скрипты автоматизации резервного копирования в scripts/ и операционную блокировку, которую разделяют задания развёртывания и резервного копирования.

Включение удалённого режима

Удалённый режим превращает приложение из «локальной разработки без аутентификации» в «публичный URL за прокси с сессионными cookie». Задайте как минимум:

  • STUDIO_MODE=remote

  • STUDIO_SESSION_SECRET — случайная строка не короче 32 символов.

  • STUDIO_AUTH_PASSWORD_HASH — вывод uv run studio hash-password.

  • STUDIO_ALLOWED_HOSTS — точное имя (имена) хоста, на которые приложение будет отвечать (без подстановочных знаков; приложение откажется запускаться с *).

  • STUDIO_TRUSTED_PROXY_IPS — если перед приложением стоит обратный прокси, IP-адрес(а), с которых он обращается к приложению.

Приложение отказывается запускаться в удалённом режиме, если отсутствует любой из обязательных секретов или allowed_hosts пуст — это намеренно. Не существует конфигурации «тихо открытой».

Конфигурация

Большинство значений живут в переменных окружения (так производство дружелюбно к Docker). Подмножество также может жить в TOML-файле, загружаемом через --config или STUDIO_CONFIG — см. config/config.example.toml для формы.

Секреты только в окружении по дизайну: они никогда не читаются из TOML-конфига, так что включение конфиг-файла в развёртывание никогда не может их утечь.

Переменная

Назначение

По умолчанию

STUDIO_MODE

local (только loopback, без аутентификации) или remote (сессионные cookie + пароль)

local

STUDIO_BIND_HOST

Адрес, к которому привязывается сервер

127.0.0.1

STUDIO_BIND_PORT

Порт, к которому привязывается сервер

8000

STUDIO_ALLOWED_HOSTS

Список имён хостов через запятую, принимаемых в заголовке Host: (обязательно в remote-режиме)

(пусто)

STUDIO_TRUSTED_PROXY_IPS

Список IP прокси через запятую, чьим CF-Connecting-IP / X-Forwarded-For доверять

(пусто)

STUDIO_SESSION_SECRET

Случайная строка ≥32 символов для подписи сессионных cookie (обязательно в remote-режиме)

(пусто)

STUDIO_AUTH_PASSWORD_HASH

scrypt-хеш пароля для входа из studio hash-password (обязательно в remote-режиме)

(пусто)

STUDIO_DATA_DIR

Где хранится база данных SQLite

./data

STUDIO_UPLOADS_DIR

Где хранятся загруженные вложения

<data-dir>/uploads

STUDIO_BACKUPS_DIR

Куда записываются онлайн-резервные копии SQLite

./backups

STUDIO_LOGS_DIR

Куда идут журналы приложения

./logs

STUDIO_BACKUP_STATE_DIR

Необязательный путь только для чтения, куда задания резервного копирования хоста кладут capacity.json для виджета статуса в приложении

(не задан — статус показывает unknown)

STUDIO_UPLOAD_MAX_FILE_BYTES

Лимит загрузки на один файл

26214400 (25 МиБ)

STUDIO_UPLOAD_QUOTA_BYTES

Общая квота загрузки на поддерево каждого материнского проекта

2147483648 (2 ГиБ)

STUDIO_UPLOAD_MAX_IMAGE_PIXELS

Защита от бомбы декомпрессии

40000000 (40 млн пикс.)

STUDIO_RADAR_TOKEN_HASH

sha256-hex bearer-токена канала приёма; если не задан, конечная точка приёма отключена

(не задан)

STUDIO_MCP_TOKEN_HASH

sha256-hex статического bearer-токена MCP; если не задан, путь статического Bearer отключён

(не задан)

STUDIO_MCP_OAUTH_ISSUER_URL

Публичный URL, на котором размещены метаданные OAuth AS; его установка активирует путь OAuth

(не задан)

STUDIO_MCP_OAUTH_ALLOWED_REDIRECT_HOSTS

Список имён хостов через запятую, разрешённых в redirect URI DCR (loopback разрешён всегда)

chatgpt.com

STUDIO_MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS

Время жизни OAuth access-токена

3600

STUDIO_MCP_OAUTH_REFRESH_TOKEN_TTL_SECONDS

Время жизни OAuth refresh-токена

2592000 (30 дней)

STUDIO_MCP_OAUTH_CODE_TTL_SECONDS

Время жизни OAuth кода авторизации

300

Генерация хешированных токенов

Конечная точка приёма и путь статического Bearer MCP хранят sha256(token) — никогда не сам токен — поэтому утёкший .env.production не даёт ничего, что можно было бы воспроизвести.

# Generate a token and its hash. The token goes to whichever caller needs it
# (your external intake, your MCP client). The hash goes into .env.production.
TOKEN=$(python -c "import secrets; print(secrets.token_urlsafe(32))")
printf %s "$TOKEN" | sha256sum | cut -d' ' -f1   # → STUDIO_RADAR_TOKEN_HASH / STUDIO_MCP_TOKEN_HASH
echo "$TOKEN"                                     # → give to the caller, nowhere else

Внешний приём: передайте свой собственный поток входящих во входящие

Существует HTTP-конечная точка, предназначенная для приёма элементов от внешнего источника — скрапера RSS, инструмента радара тем, запланированного задания по сбору данных, любого инструмента, который загружает контент от вашего имени. Конечная точка универсальна: принесите свой собственный апстрим, подключите его сюда, и элементы попадут во входящие, где вы их разберёте.

Конечная точка: POST /api/inbox

Аутентификация: Authorization: Bearer <token>. Сервер сравнивает sha256(token) с STUDIO_RADAR_TOKEN_HASH за постоянное время. Если эта переменная окружения не задана, конечная точка возвращает 401 на каждый вызов Bearer — приём остаётся полностью закрытым.

CSRF не требуется на этом пути: CSRF-cookie защищает от воспроизведения браузерной сессии, что не является угрозой, когда вызывающая сторона предоставляет свой собственный bearer-заголовок.

Тело запроса (JSON):

Поле

Тип

Примечания

title

строка, обязательно, ≤500 символов

Заголовок элемента входящих. Пусто / отсутствует → 400.

first_reaction

строка, необязательно

Ваш однострочный комментарий.

links

строка, необязательно

Произвольный текст — вставленные URL подойдут.

radar_topic_id

строка, необязательно, ≤500 символов

Идентификатор этой темы от вашего источника. Второй по силе ключ дедупликации.

canonical_url

строка, необязательно, ≤2000 символов

Канонический URL элемента. Третий по силе ключ дедупликации.

idempotency_key

строка, необязательно, ≤500 символов

Уникальный ключ на каждую доставку. Самый сильный ключ дедупликации.

Приоритет дедупликации: idempotency_key > radar_topic_id > canonical_url. При повторной доставке сервер возвращает уже существующую строку, а не создаёт вторую — даже если вы уже отклонили или преобразовали эту строку. Повторная доставка не должна отменять ваше решение по разбору.

Ответ:

  • 201 Created — была вставлена новая строка.

  • 200 OK — повторная доставка была сопоставлена с существующей строкой (любой статус, включая отклонённый / преобразованный). Та же форма тела.

  • 400 Bad Request — отсутствует / недействителен title.

  • 401 Unauthorized — неверный или отсутствующий bearer-токен, или приём не настроен.

Тело ответа:

{
  "item": {
    "id": 42,
    "title": "…",
    "source": "radar",
    "status": "pending",
    "created_at": "2026-08-12T12:34:56Z",
    "…": "…"
  },
  "deduplicated": false
}

Пример curl:

curl -X POST https://studio.example.com/api/inbox \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Interesting minisite on typography systems",
    "first_reaction": "worth a look for the next essay",
    "links": "https://example.com/article",
    "canonical_url": "https://example.com/article",
    "idempotency_key": "myfeed-2026-08-12-a3f9"
  }'

Поле называется «radar» во всей кодовой базе, потому что изначально оно было подключено к внешнему инструменту радара контента; сама конечная точка универсальна и работает с любым источником, умеющим говорить по HTTP.

Лицензия

Copyright (C) 2026 yoruuuchan.

Yoru Studio лицензируется по GNU Affero General Public License, версия 3, только (AGPL-3.0-only). LICENSE содержит точный текст лицензии.

Одним предложением: вы можете свободно размещать у себя, использовать и изменять код для своей собственной творческой работы; если вы запускаете модифицированную версию как сетевой сервис, с которым взаимодействуют другие люди, вы должны предложить им исходный код этой модифицированной версии. Именно это AGPL и призвана обеспечивать — пункт о «сетевом использовании» (§13) делает взаимное обязательство срабатывающим при запуске, а не только при распространении.

Ожидания

Это личный проект. Он существует, потому что одному создателю он был нужен, и он решил им поделиться.

  • Не продукт. Нет дорожной карты, на которую кто-то другой имеет право, нет SLA поддержки и нет обещания, что следующий релиз не сломает вашу настройку.

  • Поддерживается в собственном ритме автора. Проблемы и pull request'ы приветствуются, но ответы приходят, когда приходят.

  • Вы размещаете его у себя. Хостинговая версия не существует. И планов на неё нет.

  • Данные живут на вашей машине. Ничего не обращается домой. Ничего не отправляется третьим лицам. В этом и смысл самостоятельного размещения; это также причина, по которой никто не спасёт ваши данные, если вы их потеряете. Делайте резервные копии.

Если что-то из этого читается как «не для меня» — это честный сигнал: пожалуйста, выберите что-то другое, и никаких обид.

Вклад в проект

Отчёты об ошибках приветствуются. Пожалуйста, включайте достаточно деталей, чтобы ошибку можно было воспроизвести на чистой копии репозитория.

Запросы функций: этот проект намеренно ограничивает свой масштаб и добавляет функции только после того, как реальное использование выявит потребность. Запрос функции, который звучит как «вот с чем я реально столкнулся, пытаясь использовать приложение», с гораздо большей вероятностью будет принят, чем тот, который звучит как «вот что было бы неплохо иметь».

Pull request'ы: для чего-то большего, чем исправление ошибки в одном файле, пожалуйста, сначала откройте issue, чтобы проверить, что направление подходит. AGPL-3.0-only означает, что вклад должен быть совместим с этой лицензией — открывая pull request, вы соглашаетесь, что ваш вклад распространяется на тех же условиях, что и остальная часть проекта.

Атрибуция

Создано Yoru, Claude Fable 5 и GPT 5.6 Sol — нами троими.

Maintenance

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    Production-grade MCP server that gives AI agents safe access to your local dev environment: filesystem, databases, processes, and OpenAPI specs.
    15
    33 npm
    3
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP server for task management, project knowledge, workspace trust, runner sandboxes, extension registry, and workflow prompts, enabling AI agents to manage tasks and collaborate locally.
    5,117 npm
    Apache 2.0
  • F
    license
    A
    quality
    C
    maintenance
    A local-first MCP server that handles daily work tasks through your AI assistant: converts meeting notes into todos, manages todo lifecycle, tracks work hours, generates daily/weekly reports, organizes files via move-only operations, and diagnoses dev environments, all guarded by a human-maintained preview/apply safety model.
    25
    -