Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Yoru Studio

БесплатноНе проверен

MCP server for Yoru Studio that lets AI agents read projects, schedules, and inbox, and safely append inspiration, storyboard shots, and execution records to a

GitHubEmbed

Описание

MCP server for Yoru Studio that lets AI agents read projects, schedules, and inbox, and safely append inspiration, storyboard shots, and execution records to a creator's self-hosted workspace.

README

A self-hosted execution studio for a single creator.

Read this in Simplified Chinese.

Yoru Studio is one person's workspace for turning ideas into finished creative work: catch a spark in the inbox, pull it into a mother project, break it into sub-projects (video / photo essay / long-form article / asset delivery), plan storyboards, run field shoots with an offline-safe queue, log what actually happened, then close the loop with a retrospective. Deliverables live under platform versions so the same cut can ship as Douyin / Bilibili / a Xiaohongshu image set without duplicating the project itself.

It is written for one person — the creator — running it on their own box. There is no multi-tenant story, no team seats, no SaaS backend. When you install it, the whole system lives on one machine you control.

What is here

Reading the source is the authoritative answer to "what does it actually do?" — the summary below is a map, not the territory.

  • Inbox → Mother project → Sub-project → Platform version: the full spine of a creative workflow, with a fast-track that lets a good idea skip straight into an active sub-project when you know where it belongs.
  • Storyboards with a list view and a board view, drag-to-reorder, per-shot reference images, XLSX export, and a printable version for on-set use.
  • Schedule and calendar: precise times, all-day dates, and fuzzy windows ("this week", "the weekend") coexist in one calendar; overdue is computed, not remembered, so nothing quietly rots.
  • Reminders and in-site notifications, deduplicated at the database layer so a restart never fires the same alert twice.
  • Execution records for shoots / re-shoots / screen recordings / writing sessions — attach the record to whichever project layer actually matches what you did.
  • Retrospectives, light or full; every field is optional. The structure is there to remind you, not to demand.
  • Attachments in four shapes: uploaded images (with thumbnails), path pointers (e.g. NAS/2026/Aug shoots/), external links, and text snippets. Soft-deleted files sit in a recycle bin for 30 days.
  • Field mode: mobile-first page for on-set use. If the network drops, edits queue in IndexedDB and sync when connectivity returns.
  • Full export: JSON + uploads bundle for takeout, from either the CLI or the settings page.
  • Backups: online SQLite backup on a schedule, and optional restic scripts for encrypted off-site copies with a real restore-drill runner.
  • MCP channel for AI agents (Claude, ChatGPT, Codex, …) — see the section below.

Design stance

  • Single user, single account. The data model carries a workspace column so a future multi-user version does not have to rebuild the schema, but everything in the shipped code assumes exactly one user.
  • No external services required. SQLite on disk, files on disk. No Redis, no message queue, no third-party auth. You can run it on a $5/month VPS.
  • Small footprint. Target is app 512 MiB / scheduler 256 MiB / (optional) reverse-proxy sidecar 128 MiB. A 2 GiB VM is enough.
  • The server does not build the frontend. The Vite bundle is built locally (or in CI) and shipped as pre-built files. Deployment machines never need Node.
  • Content over ceremony. The retro form has no required fields — the schema exists to remind you what to think about, not to gatekeep saving.

Tech stack

  • Backend: Python 3.12, FastAPI, SQLite (with uv for dependency management).
  • Frontend: React 19 + TypeScript, built with Vite.
  • Deployment: Docker Compose (single-host). Reverse-proxying and TLS are your call — Cloudflare Tunnel, Caddy, Nginx, Tailscale Funnel, or just SSH tunnels for local-only use all work.
  • Testing: pytest for the backend, vitest for the frontend.

MCP channel: connect AI agents

Yoru Studio exposes a Model Context Protocol (MCP) server so agents that speak MCP — Claude Desktop, ChatGPT desktop, Codex CLI, Claude Code, and others — can read from and (append to) your studio without you copy-pasting.

Eight tools total, all scoped to append-only writes with idempotency:

Read (5):

  • list_projects — mother-project list with counts.
  • get_project — one mother's full detail, including its sub-projects.
  • get_sub_project — one sub-project, with storyboard / execution records / retrospective folded in.
  • get_schedule — upcoming (14 or 30 days), all overdue, near-term fuzzy events.
  • get_inbox — pending or discarded inbox items.

Write (3, all append-only, all idempotent):

  • capture_inspiration — drop a spark into the inbox.
  • append_storyboard_shots — atomically add N shots to a video sub-project's storyboard.
  • append_execution_record — log a shoot / write session / test.

Every write tool takes an idempotency_key. Retry with the same key returns the first result; a different payload with the same key is a hard conflict. Nothing an agent does can silently overwrite work you already have.

Two auth paths behind the same /mcp endpoint (spec §5.1 of docs/spec/ in the source):

  • Static Bearer token for personal / single-agent use. You mint one long random string, store its sha256 in the environment, and give the token to the agent.
  • OAuth 2.0 with PKCE + Dynamic Client Registration for connectors that expect it (ChatGPT's connector is the current case that requires it).

Both paths can coexist. Both are optional — leave both unset and the /mcp route never mounts.

Quick start

Two paths depending on how you want to run it: from source (for development, or if you prefer to manage Python yourself), or via Docker Compose (for a stable single-host install).

From source

Requires Python 3.12 and uv, plus Node 20+ for the frontend.

# 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

Local mode binds to loopback and skips authentication for developer convenience. To try the auth flow locally, follow the "Enabling remote mode" section below.

Other CLI commands:

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

Run the tests:

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

Docker Compose

The docker-compose.yml in this repo defines three services: app (the FastAPI + built SPA), scheduler (a 60-second-tick loop that runs backups, reminders, retention), and cloudflared (a reference reverse-proxy sidecar — swap it for whatever fits your infrastructure).

Reverse-proxying / TLS is deliberately out of scope of the app: pick your own. Reasonable choices include:

  • Cloudflare Tunnel (the reference cloudflared service in docker-compose.yml, with the provisioning script in scripts/provision-cloudflare-tunnel.py).
  • Caddy or Nginx as a host-level reverse proxy, terminating TLS with your own certs.
  • Tailscale Funnel for private-first hosting.
  • Just SSH forward -L 8000 if you only want it on your own machine.

If you use Cloudflare Tunnel, either edit or remove the cloudflared service and unset STUDIO_TRUSTED_PROXY_IPS in .env.production. If you use another proxy, set STUDIO_TRUSTED_PROXY_IPS to your proxy's IP so real client IPs land in the audit log.

Deployment steps (once your Docker host is ready):

# 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 has a longer walkthrough covering the reference layout, the backup automation scripts under scripts/, and the operation lock the deploy and backup jobs share.

Enabling remote mode

Remote mode is what turns the app from "local dev with no auth" to "public URL behind a proxy with session cookies". Set at minimum:

  • STUDIO_MODE=remote
  • STUDIO_SESSION_SECRET — a random string, at least 32 characters.
  • STUDIO_AUTH_PASSWORD_HASH — the output of uv run studio hash-password.
  • STUDIO_ALLOWED_HOSTS — the exact hostname(s) the app will answer on (no wildcards; the app will refuse to boot with *).
  • STUDIO_TRUSTED_PROXY_IPS — if there is a reverse proxy in front, the IP(s) it uses to talk to the app.

The app refuses to boot in remote mode if any of the required secrets are missing or if allowed_hosts is empty — this is deliberate. There is no "silently open" configuration.

Configuration

Most values live in environment variables (production is Docker-friendly that way). A subset can also live in a TOML file loaded by --config or STUDIO_CONFIG — see config/config.example.toml for the shape.

Secrets are env-only by design: they are never read from the TOML config, so bundling the config file with a deployment can never leak them.

Variable Purpose Default
STUDIO_MODE local (loopback-only, no auth) or remote (session cookies + password) local
STUDIO_BIND_HOST Address the server binds to 127.0.0.1
STUDIO_BIND_PORT Port the server binds to 8000
STUDIO_ALLOWED_HOSTS Comma-separated list of hostnames accepted in Host: header (required in remote mode) (empty)
STUDIO_TRUSTED_PROXY_IPS Comma-separated list of proxy IPs whose CF-Connecting-IP / X-Forwarded-For to trust (empty)
STUDIO_SESSION_SECRET Random string ≥32 chars used to sign session cookies (required in remote mode) (empty)
STUDIO_AUTH_PASSWORD_HASH scrypt-hashed login password from studio hash-password (required in remote mode) (empty)
STUDIO_DATA_DIR Where the SQLite database lives ./data
STUDIO_UPLOADS_DIR Where uploaded attachments live <data-dir>/uploads
STUDIO_BACKUPS_DIR Where SQLite online backups are written ./backups
STUDIO_LOGS_DIR Where app logs go ./logs
STUDIO_BACKUP_STATE_DIR Optional read-only path where the host's backup jobs drop capacity.json for the in-app status widget (unset — status shows unknown)
STUDIO_UPLOAD_MAX_FILE_BYTES Per-file upload cap 26214400 (25 MiB)
STUDIO_UPLOAD_QUOTA_BYTES Total upload quota per mother-project subtree 2147483648 (2 GiB)
STUDIO_UPLOAD_MAX_IMAGE_PIXELS Decompression-bomb guard 40000000 (40M px)
STUDIO_RADAR_TOKEN_HASH sha256 hex of the intake channel's bearer token; unset disables the intake endpoint (unset)
STUDIO_MCP_TOKEN_HASH sha256 hex of the MCP static-bearer token; unset disables the static-Bearer path (unset)
STUDIO_MCP_OAUTH_ISSUER_URL Public URL that hosts the OAuth AS metadata; setting it wakes the OAuth path (unset)
STUDIO_MCP_OAUTH_ALLOWED_REDIRECT_HOSTS Comma-separated hostnames allowed in DCR redirect URIs (loopback always allowed) chatgpt.com
STUDIO_MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS OAuth access-token lifetime 3600
STUDIO_MCP_OAUTH_REFRESH_TOKEN_TTL_SECONDS OAuth refresh-token lifetime 2592000 (30 days)
STUDIO_MCP_OAUTH_CODE_TTL_SECONDS OAuth authorization-code lifetime 300

Generating hashed tokens

The intake endpoint and the MCP static-Bearer path both store sha256(token) — never the token itself — so a leaked .env.production yields nothing replayable.

# 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

External intake: hand your own feed to the inbox

There is an HTTP endpoint designed to receive items from an external feeder — an RSS scraper, a topic-radar tool, a scheduled scrape job, whatever ingests content on your behalf. The endpoint is generic: bring your own upstream, wire it here, and items land in the inbox where you triage them.

Endpoint: POST /api/inbox

Authentication: Authorization: Bearer <token>. The server compares sha256(token) against STUDIO_RADAR_TOKEN_HASH in constant time. If that env var is unset, the endpoint returns 401 to every Bearer call — the intake stays fully closed.

CSRF is not required on this path: the CSRF cookie defends against browser session replay, which is not a threat when the caller supplies its own bearer header.

Request body (JSON):

Field Type Notes
title string, required, ≤500 chars The inbox item title. Blank / missing → 400.
first_reaction string, optional Your one-line hot take.
links string, optional Free text — pasted URLs are fine.
radar_topic_id string, optional, ≤500 chars Your feeder's identifier for this topic. Second-strongest dedup key.
canonical_url string, optional, ≤2000 chars Canonical URL of the item. Third-strongest dedup key.
idempotency_key string, optional, ≤500 chars Per-delivery unique key. Strongest dedup key.

Dedup priority: idempotency_key > radar_topic_id > canonical_url. On a repeat delivery, the server returns the row that already exists rather than creating a second one — even if you had already discarded or converted that row. Re-delivery must not overturn your triage decision.

Response:

  • 201 Created — a brand-new row was inserted.
  • 200 OK — a repeat delivery was matched to an existing row (any status, including discarded / converted). Same body shape.
  • 400 Bad Request — missing / invalid title.
  • 401 Unauthorized — bad or missing bearer token, or intake not configured.

Response body:

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

Curl example:

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"
  }'

The field is nicknamed "radar" throughout the codebase because it was originally wired to an external content-radar tool; the endpoint itself is generic and works with any feeder that can speak HTTP.

License

Yoru Studio is licensed under GNU Affero General Public License, version 3, only (AGPL-3.0-only). See LICENSE for the full text.

In one sentence: you may self-host, use, and modify the code freely for your own creative work; if you run a modified version as a network service that other people interact with, you must offer them the source of that modified version. This is exactly what AGPL is designed to enforce — the "network use" clause (§13) makes the reciprocal obligation trigger on running it, not just on distributing it.

Expectations

This is a personal project. It exists because one creator needed it and decided to share it.

  • Not a product. There is no roadmap that anyone else is entitled to, no support SLA, and no promise that the next release will not break your setup.
  • Maintained on the author's own rhythm. Issues and pull requests are welcome, but replies come when they come.
  • You self-host it. No hosted version exists. There is no plan for one.
  • Data lives on your machine. Nothing calls home. Nothing is sent to a third party. That is the point of self-hosting; it is also the reason nobody is going to rescue your data if you lose it. Take backups.

If any of that reads as "not for me", that is the honest signal — please pick something else and no hard feelings.

Contributing

Bug reports are welcome. Please include enough detail that the bug can be reproduced against a clean checkout.

Feature requests: this project scopes itself intentionally small and adds features only after real use exposes a need. A feature request that reads like "here is what I actually hit trying to use the app" is far more likely to land than one that reads like "here is a nice thing to have".

Pull requests: for anything larger than a one-file bug fix, please open an issue first to check that the direction fits. AGPL-3.0-only means contributions must be compatible with that license — by opening a pull request you agree that your contribution is under the same terms as the rest of the project.

Attribution

Built by Yoru, Claude Fable 5, and GPT 5.6 Sol — the three of us.

from github.com/yoruuuchan/yoru-studio-oss

Установка Yoru Studio

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/yoruuuchan/yoru-studio-oss

FAQ

Yoru Studio MCP бесплатный?

Да, Yoru Studio MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Yoru Studio?

Нет, Yoru Studio работает без API-ключей и переменных окружения.

Yoru Studio — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Yoru Studio в Claude Desktop, Claude Code или Cursor?

Открой Yoru Studio на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Yoru Studio with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории communication