Content
# MVR — My Video Resource
**A self-hosted MCP server and local dashboard for building a reusable AI-video reference library.**
MVR stores named **Elements** — characters, products, locations, styles — as structured, versioned reference images (not just text prompts), and hands that context to any MCP-compatible agent through `@tags`. It is a **creative library and agent-context layer**, not a video-generation model: the connected agent still does the actual generation, using its own provider integration.

---
## Table of contents
- [Why MVR](#why-mvr)
- [How it works](#how-it-works)
- [Core concepts](#core-concepts)
- [Quickstart](#quickstart)
- [MCP tools](#mcp-tools)
- [HTTP API](#http-api)
- [Storage](#storage)
- [Security and operations](#security-and-operations)
- [Current scope](#current-scope)
---
## Why MVR
AI video prompts repeat the same character, product, and location descriptions constantly. A small wording difference between scenes is often enough to make a character's face or a product's shape drift. Standard practice among AI video creators is to build a **reference sheet** for every recurring asset — a locked close-up of a face, a front/back turnaround of a body, a multi-angle sheet for a product — and reuse those exact images in every scene instead of re-describing them in words.
MVR turns that practice into a small, self-hosted service:
- Reference images live in one place, organized by **named Element** and **slot** (angle/view), not scattered across chat history.
- Each Element **kind** (character, prop, location, style) comes with a seeded checklist of which angles it needs, based on what actually keeps AI video output consistent.
- Any MCP-compatible agent — Claude Desktop, Claude Code, Cursor, or a custom agent — can query the same library, so the work of building a character or product sheet is done once and reused everywhere.
---
## How it works
### Architecture
```mermaid
flowchart TD
Creator(["🎬 Creator"])
UI["Local Dashboard\nlocalhost:8080"]
Agent["MCP-compatible Agent\nClaude · Cursor · custom"]
MVR["MVR Server\nMCP tools + REST API"]
DB[("SQLite Vault\nElements · Configs · Providers")]
Assets[("Asset Storage\nlocal volume or S3")]
Provider["External AI Provider\nimage / video generation"]
Output(["🎥 Generated video / image"])
Creator -->|manages library, keys| UI
Creator -->|"chats: use @hero + @headphones"| Agent
UI -->|REST, bearer token| MVR
Agent -->|MCP tools, stdio or HTTP| MVR
MVR --> DB
MVR --> Assets
Agent -->|its own provider integration| Provider
Provider --> Output
Output -.->|shown back to| Creator
```
**The key boundary:** MVR never calls the production generation API itself. It resolves `@tags` into reference assets and a saved model preference, and returns that to the agent — the agent performs the actual call to Higgsfield, OpenAI, or whichever provider it's configured with.
### A typical request, step by step
```mermaid
sequenceDiagram
participant C as Creator
participant A as AI Agent
participant M as MVR Server
participant P as External Provider
C->>A: "Generate a shot of @hero putting on @headphones"
A->>M: resolve_prompt(text)
M-->>A: resolved_text, elements_used (with asset paths),<br/>completeness_warnings
A->>M: get_active_model("video-generation")
M-->>A: provider, model, settings
A->>P: generate(resolved prompt, references, settings)
P-->>A: generated video / image
A-->>C: result, plus any "you're missing @hero's back view" reminder
```
If an Element referenced in the prompt is missing a required reference angle, MVR does **not** block the request — it attaches a `completeness_warnings` entry so the agent can pass the reminder along, and the creator decides whether to fix it now or accept the risk.

### How an Element is structured
An Element is not one text field — it's a named record plus a set of **slots**, where each slot is a real, versioned reference image. The required slots depend on the Element's `kind`:
```mermaid
flowchart LR
subgraph Element["Element: @hero (kind: character)"]
direction LR
S1["face_closeup\n✅ uploaded"]
S2["body_front\n✅ uploaded"]
S3["body_back\n⬜ missing"]
end
Element --> Check{"check_element_completeness"}
Check -->|missing required slot| Warn["completeness_warnings\nreturned to agent"]
Check -->|all required slots present| OK["Element ready to use"]
```
| Kind | Required slots | Optional slots |
|---|---|---|
| `character` | `face_closeup`, `body_front`, `body_back` | variants via `variantOf` |
| `prop` | `front`, `three_quarter` | `lateral`, `medial`, `top`, `rear`, `bottom` |
| `location` | `establishing_3_4` | `schematic_map` |
| `style` | — | `reference_image` |
| `audio`, `other` | — | — |
These recipes are seeded on first run and can be inspected at any time via `get_kind_template`.
<!-- TODO: screenshot — the dashboard's slot uploader for a single Element, showing the progress indicator (e.g. "2/3 slots complete") -->

---
## Core concepts
| Concept | What it is |
|---|---|
| **Element** | A reusable character, product, location, style, or other creative asset, referenced as `@tag`. |
| **Element asset** | A real, versioned PNG stored for one slot of an Element (e.g. `face_closeup`). Locking an asset prevents accidental overwrites. |
| **Kind template** | The seeded recipe of required/optional slots and best practices for a given `kind`. |
| **Prompt format template** | The model-specific structure a resolved prompt should follow (e.g. the Style/Characters/Scene/CUT blocks Seedance 2.0 expects). Kept separate from Element recipes, so a new model only needs a new format entry. |
| **Model config** | A saved provider + model + settings preference for one task, e.g. `video-generation → Seedance → 2.0`. |
| **Provider** | An encrypted API key stored for future evaluation features. Not used for production generation calls today. |
| **Vault** | The local SQLite database backing all of the above. |
---
## Quickstart
### Local
```bash
cp .env.example .env
# Replace MVR_ENCRYPTION_KEY and MVR_AUTH_TOKEN with your own secrets.
npm install
npm run build
npm start
```
```bash
npm run dev # watch mode
npm run build # TypeScript build
npm test # Vitest suite
npm run stdio # run only the stdio MCP server
```
### Docker
```bash
cp .env.example .env
# Replace MVR_ENCRYPTION_KEY and MVR_AUTH_TOKEN.
docker compose up --build
```
Docker Compose persists the database in the `mvr-data` volume and bind-mounts `./data/assets` to `/app/data/assets`, so reference images stay inspectable on the host.

### Connecting an MCP client
**stdio** (Claude Desktop, Claude Code, and similar):
```json
{
"mcpServers": {
"mvr": {
"command": "node",
"args": ["/absolute/path/to/MVR/dist/index.js", "--stdio"],
"env": {
"MVR_DB_PATH": "/absolute/path/to/MVR/data/mvr.db",
"MVR_ASSET_PATH": "/absolute/path/to/MVR/data/assets",
"MVR_ENCRYPTION_KEY": "your-long-random-secret"
}
}
}
}
```
```text
Authorization: Bearer your-long-random-token
Accept: application/json, text/event-stream
```
<!-- TODO: screenshot — Claude Desktop's MCP settings panel showing "mvr" connected -->

---
## MCP tools
| Tool | Purpose |
|---|---|
| `list_elements` | List Elements. |
| `get_element` | Get an Element and its summary-version history. |
| `create_element` | Create an Element (`tag`, `kind`, `displayName`, `summary`, `variantOf?`, `metadata?`). |
| `update_element` | Update Element metadata or summary. |
| `delete_element` | Permanently delete an Element and its records. |
| `upload_element_asset` | Upload a PNG to a named slot (`tag`, `slotName`, `image`, `sourcePrompt?`, `locked?`). |
| `list_element_assets` | Return the current version of every filled slot. |
| `check_element_completeness` | Return `{ complete, missing: [{ slot, required, rationale }] }`. |
| `get_kind_template` | Get a kind's slot recipe and best practices. |
| `resolve_prompt` | Resolve `@tags` into references, plus non-blocking completeness warnings. |
| `get_prompt_format` | Get a model-specific prompt format by provider and model. |
| `list_model_configs` / `set_model_config` / `get_active_model` / `delete_model_config` | Manage per-task model preferences. |
| `list_providers` | List provider names with masked API keys. |
---
## HTTP API
All endpoints are under `/api` and require the bearer token when `MVR_AUTH_TOKEN` is set.
| Method | Endpoint | Purpose |
|---|---|---|
| `GET`, `POST` | `/elements` | List or create Elements. |
| `DELETE` | `/elements/:tagOrId` | Delete an Element. |
| `GET`, `POST` | `/elements/:tagOrId/assets` | List assets or upload one PNG. |
| `GET` | `/elements/:tagOrId/completeness` | Check required slots. |
| `GET` | `/kind-templates/:kind` | Get a seeded kind template. |
| `GET` | `/prompt-formats?provider=Seedance&model=2.0` | Get a prompt format template. |
| `GET`, `POST` | `/providers` | List masked providers or save a key. |
| `DELETE` | `/providers/:name` | Delete a provider. |
| `GET`, `POST` | `/model-configs` | List or save model configurations. |
| `DELETE` | `/model-configs/:task` | Delete a model configuration. |
---
## Storage
**Local (default):**
```dotenv
MVR_STORAGE_DRIVER=local
MVR_ASSET_PATH=/app/data/assets
MVR_ASSET_IMPORT_PATH=/app/data/imports
```
**S3-compatible:**
```dotenv
MVR_STORAGE_DRIVER=s3
MVR_S3_BUCKET=your-bucket
MVR_S3_REGION=us-east-1
MVR_S3_ACCESS_KEY_ID=...
MVR_S3_SECRET_ACCESS_KEY=...
MVR_S3_ENDPOINT=https://your-s3-compatible-endpoint # optional
```
The SQLite database defaults to `data/mvr.db` locally and `/app/data/mvr.db` in Docker. Startup automatically migrates an existing Phase 1 database and seeds any missing templates without touching existing ones.
---
## Security and operations
| Area | Behavior | Operator responsibility |
|---|---|---|
| Provider keys | AES-256-GCM encrypted at rest; list output is masked. | Set and protect `MVR_ENCRYPTION_KEY` — changing it makes existing keys unreadable. |
| Authentication | API and MCP routes require a bearer token when configured. | Set `MVR_AUTH_TOKEN` before exposing MVR beyond a trusted network. |
| Assets | Real reference PNGs live on the configured local/S3 backend. | Back them up and control filesystem/bucket access. |
| File-reference uploads | Paths are constrained beneath `MVR_ASSET_IMPORT_PATH`. | Treat that directory as trusted local input only. |
| Generation | MVR never calls production image/video providers itself. | The connected agent owns provider calls and their security. |
---
## Current scope
**Implemented**
- Multi-slot Elements with real, versioned, optionally locked PNG assets
- Completeness checks and non-blocking prompt-resolution warnings
- Seeded kind recipes and a seeded Seedance 2.0 prompt format
- Local and S3-compatible asset storage
- MCP over stdio and authenticated Streamable HTTP
- Local dashboard, encrypted provider-key storage, per-task model configurations
**Not yet implemented**
- Video/image generation orchestration
- Automated evaluation runs or evaluation-driven template updates
- Multi-user authentication and authorization
See `ROADMAP.md` for what's planned next.
---
## License
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available