Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Both links expire **24 hours** after they are created. Save the project from Stu

| Tool | What it does | Annotations |
| --- | --- | --- |
| `get_capabilities` | Lists the formats, layer types, layer properties and effect presets that Studio supports, with the Studio revision they come from. | read-only |
| `get_capabilities` | Lists the formats, layer types, layer properties and effect presets that Studio supports, how to reference files the user keeps in Studio (photos, logo, video clips) by id, and the Studio revision they come from. | read-only |
| `create_hcmotion` | Validates a finished composition and packages it as a `.hcmotion` file. Invalid input returns `created: false` with the errors to fix and stores nothing. Valid input returns `open` and `download` links (24 h). | write (creates a temporary file), not destructive |
| `get_template` | Returns the full design of a Studio gallery template by its id and format (brief, scenes, HTML scene documents and layers), to use as a design reference. | read-only |
| `validate_json` | Checks a composition, design or improvement patch and returns errors and warnings. Preliminary: it does not prove a correct render. | read-only |
Expand Down
4 changes: 2 additions & 2 deletions api/mcp.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hypercodemotion/mcp",
"version": "0.6.2",
"version": "0.7.0",
"private": true,
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion server.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "io.github.jasp402/hypercodemotion-mcp",
"title": "HyperCodeMotion MCP",
"description": "Validate AI-made motion graphics and package them as editable HyperCodeMotion projects.",
"version": "0.6.2",
"version": "0.7.0",
"repository": {
"url": "https://github.com/jasp402/HyperCodeMotion-MCP",
"source": "github"
Expand Down
2 changes: 1 addition & 1 deletion skills/hypercodemotion/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Load only the applicable reference files instead of pasting all reference conten

## Non-negotiable truthfulness

`validate_json` currently performs **structural preflight** against a source-pinned capability snapshot, returns `fullyValidated: false`, and **cannot verify appearance or guarantee Studio import/rendering**. Images in a prompt are references; they are not automatically embedded in JSON. Never invent asset IDs, live external image URLs, render results, tool calls, or successful tests.
`validate_json` currently performs **structural preflight** against a source-pinned capability snapshot, returns `fullyValidated: false`, and **cannot verify appearance or guarantee Studio import/rendering**. Images in a prompt are references; they are not automatically embedded in JSON. The exception is files the user lists by id (photos, cut-outs, a logo, videos kept in their Studio): reference those ids in `image` and `video` layers as [the contract](references/composition.md) describes. Never invent other asset IDs, live external image URLs, render results, tool calls, or successful tests.

## Output contract

Expand Down
16 changes: 15 additions & 1 deletion skills/hypercodemotion/references/composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,26 @@ This is an illustrative sample, not evidence of a successful real render.

## Layers and timing

Supported layer types from the current contract snapshot: `text`, `rect`, `ellipse`, `polygon`, `particles`, `image`, `code`, `camera`, `svg`, `ui`, `cursor`, `html`, `component`. Query `get_capabilities` for properties per type. Layer order determines draw order (earliest at back). Use stable, unique IDs. `start` and `end` denote visible interval in composition seconds, `[start,end)`. `parent` refers to another layer ID; `clip` can mask children where supported; `fixed` remains screen-fixed through camera or scene transitions.
Supported layer types from the current contract snapshot: `text`, `rect`, `ellipse`, `polygon`, `particles`, `image`, `video`, `code`, `camera`, `svg`, `ui`, `cursor`, `html`, `component`. Query `get_capabilities` for properties per type. Layer order determines draw order (earliest at back). Use stable, unique IDs. `start` and `end` denote visible interval in composition seconds, `[start,end)`. `parent` refers to another layer ID; `clip` can mask children where supported; `fixed` remains screen-fixed through camera or scene transitions.

A property value may be fixed (number/string), `{"kf":[{"t":0,"v":0},{"t":1,"v":100,"ease":"easeOutCubic"}]}`, or `{"expr":"..."}`. Keyframe timestamps are global composition seconds. Spring targets may be specified by `spring` where supported by Studio; check actual accepted names.

Optional composition structures include `scenes` (sequential periods and transitions), `docs` (HTML templates), `assets`, `audio`, `camera`, `params`, `plan`, `markers` and `components`. Do not fabricate structures: inspect Studio code for advanced fields.

## The user's own files (photos, cut-outs, logo, videos)

When the request lists the user's files by id (Studio's «Tus materiales», e.g. `"video-1" · «playa.mp4» · video · 1920×1080 · 12.4 s`), those files already live in the user's Studio and are filled in by id when the project opens. Nothing is uploaded to the MCP.

- Image (photo, cut-out or logo): an `image` layer with `"asset": "<id>"` and its own proportion, or `<img src="asset:<id>">` inside an HTML scene.
- Video: a `video` layer. `props.from` is the second of the file where the clip starts; the clip lasts `end - start`. Keep `from + (end - start)` within the file's duration. Videos play without sound (sound lives in Studio's audio channels). For full frame use the canvas `w`/`h`: the video is cropped to cover, never stretched.

```json
{"id":"clip1","name":"Clip · playa","type":"video","start":2,"end":5.5,"props":{"asset":"video-1","from":6.4,"x":540,"y":960,"w":1080,"h":1920}}
```

- Do not add these ids to `assets` and never use a URL for them. Use only ids the user listed; an unknown id opens as an empty slot.
- Videos usually arrive as a sheet of frames with their seconds (`materiales-para-claude.png`): pick each clip's `from` from it.

## Editing an existing project

Start from the user's composition and return all of it with the changes applied. Keep untouched layers, IDs, docs, assets and audio byte-for-byte; change only what was asked. Keep `start`/`end` and keyframe times inside `duration`. Deliver it through `create_hcmotion` like a new project.
Expand Down
2 changes: 1 addition & 1 deletion skills/hypercodemotion/references/html-scenes.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Use HTML scenes for precise editorial layouts, dashboards and typographic compos

## Image assets

A photo in a user prompt is a **reference**, not automatically an embedded asset. Composition `assets` maps asset IDs to actual data URLs; image layers must use an asset/source format Studio supports. Verify IDs and actual media availability instead of inventing a Pexels image URL, claiming a fetch succeeded, or fabricating a brand logo.
Files the user lists by id stay in their Studio: use `<img src="asset:<id>">` or an `image`/`video` layer with that id (see [composition](composition.md)). Any other photo in a user prompt is a **reference**, not automatically an embedded asset. Composition `assets` maps asset IDs to actual data URLs; image layers must use an asset/source format Studio supports. Verify IDs and actual media availability instead of inventing a Pexels image URL, claiming a fetch succeeded, or fabricating a brand logo.

An external asset may require Studio to fetch and embed it. If not available, supply an explicitly labelled placeholder and state what is required to complete import. Avoid remote dependencies for reproducible compositions when assets can be bundled.

Expand Down
4 changes: 4 additions & 0 deletions src/contract.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import contract from "./studio-contract.json" with { type: "json" };
import { validateComposition } from "./validator.js";

/** The user's own files («Tus materiales» in Studio): referenced by id, never uploaded here. */
const USER_MATERIALS = "Files the user listed by id in their request (photos, cut-outs, a logo, videos) stay in Studio and are filled in by id when the project opens. Reference them with props.asset = <id>: an image as an \"image\" layer (or <img src=\"asset:<id>\"> in an HTML scene), a video as a \"video\" layer whose props.from is the second of the file where the shown clip starts; the clip lasts end - start and plays without sound. Do not put them in \"assets\" or as URLs, and use only ids the user gave.";

export function getCapabilities() {
return { contractVersion: "studio-snapshot-1", source: contract.source,
sourceSchemaSha: contract.sourceSchemaSha, sourcePresetsSha: contract.sourcePresetsSha,
formats: ["composition", "design", "improvement"],
layerProperties: contract.types, presetSlots: contract.presets,
userMaterials: USER_MATERIALS,
fullyValidated: false };
}

Expand Down
2 changes: 1 addition & 1 deletion src/hcmotion.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
const MAGIC = [0x48, 0x43, 0x4d];
const HEAD = 48;
const LAYER_TYPES = new Set(["text","rect","ellipse","polygon","particles","image","code","camera","svg","ui","cursor","html","component"]);
const LAYER_TYPES = new Set(["text","rect","ellipse","polygon","particles","image","video","code","camera","svg","ui","cursor","html","component"]);
const isObj = x => !!x && typeof x === "object" && !Array.isArray(x);
const num = x => typeof x === "number" && Number.isFinite(x);

Expand Down
29 changes: 26 additions & 3 deletions src/studio-contract.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"source": "jasp402/HyperCodeMotion",
"sourceSchemaSha": "c4088722b2919d164401a944e5a88e633dbbec4b",
"sourcePresetsSha": "57593271af877058bd0b9a1b77a063dd013564b7",
"generatedAt": "2026-10-13",
"sourceSchemaSha": "fb6f8502cb87baf9980e793f619b4c8de32e65c2",
"sourcePresetsSha": "3b1ce95290d180668bf102b2da64ae9a75d08c8b",
"generatedAt": "2026-10-20",
"types": {
"text": [
"text",
Expand Down Expand Up @@ -209,6 +209,29 @@
"rOpacity",
"rDelay"
],
"video": [
"asset",
"from",
"w",
"h",
"radius",
"reveal",
"x",
"y",
"scale",
"scaleX",
"scaleY",
"rotation",
"opacity",
"depth",
"rotateX",
"rotateY",
"perspective",
"glow",
"glowColor",
"blur",
"blend"
],
"code": [
"w",
"h",
Expand Down
Loading
Loading