{"schema_version":"verify.report.v1","generated_at":"2026-09-12T12:09:43.111722+00:00","snapshot_id":"trustsnap_cbe6c78c4b9bb52c","server":{"namespace":"awesome-clipkit-video","name":"clipkit","title":"clipkit-video/clipkit","description":"clipkit-video/clipkit clipkit-video/clipkit](https://glama.ai/mcp/servers/clipkit-video/clipkit) 🎖️ 📇 ☁️ 🏠 🍎 🪟 🐧 - Compose motion-graphics videos from a single JSON document (Clipkit Protocol): 15 tools to author, validate, tweak per element, and preview PNG stills in-chat, plus captions and templated promos; hands the human a free browser editor link, with a deterministic GPU runtime for MP4 renders. Hosted at `https://www.clipkit.dev/mcp` or local `npx @clipkit/mcp-server`.","homepage_url":"https://github.com/clipkit-video/clipkit","docs_url":"https://github.com/clipkit-video/clipkit","icon_url":null,"support_url":"https://github.com/clipkit-video/clipkit","remote_url":"https://www.clipkit.dev/mcp","server_card_url":null,"latest_version":null,"current_status":"healthy","current_score":59.9,"transport_type":"streamable-http","has_oauth":false,"has_dcr":false,"has_prompts":false,"tool_count":15,"current_validation_schema_version":"8058defc70ca932c","last_validated_at":"2026-09-12T09:33:34.449596+00:00","registry_source":"awesome_mcp_servers","registry_identifier":"awesome_mcp_servers:clipkit-video/clipkit","canonical_identifier":null,"current_score_components":{"auth_operability_score":2.0,"error_contract_score":1.0,"rate_limit_semantics_score":2.0,"schema_completeness_score":3.0,"backward_compatibility_score":4.0,"slo_health_score":3.0,"security_hygiene_score":3.0,"task_success_score":4.0,"trust_confidence_score":3.0,"abuse_noise_ratio_score":4.0,"prompt_contract_score":0.0,"resource_contract_score":4.0,"discovery_metadata_score":2.0,"registry_consistency_score":2.0,"installability_score":4.0,"session_semantics_score":4.0,"tool_surface_design_score":4.0,"result_shape_stability_score":4.0,"oauth_interop_score":0.0,"recovery_semantics_score":0.0,"maintenance_signal_score":2.0,"adoption_signal_score":2.0,"freshness_confidence_score":4.0,"transport_fidelity_score":4.0,"spec_recency_score":2.0,"session_resume_score":3.0,"step_up_auth_score":0.0,"transport_compliance_score":2.0,"utility_coverage_score":0.0,"advanced_capability_coverage_score":1.0,"connector_publishability_score":1.0,"tool_snapshot_churn_score":4.0,"connector_replay_score":4.0,"request_association_score":0.0,"interactive_flow_safety_score":4.0,"action_safety_score":1.0,"official_registry_presence_score":2.0,"safety_transparency_score":2.0,"tool_capability_clarity_score":4.0,"destructive_operation_safety_score":1.0,"egress_ssrf_resilience_score":1.0,"execution_sandbox_safety_score":4.0,"data_exfiltration_resilience_score":3.0,"least_privilege_scope_score":1.0,"secret_handling_hygiene_score":3.0,"dependency_supply_chain_signal_score":0.0,"input_sanitization_safety_score":3.0,"tool_namespace_clarity_score":4.0},"capability_taxonomy":[],"machine_summary":{},"taxonomy_tags":[],"score_decomposition":[],"validation_diff":null,"tool_snapshot_diff":null,"connector_replay":{},"request_association":{},"production_readiness":{"code":"needs_remediation","label":"Needs remediation","reason":"Current validation evidence shows operational or discovery gaps that should be fixed first.","badge":"score-low","critical_alerts":0,"high_or_critical_alerts":0,"degraded_by_active_alerts":false},"recommended_for":[],"history_summary":{},"validation_timeline":[],"evidence_confidence":{"score":88.75,"label":"high","reason":"Based on 11 recent validations, 15 captured checks, and validation age of 2.6 hours.","live_check_count":15,"validation_age_hours":2.6,"basis":{"evidence_bearing_validations":11,"affirmative_live_check_count":15,"validation_age_hours":2.6,"freshness_threshold_hours":24}},"incident_feed":[],"disputes":[],"remediations":[],"client_remediation_modes":[],"client_profiles":[],"client_readiness_verdicts":[],"publishability_policy_profiles":[],"compatibility_fixtures":[],"install_snippets":{},"aliases":[],"raw_evidence":{"checks":{"probe_noise_resilience":{"status":"ok","latency_ms":192.32,"details":{"url":"https://www.clipkit.dev/robots.txt","http_status":200,"headers":{"content-type":"text/plain","set-cookie":"ck_sid=7c826971-cce3-40c7-956a-3633b21da0b4; Path=/; Expires=Thu, 11 Mar 2027 09:33:31 GMT; Max-Age=15552000; Secure; HttpOnly; SameSite=lax","strict-transport-security":"max-age=63072000"},"validation_disallowed":false,"consent_error":null}},"server_card":{"status":"error","latency_ms":52.49,"details":{"url":"https://www.clipkit.dev/.well-known/mcp/server-card.json","error":"Client error '404 Not Found' for url 'https://www.clipkit.dev/.well-known/mcp/server-card.json'\nFor more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404"}},"oauth_protected_resource":{"status":"error","latency_ms":83.71,"details":{"url":"https://www.clipkit.dev/.well-known/oauth-protected-resource","error":"Client error '404 Not Found' for url 'https://www.clipkit.dev/.well-known/oauth-protected-resource'\nFor more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404"}},"oauth_authorization_server":{"status":"missing","latency_ms":null,"details":{"reason":"no_authorization_server"}},"openid_configuration":{"status":"missing","latency_ms":null,"details":{"reason":"no_authorization_server"}},"initialize":{"status":"ok","latency_ms":237.44,"details":{"url":"https://www.clipkit.dev/mcp","payload":{"result":{"protocolVersion":"2025-03-26","capabilities":{"tools":{"listChanged":true},"resources":{"listChanged":true}},"serverInfo":{"name":"clipkit","version":"0.0.0"},"instructions":"Clipkit MCP — author Clipkit Protocol videos via tools, and read the canonical authoring docs as resources. BEFORE composing, call read_docs for the authoring guide (read_docs topic:\"protocol\" for field semantics, topic:\"brand\" for brand-correct visuals). For exact field names + types call get_schema (optionally with an element_type). These are TOOLS, not resources, so you can read them directly. TWO WAYS TO BUILD — pick by the brief, do NOT default to one: (A) AUTHOR IT YOURSELF — write a full source JSON and call set_project. This is the full creative canvas: every element type (text, image, video, shape, audio, group, caption, particles) plus effects, 3D camera, lighting, gradients, keyframe animation, and expressions. Use this for anything specific, original, or not a stock promo — most briefs. (B) create_promo — a FAST path that assembles a conventional promo/intro/product/data video from prebuilt scenes (it handles camera/glass/lighting/motion/layout). Good when a template fits; it is ONE option, not the default. Mix scene types and fit the structure to the content — do NOT reflexively make a glass-orb hero + CTA. TWEAK an existing project with add_element / edit_element / delete_element. SEE YOUR WORK — you cannot otherwise see the video: call preview_still for a frame, and validate_project / describe_project for a structural read-back with render-time warnings. The loop is edit → preview_still → fix. CAPTIONS: transcribe_to_captions turns a media url (or local file) into a timed caption. MEDIA: to use an image/video/audio that is not already a stable public URL (or to make a link durable so it cannot rot), call ingest_asset to host it and get a stable url for an element's `url`. DELIVER: ALWAYS deliver by calling open_in_editor and giving the user that free link — it opens the project in the editor where they preview and render the video themselves in the browser. This is the default and almost always what you want. load_project re-imports a shared project by id/URL. Do NOT call render_video unless the user EXPLICITLY asks you to render/export a finished MP4 file for them; it is the paid cloud path (consumes credits, needs CLIPKIT_API_KEY) and is not offered on the hosted connector. PROJECT IDS: create_project / set_project / create_promo / load_project return a project_id; pass it to EVERY subsequent tool call (get_project / edit_element / preview_still / validate_project / set_project revisions / render_video / …). REVISIONS: one video = ONE project. When the user asks for changes, update the SAME project_id (set_project with project_id, or edit_element) — never create a second project for an iteration; that strands the previous version and breaks the editor link you already shared. Only the local single-project stdio server may omit project_id."},"jsonrpc":"2.0","id":1},"http_status":200,"headers":{"content-type":"text/event-stream","strict-transport-security":"max-age=63072000"}}},"protocol_version_probe":{"status":"warning","latency_ms":null,"details":{"claimed_version":"2025-03-26","validator_protocol_version":"2025-03-26","latest_known_version":"2025-11-25","releases_behind":2,"lag_days":244}},"tools_list":{"status":"ok","latency_ms":212.53,"details":{"url":"https://www.clipkit.dev/mcp","payload":{"result":{"tools":[{"name":"read_docs","title":"Read the Clipkit authoring docs","description":"Return a canonical Clipkit doc as text. topic \"card\" = the ~8KB compact authoring card — the recommended context for authoring; \"pattern-data-viz\" / \"pattern-cinematic-ui\" / \"pattern-ui-screencast\" = ~4-5KB archetype pattern cards (proven idioms: count-ups and bar rows; product hero shots with camera rigs; faked app UI with typing/cursor/clicks) — load ONE alongside the card when the brief matches its archetype; \"agents\" = the full authoring guide (fetch only when the card doesn't cover a need); \"protocol\" = the formal field spec; \"brand\" = brand reference. (Same docs offered as MCP resources, exposed as a tool so you can read them directly — resources are not always model-readable.)","inputSchema":{"type":"object","properties":{"topic":{"type":"string","enum":["agents","protocol","brand","card","pattern-data-viz","pattern-cinematic-ui","pattern-ui-screencast"],"description":"Which doc. Default \"agents\"."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Read the Clipkit authoring docs","readOnlyHint":true,"openWorldHint":false,"destructiveHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"get_schema","title":"Get the Clipkit JSON Schema (exact fields)","description":"Return the authoritative JSON Schema for a Clipkit Source — exact field names, types, and enums, generated from the protocol. Call with no argument for the full Source schema, or with element_type (e.g. \"text\", \"shape\", \"particles\") for just that element's fields (much smaller). Use this when authoring with set_project / add_element so you never guess a field.","inputSchema":{"type":"object","properties":{"element_type":{"type":"string","enum":["video","image","text","shape","audio","group","caption","particles"],"description":"Limit to one element type's fields (e.g. \"text\"). Omit for the full Source schema (large)."}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Get the Clipkit JSON Schema (exact fields)","readOnlyHint":true,"openWorldHint":false,"destructiveHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"create_project","title":"Create a new Clipkit project","description":"Create a new, blank Clipkit project with the given dimensions and duration, and return its project_id. Defaults: 1920×1080, 10 seconds, 30 fps, output_format \"mp4\". Call this first when starting a new video. Pass an existing project_id to reset that project to blank; omit it to start a fresh project (note the returned id for subsequent tools).","inputSchema":{"type":"object","properties":{"width":{"type":"integer","exclusiveMinimum":0,"description":"Composition width in pixels. Default 1920."},"height":{"type":"integer","exclusiveMinimum":0,"description":"Composition height in pixels. Default 1080."},"duration":{"type":"number","exclusiveMinimum":0,"description":"Composition duration in seconds. Default 10."},"frame_rate":{"type":"number","exclusiveMinimum":0,"description":"Frame rate. Default 30."},"output_format":{"type":"string","enum":["mp4","gif"],"description":"Output container/codec. Default \"mp4\"."},"background_color":{"type":"string","description":"Hex color, e.g. \"#000000\"."},"output_dither":{"anyOf":[{"type":"boolean"},{"type":"object","properties":{"enabled":{"type":"boolean"},"amplitude":{"type":"number","minimum":0,"maximum":8},"pattern":{"type":"string","enum":["bayer","noise"]}},"additionalProperties":true}],"description":"Anti-banding output dither — DEFAULT ON, leave unset. Pass false for byte-exact undithered output, or { amplitude, pattern } to tune. Stops gradients / soft shadows / blur from showing 8-bit banding; deterministic."},"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Create a new Clipkit project","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"project_id":{"type":"string","description":"Id of the created project — pass to subsequent tools."},"width":{"type":"number"},"height":{"type":"number"},"duration":{"type":"number"},"frame_rate":{"type":"number"},"output_format":{"type":"string"}},"required":["project_id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"get_project","title":"Get the current Clipkit project JSON","description":"Return the full current Clipkit source as JSON. Use this to inspect the project, pass it to a render pipeline, or compose follow-up edits.","inputSchema":{"type":"object","properties":{"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Get the current Clipkit project JSON","readOnlyHint":true,"openWorldHint":false,"destructiveHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"describe_project","title":"Describe the current project in plain language","description":"Return a compact, human-readable summary of the current project — dimensions, fps, duration, an element breakdown by type, a per-track timeline (paint order low→high), and render-time warnings. Much cheaper to read than get_project's full JSON; use it to orient yourself or sanity-check structure without dumping the whole source.","inputSchema":{"type":"object","properties":{"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Describe the current project in plain language","readOnlyHint":true,"openWorldHint":false,"destructiveHint":false},"execution":{"taskSupport":"forbidden"}},{"name":"set_project","title":"Replace the entire Clipkit project","description":"Replace the entire project with the given source JSON, returning its project_id. This is the PRIMARY way to build: use it to create a composition or to add/change many elements at once. (To tweak a single element in an existing project, use edit_element / add_element / delete_element instead.) REVISION RULE: if you are iterating on a video that already exists in this conversation — the user asked for changes, you are fixing your own work — you MUST pass its project_id so the project is updated in place and keeps one editor link and its version history. Omitting project_id creates a NEW, unrelated project and strands the old one; only omit for a genuinely different video. The input is validated against the @clipkit/protocol before being accepted; invalid inputs return an error. Shape: { width, height, duration, frame_rate, output_format, background_color?, fonts?, camera?, lights?, elements:[…] }; every element has a `type` plus base fields (id, x, y, width, height, time, duration, track, opacity, rotation, animations, keyframe_animations) and type-specific fields. For exact field names + types call get_schema (optionally with an element_type) — the runtime ignores unrecognized keys, and this tool flags any it does not recognize.","inputSchema":{"type":"object","properties":{"source":{"type":"object","additionalProperties":{},"description":"A full Clipkit source object."},"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"required":["source"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Replace the entire Clipkit project","readOnlyHint":false,"destructiveHint":true,"openWorldHint":false},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"project_id":{"type":"string"},"element_count":{"type":"number"},"width":{"type":"number"},"height":{"type":"number"},"duration":{"type":"number"}},"required":["project_id","element_count"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"add_element","title":"Add an element to the current project","description":"Append a single element to an existing project — a TWEAK, e.g. dropping in one more caption or shape. By default it is added at the top level; pass parent_id to add it INTO a group (nested). The element is any valid schema element: video, image, text, shape, audio, group, caption, or particles. To create a composition or add several elements at once, build the JSON and use set_project instead. The new element is validated as part of the project as a whole before being added. Call get_schema(element_type) for the exact per-type fields; unrecognized keys are flagged.","inputSchema":{"type":"object","properties":{"element":{"type":"object","additionalProperties":{},"description":"A Clipkit element object. Must include `type`."},"parent_id":{"type":"string","minLength":1,"description":"Optional id of a group to add this element INTO (nested). Omit to add at the top level."},"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"required":["element"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Add an element to the current project","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"added_element_id":{"type":"string"},"element_type":{"type":"string"},"parent_id":{"type":"string"},"top_level_element_count":{"type":"number"}},"required":["top_level_element_count"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"edit_element","title":"Tweak one existing element (merge changed fields)","description":"Change fields on the element with the given id by merging in a partial element — only the keys you include change. The id may be any element ANYWHERE in the tree, including one nested inside a group (or its mask). Pass a whole nested value (e.g. a new `keyframe_animations` array) to replace that key; set a key to null to remove it. This is for TWEAKING an existing composition. To create a composition or change many elements at once, edit the JSON and call set_project instead. The result is re-validated before being accepted.","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"The id of the element to edit."},"patch":{"type":"object","additionalProperties":{},"description":"Partial element: the fields to change. Omitted keys are left as-is; a key set to null is removed."},"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"required":["id","patch"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Tweak one existing element (merge changed fields)","readOnlyHint":false,"destructiveHint":false,"openWorldHint":false},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"element_id":{"type":"string"},"changed_keys":{"type":"array","items":{"type":"string"}}},"required":["element_id","changed_keys"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"delete_element","title":"Delete one element by id","description":"Delete the element with the given id, anywhere in the tree (including one nested inside a group or its mask) — a tweak to an existing composition. (The project must keep at least one top-level element.)","inputSchema":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"The id of the element to delete."},"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"required":["id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Delete one element by id","readOnlyHint":false,"destructiveHint":true,"openWorldHint":false},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"deleted_element_id":{"type":"string"},"top_level_element_count":{"type":"number"}},"required":["deleted_element_id","top_level_element_count"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"validate_project","title":"Validate the current project (schema + render-time warnings)","description":"Run the @clipkit/protocol validator against the current project AND surface render-time warnings even when the JSON is valid — things that pass the schema but the runtime will silently drop or clip: emoji / non-ASCII text (the runtime font atlas is ASCII-only), elements that run past the composition end, a missing top-level duration. Run it before you share or render the project. For a fuller timeline read-back, use describe_project.","inputSchema":{"type":"object","properties":{"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Validate the current project (schema + render-time warnings)","readOnlyHint":true,"openWorldHint":false,"destructiveHint":false},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"valid":{"type":"boolean"},"error_count":{"type":"number"},"errors":{"type":"array","items":{"type":"string"}},"warnings":{"type":"array","items":{"type":"string"}},"unknown_keys":{"type":"array","items":{"type":"string"}},"element_count":{"type":"number"},"duration":{"type":"number"},"width":{"type":"number"},"height":{"type":"number"}},"required":["valid","error_count"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"preview_still","title":"Render one frame of the current project so you can SEE it","description":"Render a single frame of the current project to a PNG and return it as an image you can look at. This is how you check your work — in chat there is no other way to see what a composition actually looks like. Use it liberally: after composing, after edits, and at different times to inspect motion. Stills are FREE (credits are only spent by render_video). Pass `time` (seconds) to choose the frame; defaults to 0.","inputSchema":{"type":"object","properties":{"time":{"type":"number","minimum":0,"description":"Composition time in seconds to capture. Default 0 (first frame)."},"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Render one frame of the current project so you can SEE it","readOnlyHint":true,"openWorldHint":true,"destructiveHint":false},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"time":{"type":"number"},"width":{"type":"number"},"height":{"type":"number"},"mime_type":{"type":"string"}},"required":["time"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"create_promo","title":"Compose a designed promo from prebuilt scenes (one fast option)","description":"Assemble a designed-looking promo/intro/product/data video from the Clipkit pattern library: give an ordered list of SCENES and the words, and it bakes in the camera, glass, lighting, motion blur, timing, and layout, then returns an editor link. This is a FAST option when a conventional promo structure fits — it is NOT the only way to make a video and NOT a default; for anything specific or original, author the JSON yourself and call set_project (the full creative range). When you do use this, MIX scene types to fit the brief and vary the structure — a video can be a single kinetic headline, three title cards, a showcase montage, or a data explainer; you do NOT need a hero or a cta. Scene types: hero (glass-orb logo reveal: wordmark, tagline?), kinetic (letter-fly headline: text, subtitle?), showcase (a screenshot tilted in 3D: screenshot URL), title (full-frame title card: headline, kicker?, subtitle?), cta (closing card with a glass button: wordmark, tagline?, cta), stats (hero numbers: stats[{label,current,previous?}], title?), bars (bar chart: bars[{label,value,previous?}], title?), ranking (top-N list: items[{label,value}], title?), pie (pie cards: cards[{label,value,total,previous?}], title?). The data scenes (stats/bars/ranking/pie) look best with theme \"mux\".","inputSchema":{"type":"object","properties":{"scenes":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"hero"},"wordmark":{"type":"string"},"tagline":{"type":"string"},"color":{"type":"string","enum":["pink","green","blue","lavender","purple","yellow","gray"],"description":"Accent color slot. Default \"green\"."},"duration":{"type":"number","exclusiveMinimum":0,"description":"Scene length in seconds. Sensible default per scene type."}},"required":["type","wordmark"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"kinetic"},"text":{"type":"string"},"subtitle":{"type":"string"},"color":{"$ref":"#/properties/scenes/items/anyOf/0/properties/color"},"duration":{"$ref":"#/properties/scenes/items/anyOf/0/properties/duration"}},"required":["type","text"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"showcase"},"screenshot":{"type":"string","format":"uri"},"color":{"$ref":"#/properties/scenes/items/anyOf/0/properties/color"},"duration":{"$ref":"#/properties/scenes/items/anyOf/0/properties/duration"}},"required":["type","screenshot"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"title"},"headline":{"type":"string"},"kicker":{"type":"string"},"subtitle":{"type":"string"},"color":{"$ref":"#/properties/scenes/items/anyOf/0/properties/color"},"duration":{"$ref":"#/properties/scenes/items/anyOf/0/properties/duration"}},"required":["type","headline"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"cta"},"wordmark":{"type":"string"},"tagline":{"type":"string"},"cta":{"type":"string"},"color":{"$ref":"#/properties/scenes/items/anyOf/0/properties/color"},"duration":{"$ref":"#/properties/scenes/items/anyOf/0/properties/duration"}},"required":["type","wordmark","cta"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"stats"},"title":{"type":"string"},"dateRange":{"type":"string"},"stats":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"current":{"type":"number"},"previous":{"type":"number"}},"required":["label","current"],"additionalProperties":false},"minItems":1,"maxItems":4},"color":{"$ref":"#/properties/scenes/items/anyOf/0/properties/color"},"duration":{"$ref":"#/properties/scenes/items/anyOf/0/properties/duration"}},"required":["type","stats"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"bars"},"title":{"type":"string"},"dateRange":{"type":"string"},"bars":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"number"},"previous":{"type":"number"}},"required":["label","value"],"additionalProperties":false},"minItems":1,"maxItems":6},"color":{"$ref":"#/properties/scenes/items/anyOf/0/properties/color"},"duration":{"$ref":"#/properties/scenes/items/anyOf/0/properties/duration"}},"required":["type","bars"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"ranking"},"title":{"type":"string"},"dateRange":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"number"}},"required":["label","value"],"additionalProperties":false},"minItems":1,"maxItems":12},"color":{"$ref":"#/properties/scenes/items/anyOf/0/properties/color"},"duration":{"$ref":"#/properties/scenes/items/anyOf/0/properties/duration"}},"required":["type","items"],"additionalProperties":false},{"type":"object","properties":{"type":{"type":"string","const":"pie"},"title":{"type":"string"},"dateRange":{"type":"string"},"cards":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"number"},"total":{"type":"number"},"previous":{"type":"number"}},"required":["label","value","total"],"additionalProperties":false},"minItems":1,"maxItems":4},"color":{"$ref":"#/properties/scenes/items/anyOf/0/properties/color"},"duration":{"$ref":"#/properties/scenes/items/anyOf/0/properties/duration"}},"required":["type","cards"],"additionalProperties":false}]},"minItems":1,"description":"Ordered scenes; mix types to fit the brief — you do NOT need hero/cta. e.g. a single [{type:\"kinetic\",text:\"…\"}], a sequence [{type:\"title\",headline:\"…\"},{type:\"showcase\",screenshot:\"…\"},{type:\"title\",headline:\"…\"}], or a data piece [{type:\"ranking\",title:\"…\",items:[…]}]"},"theme":{"type":"string","enum":["cinematic","mux","minimal"],"description":"Visual theme. Default \"cinematic\" (dark, serif, premium)."},"motion_blur":{"type":"integer","minimum":0,"maximum":32,"description":"Supersampled motion-blur samples (≥2 enables it — nicer but slower to render). Default off."},"width":{"type":"integer","exclusiveMinimum":0,"description":"Default 1920."},"height":{"type":"integer","exclusiveMinimum":0,"description":"Default 1080."},"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"required":["scenes"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Compose a designed promo from prebuilt scenes (one fast option)","readOnlyHint":false,"destructiveHint":true,"openWorldHint":true},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"project_id":{"type":"string"},"scene_count":{"type":"number"},"width":{"type":"number"},"height":{"type":"number"},"duration":{"type":"number"},"editor_url":{"type":"string"}},"required":["project_id","scene_count"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"open_in_editor","title":"Create a shareable link that opens the current project in the editor","description":"Validate the current project and create a link that opens it in the Clipkit web editor, where the user can preview and refine it. This shares the PROJECT (nothing is rendered — that's render_video). Use after composing or editing. Returns a URL.","inputSchema":{"type":"object","properties":{"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Create a shareable link that opens the current project in the editor","readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"editor_url":{"type":"string"},"project_id":{"type":"string"}},"required":["editor_url","project_id"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"ingest_asset","title":"Host a remote image/video/audio so a project can use it","description":"Fetch a remote media URL (image/video/audio) and HOST it on Clipkit, returning a stable asset_url to put in an element's `url`. Use this so the project's media survives — the original link may rot, be private, or block hotlinking. Anonymous projects allow up to 5 hosted assets / 100 MB total / 50 MB per file; when the limit is hit, tell the user to open the project (open_in_editor) and sign in to add more. Requires project_id.","inputSchema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"A public http(s) URL to an image, video, or audio file."},"filename":{"type":"string","description":"Optional display name for the asset."},"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"required":["url"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Host a remote image/video/audio so a project can use it","readOnlyHint":false,"destructiveHint":false,"openWorldHint":true},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"asset_url":{"type":"string"},"asset_id":{"type":"string"},"filename":{"type":"string"}},"required":["asset_url","asset_id","filename"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}},{"name":"load_project","title":"Load a shared project back into the session","description":"Import a previously shared project as the current project, by its share id or its editor URL (e.g. https://clipkit.dev/editor?id=…), returning its project_id. Use this to continue working on a project the user opened in the editor or shared earlier — the round-trip for open_in_editor. Pass an existing project_id to load into that project; omit it to load into a new one.","inputSchema":{"type":"object","properties":{"id_or_url":{"type":"string","minLength":1,"description":"A share id, or a clipkit.dev/editor?id=… URL."},"project_id":{"type":"string","minLength":1,"description":"Which project to act on — the id returned by create_project / set_project / create_promo / load_project. ALWAYS pass it once a project exists in this conversation; omitting it on a build tool creates a brand-new project. (Omit only on the local stdio server working a single project.)"}},"required":["id_or_url"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"},"annotations":{"title":"Load a shared project back into the session","readOnlyHint":false,"destructiveHint":true,"openWorldHint":true},"execution":{"taskSupport":"forbidden"},"outputSchema":{"type":"object","properties":{"project_id":{"type":"string"},"element_count":{"type":"number"},"width":{"type":"number"},"height":{"type":"number"},"duration":{"type":"number"}},"required":["project_id","element_count"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}]},"jsonrpc":"2.0","id":2},"http_status":200,"headers":{"content-type":"text/event-stream","strict-transport-security":"max-age=63072000"}}},"prompts_list":{"status":"missing","latency_ms":324.95,"details":{"url":"https://www.clipkit.dev/mcp","payload":{"jsonrpc":"2.0","id":3,"error":{"code":-32601,"message":"Method not found"}},"http_status":200,"headers":{"content-type":"text/event-stream","strict-transport-security":"max-age=63072000"},"reason":"not_supported"}},"prompt_get":{"status":"missing","latency_ms":null,"details":{"reason":"not_advertised"}},"resources_list":{"status":"ok","latency_ms":341.71,"details":{"url":"https://www.clipkit.dev/mcp","payload":{"result":{"resources":[{"uri":"clipkit://docs/agents.md","name":"clipkit-agents","title":"Clipkit — Authoring guide (AGENTS.md)","description":"AI authoring reference for Clipkit videos. Schema cheat sheet, pattern catalog (HeaderBar / StatBlock / BarChartRow / RankedList / PieCard), recipe gallery pointing at the working example videos, and authoring guidance (pacing, count-ups, staggers). Read this before composing a video.","mimeType":"text/markdown"},{"uri":"clipkit://docs/protocol.md","name":"clipkit-protocol","title":"Clipkit Protocol v1.0 (PROTOCOL.md)","description":"Normative specification for the Clipkit Protocol (CKP/1.0). RFC 2119 voice — defines Source structure, every element type, the animation model, easing functions, conformance levels (validate / render / export), versioning, and the extension namespace. Source of truth for implementers.","mimeType":"text/markdown"},{"uri":"clipkit://docs/brand.md","name":"clipkit-brand","title":"Clipkit Brand reference (BRAND.md)","description":"Brand identity: dark surface system (#0A0A0A), two accents only (red #EF4444 + yellow #FFB800), Geist + Geist Mono typography, the three-bar logo construction, voice & tone (direct / concrete / honest / no hype). Use when producing Clipkit-branded assets.","mimeType":"text/markdown"},{"uri":"clipkit://schema/source.json","name":"clipkit-schema","title":"Clipkit Source — JSON Schema","description":"Machine-readable JSON Schema for a Clipkit Source, generated from the protocol Zod source of truth. The EXACT shape for set_project / add_element — every field, every element type, with types and enums. Read it to author correct JSON instead of guessing field names. (Agents that cannot read resources: use the get_schema tool instead.)","mimeType":"application/json"}]},"jsonrpc":"2.0","id":5},"http_status":200,"headers":{"content-type":"text/event-stream","strict-transport-security":"max-age=63072000"}}},"resource_read":{"status":"ok","latency_ms":412.76,"details":{"url":"https://www.clipkit.dev/mcp","payload":{"result":{"contents":[{"uri":"clipkit://docs/agents.md","mimeType":"text/markdown","text":"# Clipkit — Authoring guide (AGENTS.md)\n\n> This file is the authoring-time context for AI tools (and humans)\n> writing Clipkit videos. Paste it into a system prompt or attach it\n> to a Claude project. For the **formal protocol spec** — element types,\n> field semantics, conformance levels, normative MUST/SHOULD/MAY — see\n> [`PROTOCOL.md`](./PROTOCOL.md).\n\nClipkit is a protocol for describing motion-graphics videos as JSON.\nA `Source` document is the list of `elements` (with timing and\nanimations) that a conforming runtime turns into frames or MP4.\n\nThere are two layers you work with as an author:\n\n1. **Primitives** — the element types defined by the Clipkit Protocol.\n   The formal spec is in [PROTOCOL.md](./PROTOCOL.md). Section 1 below\n   is a quick cheat sheet so you don't have to bounce out for common\n   lookups, but PROTOCOL.md is the source of truth.\n2. **Patterns** — authoring-time TypeScript helpers in `@clipkit/patterns`\n   that produce primitive elements. You never reference patterns in\n   a Source JSON; you call them in TS/JS and inline their output.\n   Patterns are documented in section 2 (Pattern catalog).\n\nSection 3 (Recipes) walks through complete example videos showing how\npatterns compose into a full piece.\n\n---\n\n## 0. Which surface to use\n\nBefore authoring anything, pick the right surface for your context.\nThere are two — they layer cleanly, no overlap.\n\n| You are running in… | Use… | How |\n|---|---|---|\n| **Claude Code, Cursor, Replit Agent, or any IDE-embedded AI with file + shell access** | `@clipkit/cli` | Write files (`video.ts` or a JSON source) using this doc as context. Run `npx @clipkit/cli render`, `validate`, or `preview` via Bash. Skip MCP entirely. |\n| **Claude.ai chat, Claude Desktop without local tools, or any chat-mode AI without filesystem access** | `@clipkit/mcp-server` | Call the MCP tools — `set_project` to build (the full creative canvas), `create_promo` to compose from prebuilt scenes, `add_element`/`edit_element`/`delete_element` to tweak, `preview_still`/`validate_project`/`describe_project` to check, `open_in_editor`/`render_video` to deliver. The server holds state for you. |\n| **A human developer** | `@clipkit/cli` | `npx @clipkit/cli init my-video`, edit files, `npx @clipkit/cli render`. Same path as IDE-embedded AI. |\n| **CI / a build script** | `@clipkit/cli` | One-shot `clipkit render input.json --out out.mp4`. |\n\nA few clarifications agents sometimes need:\n\n- **The CLI and the MCP server are not interchangeable.** They target\n  different deployments. CLI assumes you have a shell. MCP assumes you\n  don't.\n- **If you can run `Bash`, you're in CLI territory.** You don't need\n  MCP at all — go straight to files + CLI.\n- **There is no \"Clipkit Skill.\"** The skill concept was considered and\n  dropped because everything it would do (auto-load this doc, wrap CLI\n  commands as slash commands) is already covered by the CLI and by MCP\n  resource-loading. If a user asks about installing the skill, redirect\n  them to the CLI.\n\nOnce you know your surface, continue to §1 for the schema cheat sheet.\n\n---\n\n## 1. Schema cheat sheet\n\nAuthoritative spec: **[PROTOCOL.md](./PROTOCOL.md)** (CKP/1.0). What\nfollows is the at-a-glance reference for things you look up constantly.\n\n### Source root\n\n```json\n{\n  \"clipkit_version\": \"1.0\",\n  \"output_format\": \"mp4\",\n  \"width\": 1920, \"height\": 1080,\n  \"duration\": 30, \"frame_rate\": 30,\n  \"background_color\": \"#000000\",\n  \"elements\": [ /* ... */ ]\n}\n```\n\nOptional root field `motion_blur: { \"samples\": 8, \"shutter\": 0.5 }`\nadds whole-frame motion blur at render time — the frame becomes the\nexact average of `samples` sub-frame renders across a shutter window\n(`shutter` = fraction of the frame interval; 0.5 ≈ a film camera's\n180° shutter). Use it when fast motion strobes: whip-pans, fast\ntravel, spinning elements. Renders cost `samples`× the frames, so keep\nsamples at 8 (default) unless trails visibly stair-step. Previews play\nunblurred; the blur appears in the exported file.\n\n### Element types\n\n| `type` | Renders | Key fields |\n|---|---|---|\n| `shape` | rect / ellipse / rounded rect, **or** vector paths | primitive: `shape`, `fill_color`, `gradient`, `border_radius`; path form: `paths[]`, `view_box`, `gradients` |\n| `text` | one-line text | `text`, `spans`, `font_family`, `font_size`, `background_color`, `text_shadow`, `mask` |\n| `image` | bitmap | `source`, `fit`, `crop_*` |\n| `video` | video frame at element time | `source`, `volume`, `trim_start`, `loop`, `fit`, `crop_*` |\n| `audio` | non-visual; mixed in export | `source`, `volume` |\n| `caption` | word-timed captions | `words`, `style`, `highlight_color` |\n| `particles` | ballistic or convergence | `rate`/`burst`/`target_points`, `lifetime`, `velocity`, `z_velocity` |\n| `group` | container; transforms/animates children as one | `elements[]`, `clip`, `mask`, `time_remap` |\n\nA `shape` is **either** a primitive (`shape: \"rectangle\" | \"ellipse\"`, default\nrectangle) **or** vector geometry (`paths`) — never both on one element. When\n`paths` is present it wins and the primitive fields (`shape`, `fill_color`,\n`gradient`, `border_radius`, `stroke_*`) are ignored; geometry and fill come\nfrom inside each path. There is no `triangle`/`polygon` primitive — use `paths`\n(e.g. `\"M ... L ... L ... Z\"`) for triangles and any arbitrary/morphing shape.\n\n`text` and `caption` take `background_color` (+ `background_border_radius`,\n`background_padding` as a number or `[x, y]`) for a solid bg drawn as **one\nband per line**, each shrink-wrapped to that line's glyphs (the social-\ncaption look) — don't put a separate shape behind text; the integral bg\nauto-sizes to the wrapped / auto-fit text and moves with it.\n\nFor text shadows: `text_shadow` ({`color`, `offset_x`, `offset_y`, `blur`,\n`opacity`}, or an **array** for stacked / 3D-extrusion shadows) is\n**per-glyph** — each letter casts its own, tracking per-letter animation.\nUse the `drop_shadow` *effect* instead for one soft shadow of the whole\ntext silhouette.\n\n`image` and `video` take `fit` (CSS `object-fit`: `cover` default,\n`contain`, `fill`, `none`) and a source **crop** — `crop_x` / `crop_y` /\n`crop_width` / `crop_height`, normalized `0..1` of the source, origin\ntop-left, default `0,0,1,1` (whole source). Crop selects a sub-rectangle\nof the media BEFORE `fit` maps it into the box; the element box is\nunchanged. Each crop component is keyframeable — animate the origin to\npan and the size to zoom (Ken Burns) without touching layout.\n\nThere is deliberately NO nested-composition (\"pre-comp\") element. Reuse\nis an authoring-time concern — component patterns in `@clipkit/patterns`\n(section 2) expand into plain elements before serialization. Nested\ntiming (speed-ramp / freeze / reverse a whole scene) is `time_remap` on\na plain `group`.\n\n### Shared transform fields (every element)\n\n`x`, `y`, `x_anchor` (0..1), `y_anchor` (0..1), `width`, `height`,\n`rotation` (degrees), `scale`, `opacity` (0..1 — CSS convention),\nand the CKP/1.0 3D fields `x_rotation` / `y_rotation` / `z_rotation` /\n`z` (see \"3D transforms\" below).\n\nAll of `x` / `y` / `width` / `height` accept numbers (pixels) or\nlength strings — `\"50%\"`, `\"10vw\"`, `\"15vh\"`, `\"5vmin\"`, `\"5vmax\"`.\n\n> ⚠️ **`%` is relative to the CANVAS, not the parent** (unlike CSS).\n> `\"50%\"` on `x` means half the canvas width regardless of nesting.\n> `vw`/`vh`/`vmin`/`vmax` likewise resolve against the canvas. There is\n> no parent-relative percentage; use pixels inside groups when you need\n> child-relative sizing.\n\n### Coming from CSS/HTML\n\nClipkit deliberately tracks CSS where an agent's CSS prior would\notherwise misfire, and deliberately diverges where the protocol earns\nit. Aligned (write them like CSS): **`opacity`** is `0..1` (default 1);\n**gradient `angle`** is the CSS convention (`0deg` = to top, clockwise,\nso `90` = to right, `180`/default = to bottom); **colors** accept hex,\n`rgb()/rgba()`, `hsl()/hsla()`, the 148 CSS named colors, and\n`transparent`. Field name map:\n\n| CSS | Clipkit | Note |\n|---|---|---|\n| `left` / `top` | `x` / `y` | top-left corner, like CSS |\n| `width` / `height` | `width` / `height` | px or canvas-% / vw / vh |\n| `opacity` | `opacity` | `0..1`, default 1 |\n| `border-radius` | `border_radius` | px |\n| `color` / `background` | `fill_color` | SVG-flavored name |\n| `border` color | `stroke_color` / `stroke_width` | |\n| `z-index` | `z` (+ `layer`) | see below |\n| `transform: rotate()` | `rotation` (= `z_rotation`) | degrees |\n| `transform: scale()` | `scale` / `x_scale` / `y_scale` | |\n| `linear-gradient(θ)` | `gradient.angle` | same θ convention |\n| `transition` / `@keyframes` | `animations` / `keyframe_animations` | declarative timeline |\n\n**Positioning is the CSS model:** `x`/`y` place the element's **top-left\ncorner** (`x_anchor`/`y_anchor` default `0`), exactly like `left`/`top` /\nSVG / Canvas. Set `x_anchor: 0.5, y_anchor: 0.5` to position by center\n(or `1` for the far edge). Rotation and scale always pivot the element's\ncenter — like CSS `transform-origin` — independent of the anchor. So a\nfull-frame layer is just `x:0, y:0, width:W, height:H`.\n\n**Layers (required).** Every element gets its own integer `layer` (1..1000),\nlike CSS `z-index`: **the highest layer is on top**, lower numbers go toward\nthe back (backgrounds at layer 1). Give each element a UNIQUE layer — number them 1..N within a container\n(the top-level `elements`, each group's `elements`, each group mask's\n`elements`). Duplicate layers are a validation error. `z` depth still wins over\n`layer`; `layer` orders elements within equal depth.\n\nKept divergences (intentional — don't \"fix\" them):\n- **`z` not `z_index`.** `z` is the single depth axis (layering +\n  perspective foreshortening); `layer` orders within equal depth (higher = front). See \"3D\n  transforms\".\n- **snake_case keys** and SVG-flavored `fill_color` / `stroke_color`.\n- **Easing names are a superset of CSS** (`ease-out-back`, springs, …).\n\n### Expressions (procedural motion)\n\nA numeric property can be a **formula** instead of a number or a keyframe\ntable — a pure function of the element's own clock. Reach for it on the\nmotions that are one line as math and a nightmare as keyframes: bobs,\norbits, spins, handheld shake, staggered reveals.\n\n```jsonc\n{ \"y\": { \"expr\": \"540 + sin(t * PI) * 30\" } }      // bob ±30px at 0.5 Hz\n{ \"rotation\": { \"expr\": \"t * 90\" } }                // 90°/s spin\n{ \"x\": { \"expr\": \"960 + wiggle(3, 12)\" } }          // handheld shake, ±12px\n{ \"opacity\": { \"expr\": \"clamp(t / 0.4, 0, 1)\" } }   // 0.4s linear fade-in\n```\n\nIn scope: `t` (element-local seconds), `dur`, `i` (index in a generated\nset), `n` (sibling count), `value` (the property's base default); the\nconstants `PI`/`TAU`/`E`; and a fixed function set (`sin cos … clamp lerp\nsmoothstep linear ease noise wiggle random`). Operators `+ - * / % ^`,\ncomparisons, `&& || !`, and `cond ? a : b`. The full normative grammar is\nPROTOCOL.md §3.6.\n\nUse `i` for staggered fleets — give every generated element the **same**\nexpression and they self-offset by index:\n\n```jsonc\n{ \"y\": { \"expr\": \"300 + i * 80\" },                       // stack by index\n  \"opacity\": { \"expr\": \"clamp((t - i * 0.1) / 0.3, 0, 1)\" } }  // ripple-in\n```\n\nThe rules that keep it safe and portable (all NORMATIVE):\n- **No cross-element references, no runtime inputs.** An `expr` sees only\n  its own clock — never another element, the mouse, or audio. (Those are\n  \"Tier-B\" and permanently unsupported. If you're tempted, bake the\n  relationship into keyframes, or parent the elements.)\n- **Deterministic.** `noise`/`wiggle`/`random` are seeded hashes, not\n  wall-clock RNG, so every render is identical; `seed` defaults to `0`.\n- **Bakeable.** An expression and a `Keyframe[]` are interchangeable — the\n  importer/editor can sample one into the other.\n- A typo (unknown name, member access, a string) does **not** stop the\n  render — the property silently falls back to its base value. So if a\n  move \"does nothing,\" check the formula.\n\n### Generated sets (`repeat` / `repeat_data`) — one element, N copies\n\nWhere do `i`/`n` get their values? From generated sets. An **image,\ntext, shape or group** element renders as N copies via `repeat: N`\n(identical copies) or `repeat_data: [row, …]` (one copy per row).\nA row is **a partial element patch + a variable scope**: keys naming\nelement fields (`time`, `duration`, `text`, `fill_color`, …) override\nthat field for the copy; ALL row keys are in scope for the copy and its\ndescendants — `{key}` in strings, bare identifiers in expressions\n(numeric values only). `repeat_stagger` delays copy k by `k * s`\nseconds (a row's own `time` wins). A grid, chart, step sequence,\nslideshow or word sequence is ONE element — never hand-list\nnear-identical siblings:\n\n```jsonc\n{ \"type\": \"group\", \"layer\": 2, \"time\": 0.8, \"repeat_stagger\": 0.2,\n  \"repeat_data\": [\n    { \"label\": \"engine a\", \"w\": 900, \"color\": \"#1ba6ff\" },   // one row = one chart row\n    { \"label\": \"engine b\", \"w\": 640, \"color\": \"#8a5cff\" } ],\n  \"y\": { \"expr\": \"380 + i * 90\" },\n  \"elements\": [\n    { \"type\": \"text\",  \"layer\": 1, \"text\": \"{label}\", \"x\": 200 },\n    { \"type\": \"shape\", \"layer\": 2, \"shape\": \"rectangle\", \"x\": 460, \"height\": 24,\n      \"fill_color\": \"{color}\", \"width\": { \"expr\": \"ease(t, 0, 0.8, 0, w)\" } } ] }\n```\n\nNot valid on video/audio/caption/particles (schema rejects it). Spec:\nPROTOCOL.md §3.7.\n\n### Styles — declare appearance once, reference by name\n\nSource-level `styles` are named appearance bundles (font, fill, stroke,\ngradient, radius, opacity) that image/text/shape elements reference via\n`style: \"h1\"` — the `fonts` pattern for field bundles. One merge rule,\nno cascade: defaults < style < the element's own fields; the element\nalways wins. Nine headlines sharing a look = one bundle + nine\none-word references. Unknown style names fail validation. Spec:\nPROTOCOL.md §2.3.\n\n```jsonc\n{ \"styles\": { \"h1\": { \"font_family\": \"Inter\", \"font_weight\": \"800\",\n                      \"font_size\": 120, \"fill_color\": \"#f4f7fb\" } },\n  \"elements\": [ { \"type\": \"text\", \"layer\": 1, \"style\": \"h1\", \"text\": \"One.\" } ] }\n```\n\n### 3D transforms (CKP/1.0)\n\nEvery element also takes `x_rotation` (tip top edge away), `y_rotation`\n(turn right edge away), `z_rotation` (alias of `rotation` — author one\nor the other, both is a validation error), and `z` — **the one depth\naxis** (px toward the viewer). `z` is also the stacking control: it\norders elements always (higher `z` = in front), and *additionally*\nforeshortens once a camera is present. There is no separate `z_index` —\nuse `z` for layering, `layer` orders within equal depth (higher = front, unique per container). Add a\nSource-level camera for true perspective:\n\n```jsonc\n{ \"camera\": { \"perspective\": 1200 },          // px; smaller = stronger\n  \"elements\": [{ \"type\": \"group\", \"clip\": true, \"y_rotation\": -24,\n                 \"width\": 620, \"height\": 420, \"elements\": [ /* UI mock */ ] }] }\n```\n\nThe tilted-UI promo recipe: put the screenshot/mock in a `clip: true`\ngroup and rotate THE GROUP — the flattened layer projects as one card.\n\nThe camera also MOVES (CKP/1.0): `x`/`y`/`z` position and `x_rotation`/\n`y_rotation`/`z_rotation` Euler orientation (all keyframeable) orbit,\npan, tilt, and dolly the viewpoint — not just push/pull on `perspective`.\nA \"look at the logo\" move is authored as resolved orientation angles\n(there is no `target` field). Identity pose = the plain perspective lens.\n\nGOTCHAS:\n- **Paint order is by `z` depth — always.** Sibling cards paint\n  back-to-front by `z` (nearer = in front), whether or not there's a\n  camera; a camera just adds perspective on top. So adding/removing a\n  camera never reorders — only the foreshortening appears. With every\n  `z = 0` it collapses to `layer` order (the highest layer on top, like a plain 2D doc).\n  Set `camera.sort: \"paint\"` to force fixed `layer` order under a camera.\n  The sort is whole-card (no per-pixel depth buffer): interpenetrating or\n  depth-ambiguous cards can mis-order — keep cards from crossing, or use\n  `sort: \"paint\"`.\n- **Animate 3D via `keyframe_animations`** (`property: \"y_rotation\"`),\n  not a keyframe array in the field itself (same rule as `rotation`).\n- **No camera = affine + flat.** 3D rotations still foreshorten (a\n  y-rotated card narrows) but edges stay parallel, and `z` orders the\n  stack without adding perspective; add the camera for converging\n  perspective and parallax.\n- **Glass works in 3D** — tilt a glass pane (`y_rotation` + camera)\n  and the whole optical model rides the plane: footprint, refraction,\n  highlights and shadow all project correctly. Signature move: a glass\n  panel swinging over a UI card. (Edge-on with no camera = invisible.)\n- **Plain groups nest 3D for free** (children live in the parent's 3D\n  space); `clip`/`mask` groups flatten, then the layer tilts as a unit.\n  Filter fields and `effects` evaluate on the PROJECTED pixels — glow\n  radius / stroke width stay uniform in screen px on a tilted card.\n\n3D text reveals — `{ \"type\": \"text-flip\", \"axis\": \"x\" }` flips each\nletter in from 90° to flat about its own center (AE's classic 3D\nper-character entrance). `axis: \"y\"` swings units in sideways, `\"z\"`\nspins them in-plane; `split: \"word\"` flips whole words as rigid slabs;\n`rotation` sets the start angle (try 180 for a full tumble). Composes\nwith `text-slide` (units slide AND flip). Pair with a `camera` for true\nperspective on the mid-flip letters; without one the flip still reads\n(pure foreshortening). Captions don't take text-* animations.\n\n### Lighting + materials (CKP/1.0)\n\nSurfaces can respond to light — PBR. **Opt-in:** with no `lights` and no\n`material`, everything renders unlit exactly as before. Add scene\n`lights` + a `material` on an element and the runtime shades it (the\nelement's own pixels are the albedo).\n\n```jsonc\n{ \"lights\": [ { \"type\": \"ambient\", \"intensity\": 0.4 },\n              { \"type\": \"directional\", \"azimuth\": 45, \"elevation\": 30, \"intensity\": 1.2 } ],\n  \"environment\": { \"type\": \"gradient\",\n                   \"stops\": [ { \"offset\": 0, \"color\": \"#0b0f1a\" },\n                              { \"offset\": 1, \"color\": \"#7da2ff\" } ] },\n  \"camera\": { \"perspective\": 1400 },\n  \"elements\": [ { \"type\": \"group\", \"clip\": true, \"y_rotation\": -20,\n                  \"material\": { \"roughness\": 0.25, \"metalness\": 0.6, \"reflectivity\": 1 },\n                  \"elements\": [ /* dark UI */ ] } ] }\n```\n\n- **`material`**: `roughness` (0 glossy/tight .. 1 matte), `metalness`\n  (0 dielectric .. 1 metal), `reflectivity` (env-reflection strength),\n  `emissive` (self-lit), `normal_map` (tangent-space normal map URL for\n  surface detail/bumps — flat = `#8080ff`) + `normal_scale`. Scalars\n  animatable.\n- **The reflection sweeps with the camera.** Specular + environment\n  reflection are view-dependent, so orbiting/dollying the camera slides\n  the highlight across a glossy dark UI — the premium look. In 2D (no\n  camera) animate the **lights** instead for the sweep.\n- **`environment`** is what reflective surfaces mirror: either a gradient\n  \"sky\" (`{ type: 'gradient', stops }`, offset 0 = down, 1 = up) or an\n  **equirectangular image** (`{ type: 'image', src }`) for real\n  photographic reflections. Roughness blurs the reflection toward the\n  environment average. It's what makes glossy screens/metal look\n  expensive.\n- **Works on shapes AND textured surfaces** — images, video, and\n  flattened `clip` groups light from their own pixels, so a whole UI card\n  wrapped in a clip group shades as one lit plane. `@clipkit/patterns`\n  ships `litSurface()` for exactly this.\n- **Bloom** (`source.bloom { threshold, knee, intensity, radius }`): a\n  whole-frame post that makes BRIGHT things bleed light (specular hits,\n  emissive surfaces, bright media) — driven by each pixel's brightness,\n  not a per-element knob. For a deliberate per-element halo (even on a\n  dark element), use the `glow` effect instead.\n- **Editor authoring:** Material is a per-element inspector block; Lights,\n  Environment, and Bloom live under Video settings (source scope).\n- Local illumination only (no cast shadows yet). Flat 2.5D surfaces.\n\n### Blend modes + filters (every element)\n\n`blend_mode`: `\"multiply\"` (darken; white neutral), `\"screen\"`\n(lighten; black neutral), `\"add\"` (glow). Element-local. On a group it\nneeds `clip: true` or a `mask` (the group must flatten to a layer).\n\nFilters, all animatable, CSS semantics: `blur_radius` (Gaussian σ px),\n`brightness` / `contrast` / `saturation` (multipliers, 1 = unchanged,\n`saturation: 0` = grayscale). Work on any element including groups —\na group filters as one flattened image.\n\nThere is no `color_overlay` field — it decomposes: put a same-sized\nshape of the overlay color on a higher layer, in front of the element (use\n`opacity` or `blend_mode: \"multiply\"` for tint strength). For a\ngrayscale-then-tint look, combine `saturation: 0` with an overlay.\n\n### Stylize effects (every element)\n\n`effects` is an ordered array of passes, applied after the filters:\n\n```json\n\"effects\": [{ \"type\": \"pixelate\", \"cell_size\": 12 }]\n```\n\nTypes: `pixelate` (`cell_size`), `dither` (`levels`, 2 = 1-bit retro),\n`halftone` (`cell_size`, `angle`), `ascii` (`cell_size`). Every param\ntakes a number or keyframes (element-local time) — animating\n`cell_size` from 1 upward makes a great \"depixelate\" reveal. Effects\nchain in array order and work on groups (the subtree flattens first).\nLayer styles — `glow` (`radius`, `intensity`, `color`), `drop_shadow`\n(`offset_x/y`, `blur`, `color`, `opacity`), `stroke` (`width`,\n`color`) — composite beneath the element and work on text, images,\nand groups (not just shapes). Backdrop readers — `glass` (`refraction`,\n`dispersion`, `blur_radius`, `tint`, … on shapes) and `backdrop_blur`\n(`radius`) — sample what's drawn BENEATH the element: put a translucent\nshape over content and add the effect for a refractive or frosted panel.\nProgrammable — `pixel_shader` (`source`) runs a Pixel-Expr program per\npixel: the `Expr` language + vectors/swizzles and the inputs `uv` (vec2\n0..1), `t`, `resolution`, `aspect`, compiled to GLSL+WGSL. ONE expression\n→ a color (`float`→gray, `vec3`→rgb, `vec4`→rgba); funcs incl. `mix`,\n`smoothstep`, `length`, `noise`, `hash`, `fbm(p)` (fractal noise), `voronoi(p)`\n→ `(F1, F2, cellHash)` (cellular — `F2−F1` = crisp borders), `hsv2rgb`, ctors\n`vec2/3/4`/`rgb`. Put it on\na frame-filling shape for a generative background, e.g. `mix(rgb(.02,.06,\n.16), rgb(.7,.87,.93), noise(uv*8.0 + vec2(t*.1, 0.0)))`. Set\n`reads_backdrop: true` to sample the scene behind via `backdrop(uv2)`→vec4\n(refraction/chromatic), and multiply offsets by the `coverage` input to ramp\nthe effect to zero at the element edge (no hard seam). Author with `let NAME =\nEXPR;` locals, declare keyframeable `params` (named float knobs read by name in\nsource), and `fold(N, init, acc-body)` bounded loops. Raymarch with the SDF\nstdlib (`sdSphere`/`sdBox`/`sdRoundBox`/`sdTorus`/`opUnion`/`opSubtract`/`opSmoothUnion`/`opExtrude`/`rotX/Y/Z`):\n`def shape(p) = ...;` names the scene SDF, `fold(80, 0, acc + shape(ro+rd*acc))`\nmarches it, `normal(shape, p)` gives a general central-difference normal — then\nshade, or make glass with `refract` + `backdrop`. Declare up to 4 image inputs\nwith `textures` (`{ name: url }`) and sample them via `texture(name, uv)`→vec4\n— image distortion (`texture(grid, uv + flow)`), gradient ramps, or MSDF\nwordmarks (decode `median(s.r, s.g, s.b)` then `smoothstep` around `0.5`).\n\nGroup time remapping — `time_remap` on a GROUP warps the clock for\neverything inside it: slow a whole composed scene to 20% at the\ndramatic beat, freeze it, or run it backwards — children's animations\nand nested videos all follow — and so does AUDIO, varispeed-style\n(ramps pitch with speed, freezes silent, reverse plays backwards).\nThe group's own position/opacity still animate on real time, so you\ncan move a frozen scene around. Same keyframe shape as video\ntime_remap below.\n\nTime remapping — `time_remap` on a video element maps element-local\ntime → media seconds with plain keyframes: `[{time: 0, value: 0},\n{time: 1, value: 3}]` is a 3× speed ramp, a flat segment is a freeze\nframe, decreasing values play in reverse, easings make the ramps\ncinematic. It replaces `trim_start`/`playback_rate`/`loop`. Audio\nfollows the warp VARISPEED, like tape: ramps pitch up/down with\nspeed, freezes go silent, reverse plays the sound backwards — usually\nexactly the cinematic speed-ramp sound you want.\n\nGOTCHA: the runtime keys ONE decoder + texture per unique video URL.\nTwo video elements with the same URL but different playheads\n(different `time_remap`, trims, or rates) will fight over it — all\ncopies display the same frames and seeking thrashes. Give each its\nown URL; a query suffix on the same file (`clip.mp4?copy=2`) is\nenough.\n\nTrim paths — on any shape path: `trim_start`/`trim_end` draw only that\nslice of the stroke (animate `trim_end` 0\\u21921 for the logo-draws-itself\nreveal), `trim_offset` rotates the window with wrap (animate it for\nthe traveling-dash \"snake\"). `stroke_progress` is the simple sugar.\nPath morphing: keyframe the `d` itself — values with the SAME command\nstructure (e.g. two cubics with equal point counts) tween smoothly;\nmismatched structures snap. Author morph targets with matching\ncommands, like every motion tool expects.\n\nLiving motion, one line each — `drift` (seeded organic float; the\n\"alive logo\" look: `{type: \"drift\", distance: 20}`), `breathe` (gentle\nscale pulse), `orbit` (circular motion: `distance` radius,\n`direction: \"left\"` for counter-clockwise). All run the element's full\nlife by default and are exactly seeded — same seed, same motion. And\nany keyframe_animation can now `loop: true` (repeat) or\n`loop: \"ping-pong\"` (back-and-forth) — build one cycle, loop forever.\n\nMotion paths — `keyframe_animations` with `property: \"position\"` and\n`[x, y]` values moves an element along a bezier path: `out_tangent` /\n`in_tangent` on the keyframes shape the curve (omit them for straight\npolyline hops), `auto_orient: true` turns the element to face its\ntravel direction. Speed is arc-length-true: linear easing = constant\nspeed; easings shape acceleration along the path. Use for swooping\nentrances, orbit flybys, anything that should move like it has\nmomentum instead of lerping x and y separately.\n\n3D paths — use `[x, y, z]` values (and optionally `[dx, dy, dz]`\ntangents) to carve the path through depth; the path then drives `z`\ntoo, and arc-length speed is true in 3D. Don't mix `[x, y]` and\n`[x, y, z]` keyframes in one path (validation error). Two gotchas:\npath z is invisible without a `camera` (same as the `z` field), and\n`auto_orient` stays in-plane — it spins `rotation` from the xy\ndirection but never tilts the element's plane (add `x_rotation` /\n`y_rotation` yourself if you want banking). Signature move: fly an\nelement from deep background to right up against the camera along a\ncurve.\n\nProcedural noise — `fractal_noise` (`scale`, `evolution`, `offset_x/y`,\n`octaves`, `seed`) fills the element's footprint with seeded grayscale\nfBM: animate `evolution` for churning fog/plasma, chain `levels` to\nshape contrast and a duotone `lut` for color (grayscale has no hue, so\n`hue_rotate` alone can't tint it), or put it on TEXT for noise-filled\ntype. `turbulent_displace` (`amount`, `scale`, `evolution`, `octaves`,\n`seed`) warps the element's own pixels — wavy text, heat shimmer.\nSame seed = same pixels, always (the noise function is normative).\n\nGrading — the `hue_rotate` filter field (degrees, joins\nblur/brightness/contrast/saturation on every element) plus two\neffects: `levels` (`in_black`, `in_white`, `gamma`, `out_black`,\n`out_white` — punchy shadows = `in_black: 0.08, gamma: 1.15`) and\n`lut` (`source` = a .cube file URL or data: URI, `intensity` to dial\nit back). A LUT is the one-liner for a whole color story\n(teal-orange, film stocks); levels is the surgical tool.\n\nKeying — `chroma_key` (`color`, `tolerance`, `softness`, `spill`)\nremoves a screen color by chroma distance: put it on a green-screen\n`video` element with `color` set to the screen's ACTUAL green (sample\nit — real screens are darker than `#00FF00`), then place the video\nover any backdrop. `spill` (default 0.5) desaturates green bounce on\nthe subject. `luma_key` (`threshold`, `softness`, `invert`) drops dark\npixels — the way to lift white-on-black mattes, flares, and smoke\nstock onto a scene. On a group, the composited children key as one\nlayer.\n\n`glass` is the one effect that reads the BACKDROP (everything drawn\nbeneath the element). It applies to SHAPE elements only (the pane\ngeometry must be exact; other element types skip it with a warning).\nDefaults give the classic clear liquid-glass button — most uses need\nNO params at all:\n\n```json\n{ \"type\": \"shape\", \"border_radius\": 65, \"width\": 380, \"height\": 300,\n  \"fill_color\": \"#FFFFFF\",\n  \"effects\": [{ \"type\": \"glass\" }] }\n```\n\nTwo materials, one dial: `blur_radius: 0` (default) = CLEAR glass,\n`blur_radius > 0` = FROSTED. `mode: \"dome\"` + `edge_width` = the\nshape's radius = a half-sphere magnifier:\n\n```json\n{ \"id\": \"magnifier\", \"type\": \"shape\", \"shape\": \"ellipse\",\n  \"width\": 320, \"height\": 320, \"fill_color\": \"#FFFFFF\",\n  \"effects\": [{ \"type\": \"glass\", \"mode\": \"dome\",\n                \"edge_width\": 160, \"refraction\": 25 }] }\n```\n\nOther dials (reference-tuned defaults — change sparingly):\n`refraction` (bend strength, ≈px), `edge_width` (lens depth; small =\nflat card with curl at the rim), `edge_highlight` (the whole light\nrig), `dispersion` (color fringing), `shadow` (built-in, outside-only\n— never add a shadow sibling under glass), `backdrop_saturation`,\n`tint`. The pane's fill color is ignored — only its geometry matters.\nA crisp ring is a stroked transparent sibling above; wrap pane + ring\nin an UNLAYERED group (no clip/mask) to move them as one element.\n\n### Easings (the 36)\n\n`linear`, `ease`, `ease-in`, `ease-out`, `ease-in-out`, plus\nease-{in,out,in-out}-{cubic, quad, quart, quint, sine, expo, circ,\nback}, plus `spring` (damped harmonic oscillator — overshoots ~5% then\nsettles; Remotion's signature feel), `elastic-{in,out,in-out}` (decaying\nsinusoidal wobble), and `bounce-{in,out,in-out}` (ball-drop bounces — the\neasing curves, distinct from the bounce-in/out animation PRESETS, which\nare scale tweens). Two parametric forms also count: `cubic-bezier(x1, y1,\nx2, y2)` and `steps(n)`.\n\nQuick guide:\n- **`spring`** — hero text reveals, signature scale-ins\n- **`ease-out-cubic`** — smooth, well-behaved ramps; default choice\n- **`ease-out-quart`** — fast start, long slow tail; great for measure bars\n- **`ease-out-back`** — slight overshoot at the end (UI bounce)\n- **`linear`** — when you specifically want no easing\n\n### Particles — two modes\n\nBallistic (emit + physics):\n\n```json\n{\n  \"type\": \"particles\",\n  \"x\": 540, \"y\": 960,\n  \"burst\": true, \"burst_count\": 140,\n  \"lifetime\": 2.5, \"velocity\": 900, \"spread\": 360, \"gravity\": 700,\n  \"color\": [\"#22d3ee\", \"#ec4899\", \"#22c55e\", \"#fbbf24\"],\n  \"particle_shape\": \"square\", \"size\": 18, \"rotation_speed\": 540,\n  \"fade_at\": 0.8\n}\n```\n\nConvergence (assemble into a target shape):\n\n```json\n{\n  \"type\": \"particles\",\n  \"x\": 960, \"y\": 540,\n  \"burst\": true, \"burst_count\": 500,\n  \"target_points\": [[100, 200], [110, 200], /* ... */ ],\n  \"scatter_radius\": 1100,\n  \"convergence_easing\": \"ease-out-quart\",\n  \"lifetime\": 1.6, \"size\": 10, \"particle_shape\": \"circle\"\n}\n```\n\nDepth — add `z_velocity` (px/s toward the viewer, can be negative) and\n`z_spread` (random vz range) to blow particles out of the screen:\nnearer ones grow, receding ones shrink. Needs a Source `camera` to be\nvisible (no perspective = no depth cue), and pairs beautifully with a\ntilted emitter (`x_rotation` on the particles element): confetti\nerupting off a card's surface. Paint order stays spawn order — depth\nnever re-sorts.\n\n### Shape paths — vector graphics\n\n```json\n{\n  \"type\": \"shape\",\n  \"view_box\": [0, 0, 180, 180],\n  \"gradients\": [\n    { \"id\": \"g1\", \"type\": \"linear\", \"x1\": 0, \"y1\": 0, \"x2\": 180, \"y2\": 0,\n      \"stops\": [{ \"offset\": 0, \"color\": \"#FF4E00\" }, { \"offset\": 1, \"color\": \"#FF1791\" }] }\n  ],\n  \"paths\": [\n    { \"d\": \"M 10 10 L 90 90\", \"stroke\": \"url(#g1)\", \"stroke_width\": 5,\n      \"stroke_progress\": [{ \"time\": 0, \"value\": 0 }, { \"time\": 1, \"value\": 1 }] }\n  ]\n}\n```\n\n`stroke_progress` from 0→1 makes the path draw in via dashoffset. Use a\nthick stroke on a small circle for pie/donut charts.\n\n### Diagonal text reveal\n\n```json\n{\n  \"type\": \"text\", \"text\": \"Vercel and Clipkit\", \"font_size\": 70,\n  \"mask\": {\n    \"type\": \"linear-wipe\", \"angle\": -45,\n    \"progress\": [{ \"time\": 0, \"value\": 0 }, { \"time\": 2.7, \"value\": 1, \"easing\": \"ease-out-cubic\" }],\n    \"softness\": 0.3\n  }\n}\n```\n\nThat's the at-a-glance set. For full normative semantics — units, edge\ncases, conformance — go to [PROTOCOL.md](./PROTOCOL.md).\n\n## 2. Pattern catalog\n\nPatterns live in `@clipkit/patterns`. Each is a TS function that emits\nplain elements — most return `Element[]` you spread into your `elements`\narray; *component* patterns (IntroCard, LowerThird) return a single\n`group` element you push, so the unit moves / animates / time-remaps as\none. They're opinionated: pick a variant by passing a `color` slot\n(\"pink\" | \"green\" | \"blue\" | \"lavender\" | \"purple\" | \"yellow\" | \"gray\")\nand a `theme` (`'mux'` is the default; `'minimal'` exists too).\n\nCommon params shared by most patterns:\n\n- `id`: string — prefix for produced element ids\n- `theme?: 'mux' | 'minimal'` — default `'mux'`\n- `color`: ColorName — picks the accent palette\n- `time`, `duration`: number — scene-local seconds\n- `layerBase`: number — pattern uses a small range starting here\n\n### HeaderBar\n\nThe scene framing for dashboard-style scenes: white header strip with\nleft logo + middle title + right date, colored body fill below.\n\n```ts\nheaderBar({\n  id: 'overall',\n  title: 'Overall stats',\n  dateRange: 'Nov. 17 - Dec. 16 2021',\n  bodyColor: 'pink',\n  canvasWidth: 1920,\n  canvasHeight: 1080,\n  logo: { viewBox: [0, 0, 215, 70], paths: MUX_LOGO_PATHS, gradients: [MUX_GRADIENT] },\n  time: 4.33,\n  duration: 6.0,\n  layerBase: 100,\n})\n```\n\nUse as the first elements of every scene that follows the \"logo + title\"\nconvention.\n\n### StatBlock\n\nThemed top border, big spring-counted number, label, optional trend pill.\n\n```ts\nstatBlock({\n  id: 'views',\n  current: 195654112,\n  previous: 159560036,        // optional — adds the trend pill\n  label: 'Total views',\n  color: 'pink',\n  x: 120, y: 380,\n  width: 1680,\n  time: 4.33, duration: 6.0,\n  layerBase: 200,\n})\n```\n\nStack two with `y` 340 px apart for a \"Total views / Total minutes watched\"\nlayout.\n\n### BarChartRow\n\nRow with an animated white background bar, top border, value count-up,\noptional icon, label, optional trend pill. Used for \"Top 5 by X\" lists.\n\n```ts\nbarChartRow({\n  id: 'phone',\n  value: 130733672,\n  max: 130733672,             // the leading value in the dataset\n  previous: 133478441,        // optional — adds trend pill\n  label: 'Phone',\n  icon: { viewBox: [0, 0, 32, 52], paths: PHONE_ICON_PATHS },\n  color: 'green',\n  x: 120, y: 320,\n  width: 1680,\n  time: 10.33, duration: 6.0,\n  staggerIndex: 0,            // 0..n for cascading entry\n  layerBase: 400,\n})\n```\n\nLoop over your dataset, increment `y` by `168` and `staggerIndex` by 1\nper row.\n\n### RankedList\n\nNumbered list of items with rank, name, value, animated measure bar.\n1 or 2 columns. Used for \"Top 10\" style scenes.\n\n```ts\nrankedList({\n  id: 'titles',\n  items: [\n    { label: 'Burgandy Alert', value: 2733413 },\n    { label: 'Tough Affection', value: 1694421 },\n    // ...\n  ],\n  color: 'lavender',\n  x: 120, y: 320,\n  width: 1680,\n  columns: 2,\n  time: 16.33, duration: 6.0,\n  layerBase: 600,\n})\n```\n\n### PieCard\n\nAnimated pie chart (white slice growing over the colored body), big\npercentage label, count-up view count, optional trend pill, optional\nlogo on a rounded white square, bottom label. One card per data point;\nposition multiple horizontally for a \"Top 4 X\" scene.\n\n```ts\npieCard({\n  id: 'chrome',\n  value: 2233793,\n  total: 3396911,             // dataset total — drives the percentage\n  previous: 2337628,          // optional — adds trend pill\n  label: 'Chrome',\n  logoUrl: '/chrome.png',     // optional — image over the white square\n  color: 'blue',\n  cx: 300,                    // center x of the card\n  time: 28.33, duration: 6.0,\n  staggerIndex: 0,\n  layerBase: 1000,\n})\n```\n\n### IntroCard (component)\n\nFull-frame opening title: themed backdrop, optional all-caps kicker,\nbig headline, accent rule wipe, optional subtitle. Entrance + exit\nanimation built in. Returns ONE `group` element — position, animate, or\n`time_remap` the whole card as a unit.\n\n```ts\nelements.push(introCard({\n  id: 'open',\n  kicker: 'Mux Data',\n  headline: '2021 in review',\n  subtitle: 'Your year of video, in numbers',\n  color: 'pink',\n  canvasWidth: 1920, canvasHeight: 1080,\n  time: 0, duration: 4,\n  layer: 100,\n}))\n```\n\nNote components return a single `Element` — `push`, don't spread.\n\n### LowerThird (component)\n\nBroadcast-style name strip: accent bar + white panel + bold name +\noptional role line. Slides in from the left, fades out at the end of\nits window. Returns ONE `group` element.\n\n```ts\nelements.push(lowerThird({\n  id: 'speaker',\n  name: 'Dana Cruz',\n  role: 'Head of Video',\n  color: 'blue',\n  x: 80, y: 900,              // left edge / vertical center\n  time: 12, duration: 5,\n  layer: 300,\n}))\n```\n\n### TiltedShowcase (component)\n\nThe promo signature shot: a screenshot in a browser-chrome frame,\ntilted in 3D (CKP/1.0) and gently swinging. Returns ONE `clip: true`\ngroup, so the framed card projects as a unit. Pair with a Source-level\n`camera: { perspective: 1200 }` for converging perspective.\n\n```ts\nelements.push(tiltedShowcase({\n  id: 'app-shot',\n  source: '/screens/dashboard.png',\n  color: 'blue',\n  x: 960, y: 540,            // card center\n  width: 760, height: 500,\n  tilt: 22,                  // swings ±22°; 0 = static\n  z: 40,\n  time: 2, duration: 6,\n  layer: 200,\n}))\n```\n\n### cameraOrbit (scene camera)\n\nUnlike the others, this returns a `Camera` for `source.camera` (not an\nelement): a ready-made keyframed move — orbit (`yaw`), constant `pitch`\ntilt, `dolly` (z), `truck` (x). Place content at varied `z` for parallax;\ndepth-correct occlusion is automatic (§4.4.3).\n\n```ts\nconst source = {\n  camera: cameraOrbit({ perspective: 1500, yaw: 40, pitch: 6, dolly: 120, duration: 5 }),\n  elements: [ /* cards at different z */ ],\n};\n```\n\n### TrendPill (helper)\n\nSmall pill rendering a signed percentage delta. Used internally by\nStatBlock, BarChartRow, PieCard, but exported so you can place one\nstandalone.\n\n```ts\ntrendPill({\n  id: 'standalone-trend',\n  cx: 1500, cy: 540,\n  delta: trendPct(2233793, 2337628),  // → -4.4\n  color: 'pink',\n  time: 4.33, duration: 6.0,\n  layerBase: 250,\n})\n```\n\n---\n\n## 3. Recipe gallery\n\nExample videos demonstrating patterns in context. Source files live in\n`apps/playground/src/`:\n\n- **`mux-demo.ts`** — `MUX_DEMO`. 1920×1080, 39 s, 7 scenes (intro, big\n  stats, devices, top-10 titles, US heatmap, browsers, outro). The\n  full-fidelity reference; every pattern in section 2 shows up here.\n- **`examples.ts`** — `VERCEL_TEMPLATE`. 1280×720, 6.7 s. Showcases the\n  SVG stroke-evolution primitive (Next.js logo paths drawing in), the\n  text linear-wipe mask, and keyframe-animated rings.\n- **`examples.ts`** — `CODE_WRAPPED`. 9:16, 15 s. Particle confetti\n  bursts at celebration moments, gradient backgrounds, spring stat\n  reveals — the github-unwrapped-style template.\n\nWhen you author a new video, lean on these as references. If a scene\n\"looks like X scene in mux-demo,\" call the same pattern with the same\nshape of args.\n\n### Captions from media (transcription)\n\nTo caption a video or audio clip, you don't hand-write `words` — transcribe\nthe media into a `caption` element. Runs Whisper **locally** (no API key, no\nupload) via `@clipkit/speech-to-text`; needs `ffmpeg` on the host.\n\n- **CLI** (local files): `clipkit transcribe <file>` prints the caption\n  `words[]` JSON to stdout (progress on stderr). Add `--element` for a full\n  `caption` element ready to drop into a Source, `--model Xenova/whisper-small`\n  for accuracy or `…-tiny.en` for speed, `--out cap.json` to write a file.\n- **MCP** (chat-mode agents): the `transcribe_media` tool takes a local\n  `path`, transcribes, and adds a `caption` element to the current project\n  (`add: false` to only return it).\n\nBoth produce the same thing: a `caption` element with `words: [{ text, start,\nend }]` (times relative to the caption's `time`). The protocol/runtime are\nunchanged — this only *fills* the caption the renderer already supports. Style\nit with the usual `caption` fields (`style`, `highlight_color`, …).\n\n---\n\n## 4. Authoring guidance\n\nA few rules of thumb that go beyond the schema:\n\n- **Lead with patterns; reach for primitives only for one-offs.** If a\n  scene fits HeaderBar + BarChartRow, do that. Don't reinvent.\n- **Stagger entrances.** For lists, use `staggerIndex` (or compute a\n  `time: time + i*0.07` offset for primitives). All-at-once entrances\n  look amateurish.\n- **Sound matters.** Set up an `audio` element early. The runtime mixes\n  it into the export and the playground plays it during preview.\n- **Pace by frames at 30 fps.** Most scenes are 180 frames = 6 s. Title\n  intros are 100–130 frames. Outros 120–150. Big stat reveals 180.\n- **Trend pills are optional.** Only add `previous` to a pattern if the\n  comparison is genuinely interesting; otherwise let the number land\n  on its own.\n\n---\n\n## 5. Output format\n\nWhatever you generate, the final output the user runs is a Clipkit\n`Source` JSON object. Patterns are TypeScript-only — they help you\nconstruct that JSON but never appear in it.\n\nIf you're emitting code for the user:\n\n```ts\nimport type { Source } from '@clipkit/protocol';\nimport { headerBar, statBlock /* ... */ } from '@clipkit/patterns';\n\nexport const MY_VIDEO: Source = {\n  width: 1920, height: 1080, duration: 30, frame_rate: 30,\n  elements: [\n    ...headerBar({ ... }),\n    ...statBlock({ ... }),\n    // ...\n  ],\n};\n```\n\nIf you're emitting raw JSON (no TS), inline the patterns' output\ndirectly. There's no `{ \"type\": \"stat_block\" }` — only the primitive\nelements the patterns produce.\n\n---\n\n## Appendix — Protocol change checklist (maintainers)\n\n> **This section is for editing the protocol itself, not for authoring\n> videos.** If you're writing a Source, ignore it. If you're adding or\n> changing an element type, field, effect, animation preset, output\n> format, etc., work this list — a protocol change \"bleeds down\" into\n> more places than it looks.\n\n**The schema source of truth (`packages/protocol/src/`) — all manual:**\n\n- `types.ts` — the interface / const enum (`ELEMENT_TYPES`,\n  `ANIMATION_TYPES`, `OUTPUT_FORMATS`, the effect union, …).\n- `zod.ts` — the matching validator. Const-driven enums sync from the\n  arrays; a new interface needs a new schema object. **Put the field's\n  default in its `.describe()` text** — that string is the only place a\n  default is declared protocol-side. It's surfaced to agents via\n  `get_schema` → `zodToJsonSchema`, so the wording an agent reads (\"…\n  (default 1)\") comes straight from here. Keep it honest against the\n  runtime.\n- `CLIPKIT_PROTOCOL_VERSION` in `types.ts` — bump only on a real spec\n  change.\n\nThere is **no central defaults table.** (A former `defaults.ts` /\n`applyDefaults()` was dead code — never imported or called — and has\nbeen deleted; don't re-add it.) The *effective* default for a field is\nthe inline `?? fallback` in whichever runtime renderer reads it\n(`packages/runtime/src/compositor/element-renderers/*.ts`, e.g.\n`fillColor = span.fill_color ?? defaults.color` in `text.ts`) — one\nfallback per field, at its point of use. So a new field with a default\nmeans **two** edits that must agree: the runtime's `??` fallback, and\nthe `.describe()` text in `zod.ts` that documents it.\n\n**Runtime (`packages/runtime/src/`):** a new field on an existing\nelement auto-works if the renderer already reads it. A new **element\ntype** needs a renderer in `compositor/element-renderers/`; a new\n**effect type** needs handling in the effect chain; a new **animation\npreset** needs a handler in `animation/presets.ts`.\n\n**Editor inspector — mostly automatic.** The registry deriver\n(`editor-core/src/registry/derive.ts`) reads the zod schema by\nstructure, so a new field gets a working control with no edits. Only\ntouch `registry/overrides.ts` when the auto-derived control is wrong\n(range, label, section, or it fell back to a raw JSON chip). Verify\nwith `node probes/probe-editor-registry.mjs` — it asserts every field\nhas exactly one knob and lists anything that fell through.\n\n**Docs (all hand-written, no codegen):**\n\n- `PROTOCOL.md` — the formal spec.\n- This file (`AGENTS.md`) — authoring patterns, if it's a preset/effect.\n- `apps/web/content/docs/fields/*.md` — per-element field reference\n  (separate from PROTOCOL.md; easy to miss).\n- `packages/mcp-server/src/embedded-docs.ts` — a COPY of the agent\n  guidance baked into the MCP server; it does **not** auto-sync with\n  this file, so it drifts silently.\n\n**Agent / MCP surface:** `packages/mcp-server/src/tools.ts` —\ntool input schemas are hand-written. A new top-level Source field or a\nnew `output_format` needs a parameter added here. Element-field CRUD\nvalidates against zod automatically.\n\n**Situational:** `apps/web/lib/playground/examples.ts` (showcase the\nnew feature) and `packages/snapshot-importer/` (if it should map from\nimported HTML/CSS).\n\nThe genuinely forgettable manual ones: **the runtime `??` fallback that\nsets the field's default, the matching `.describe()` default text in\n`zod.ts`, the `content/docs/fields/*.md` reference, and the MCP\n`embedded-docs.ts` copy** — then run the registry probe.\n"}]},"jsonrpc":"2.0","id":6},"http_status":200,"headers":{"content-type":"text/event-stream","strict-transport-security":"max-age=63072000"},"resource_uri":"clipkit://docs/agents.md"}},"determinism_probe":{"status":"ok","latency_ms":221.57,"details":{"attempts":2,"successful":2,"matches":2,"stable_ratio":1.0,"baseline_signature":"fb47c8c9f67a4752f3e7b3601ed9802644d16210119eb65e8e433291f02526ac","errors":[]}},"instruction_tool_reference_probe":{"status":"warning","latency_ms":null,"details":{"referenced_tools":["an","get_schema","ingest_asset","preview_still","read_docs","render_video","set_project","this","url"],"missing_tools":["an","render_video","this","url"],"observed_tool_count":15}},"session_resume_probe":{"status":"warning","latency_ms":null,"details":{"reason":"no_session_id","transport":"streamable-http","resume_expected":true,"protocol_version":"2025-03-26"}},"step_up_auth_probe":{"status":"missing","latency_ms":null,"details":{"oauth_present":false,"auth_required_checks":[],"supported_scopes":[],"scope_specificity_ratio":0.0,"broad_scopes":[],"challenge_headers":[],"step_up_signals":[],"minimal_scope_documented":false}},"transport_compliance_probe":{"status":"warning","latency_ms":200.18,"details":{"transport":"streamable-http","session_id_present":false,"protocol_header_present":false,"last_event_id_visible":false,"requested_protocol_version":"2025-03-26","bad_protocol_status_code":400,"bad_protocol_payload":{"jsonrpc":"2.0","error":{"code":-32000,"message":"Bad Request: Unsupported protocol version: 1999-99-99 (supported versions: 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, 2024-10-07)"},"id":null},"bad_protocol_headers":{"content-type":"application/json","strict-transport-security":"max-age=63072000"},"bad_protocol_error":null,"delete_status_code":null,"delete_error":null,"expired_session_status_code":null,"expired_session_error":null,"issues":["missing_session_id","missing_protocol_header"]}},"utility_coverage_probe":{"status":"missing","latency_ms":197.24,"details":{"completions":{"advertised":false,"sample_target":{"type":"resource","uri":"clipkit://docs/agents.md"},"live_probe":"not_executed"},"pagination":{"supported":false,"next_cursor_methods":[],"metadata_signal":false},"tasks":{"advertised":false,"probe_status":"missing","http_status":200},"initialize_capability_keys":["resources","tools"]}},"advanced_capabilities_probe":{"status":"warning","latency_ms":null,"details":{"capabilities":{"prompts":false,"resources":true,"completions":false,"roots":false,"sampling":false,"elicitation":false,"structured_outputs":true,"resource_links":true},"enabled_count":3,"enabled":["resource_links","resources","structured_outputs"],"initialize_capability_keys":["resources","tools"]}},"tool_snapshot_probe":{"status":"ok","latency_ms":null,"details":{"current_tool_count":15,"previous_tool_count":15,"similarity":1.0,"added":[],"removed":[],"changed_outputs":[]}},"connector_replay_probe":{"status":"ok","latency_ms":null,"details":{"backward_compatible":true,"would_break_after_refresh":false,"added_tools":[],"removed_tools":[],"required_arg_breaks":[],"output_breaks":[],"additive_output_changes":[]}},"request_association_probe":{"status":"missing","latency_ms":null,"details":{"reason":"no_request_association_capabilities_advertised"}},"interactive_flow_probe":{"status":"ok","latency_ms":null,"details":{"risk_hits":[],"safe_hits":["sign in"],"oauth_supported":false,"prompt_available":false}},"action_safety_probe":{"status":"error","latency_ms":null,"details":{"summary":{"tool_count":15,"high_risk_tools":3,"destructive_tools":4,"exec_tools":0,"egress_tools":5,"secret_tools":0,"bulk_access_tools":0,"declared_non_read_only_tools":9,"annotation_conflict_tools":0,"risk_distribution":{"low":5,"medium":7,"high":3,"critical":0},"capability_distribution":{"read":6,"write":9,"delete":4,"network":5,"filesystem":1},"has_mutating_capability":true,"has_non_read_capability":true},"auth_present":false,"safeguard_count":2,"confirmation_signals":["preview_still","open_in_editor"],"reason":null}},"official_registry_probe":{"status":"missing","latency_ms":null,"details":{"registry_source":"awesome_mcp_servers","direct_match":false,"official_peer_count":0}},"provenance_divergence_probe":{"status":"not_assessed","latency_ms":null,"details":{"direct_official_match":false,"registry_title":null,"server_card_title":null,"registry_version":null,"server_card_version":null,"registry_homepage":null,"server_card_homepage":null,"registry_repository":null,"server_card_repository":null,"drift_fields":[],"metadata_document_count":1,"compared_fields":["title","version","homepage","repository"],"readable_sources":[],"comparable_field_count":0}},"schema_divergence_probe":{"status":"missing","latency_ms":null,"details":{"reason":"no_server_card_tools","compared_tool_count":0,"compared_dimensions":["server_name","server_version","declared_vs_observed_auth","tool_membership","parameter_names","required_parameters","parameter_types","output_schema_presence"],"server_name_mismatch":false,"card_server_name":null,"live_server_name":"clipkit","server_version_mismatch":false,"card_server_version":null,"live_server_version":"0.0.0","auth_scheme_mismatch":false}},"connector_publishability_probe":{"status":"warning","latency_ms":null,"details":{"transport":"streamable-http","tool_count":15,"high_risk_tools":3,"blockers":["protocol_version","session_resume","step_up_auth","transport_compliance","request_association","action_safety","server_card"],"criteria":{"remote_transport":true,"initialize":true,"tools_list":true,"protocol_version":false,"session_resume":false,"step_up_auth":false,"transport_compliance":false,"connector_replay":true,"request_association":false,"action_safety":false,"server_card":false,"tool_surface":true,"auth_flow":true}}}}},"active_alerts":[{"code":"instruction_tool_reference_mismatch","severity":"low","title":"Server instructions name unavailable tools","message":"Initialize instructions reference tools not present in tools/list: an, render_video, this, url","addressee":"publisher"}],"maintainer_analytics":{},"public_server_reputation":{},"maintainer_response_quality":{},"maintainer_annotations":[],"maintainer_rebuttals":[],"security_posture_summary":{},"tool_security_inventory":[],"transport_compliance":{},"utility_coverage":{},"write_action_governance":{},"provenance_divergence":{},"alias_consolidation":{},"alert_routing":{},"authenticated_validation":{},"hosted_runtime":{},"action_controls_diff":null,"benchmark_tasks":[],"latest_capability_counts":{},"point_loss_breakdown":[],"verdict_traces":{},"current_snapshot":{"schema_version":"verify.trust_snapshot.v1","snapshot_id":"trustsnap_cbe6c78c4b9bb52c","generated_at":"2026-09-12T12:09:43.123324+00:00","trust_evaluated_at":"2026-09-12T12:09:43.123324+00:00","evidence_revision":"c54c11538d18ca72c7952ecf","source":"current_snapshot","server":"awesome-clipkit-video/clipkit","last_validated_at":"2026-09-12T09:33:34.449596+00:00","validation_age_hours":2.6,"freshness":{"schema_version":"verify.freshness_profile.v1","last_validated_at":"2026-09-12T09:33:34.449596+00:00","age_hours":2.6,"bucket":"verified_last_24h","label":"Verified in last 24h","badges":["verified_last_24h"],"freshness_sla_hours":720.0,"freshness_sla_status":"met","stale_score_suppressed":false,"display_score":59.9,"raw_score":59.9,"confidence_score":88.8,"confidence_weighted_score":53.2,"tier_status":[{"tier":"community","label":"Community","freshness_sla_hours":720,"met":true,"priority_revalidation":false},{"tier":"pro","label":"Pro","freshness_sla_hours":168,"met":true,"priority_revalidation":true},{"tier":"enterprise","label":"Enterprise","freshness_sla_hours":24,"met":true,"priority_revalidation":true}]},"current_status":"healthy","current_score":59.9,"display_score":59.9,"stale_score_suppressed":false,"production_trust_decision":{"schema_version":"verify.executive_verdict.v1","decision":"Block for production","why":"write/admin actions need confirmation safeguards + score below evaluation threshold","next_action":"revalidate, add safeguards, export policy","reason_count":2},"production_readiness_class":{"code":"needs_remediation","label":"Needs remediation","reason":"Current validation evidence shows operational or discovery gaps that should be fixed first."},"evidence_confidence":{"score":88.75,"label":"high","validation_age_hours":2.6,"live_check_count":15,"basis":{"evidence_bearing_validations":11,"affirmative_live_check_count":15,"validation_age_hours":2.6,"freshness_threshold_hours":24}},"active_alerts":[{"code":"instruction_tool_reference_mismatch","severity":"low","title":"Server instructions name unavailable tools"}],"active_alert_summary":{"critical":0,"high":0,"medium":0,"low":1,"high_or_critical":0,"total":1},"materialization":{"state":"partial","trust_core_complete":true,"fields_unavailable":["tool_security_inventory","security_posture_summary","write_action_governance","capability_taxonomy","remediations"],"materialized_at":"2026-09-12T12:09:43.123324+00:00"}},"trust_snapshot":{"schema_version":"verify.trust_snapshot.v1","snapshot_id":"trustsnap_cbe6c78c4b9bb52c","generated_at":"2026-09-12T12:09:43.123324+00:00","trust_evaluated_at":"2026-09-12T12:09:43.123324+00:00","evidence_revision":"c54c11538d18ca72c7952ecf","source":"current_snapshot","server":"awesome-clipkit-video/clipkit","last_validated_at":"2026-09-12T09:33:34.449596+00:00","validation_age_hours":2.6,"freshness":{"schema_version":"verify.freshness_profile.v1","last_validated_at":"2026-09-12T09:33:34.449596+00:00","age_hours":2.6,"bucket":"verified_last_24h","label":"Verified in last 24h","badges":["verified_last_24h"],"freshness_sla_hours":720.0,"freshness_sla_status":"met","stale_score_suppressed":false,"display_score":59.9,"raw_score":59.9,"confidence_score":88.8,"confidence_weighted_score":53.2,"tier_status":[{"tier":"community","label":"Community","freshness_sla_hours":720,"met":true,"priority_revalidation":false},{"tier":"pro","label":"Pro","freshness_sla_hours":168,"met":true,"priority_revalidation":true},{"tier":"enterprise","label":"Enterprise","freshness_sla_hours":24,"met":true,"priority_revalidation":true}]},"current_status":"healthy","current_score":59.9,"display_score":59.9,"stale_score_suppressed":false,"production_trust_decision":{"schema_version":"verify.executive_verdict.v1","decision":"Block for production","why":"write/admin actions need confirmation safeguards + score below evaluation threshold","next_action":"revalidate, add safeguards, export policy","reason_count":2},"production_readiness_class":{"code":"needs_remediation","label":"Needs remediation","reason":"Current validation evidence shows operational or discovery gaps that should be fixed first."},"evidence_confidence":{"score":88.75,"label":"high","validation_age_hours":2.6,"live_check_count":15,"basis":{"evidence_bearing_validations":11,"affirmative_live_check_count":15,"validation_age_hours":2.6,"freshness_threshold_hours":24}},"active_alerts":[{"code":"instruction_tool_reference_mismatch","severity":"low","title":"Server instructions name unavailable tools"}],"active_alert_summary":{"critical":0,"high":0,"medium":0,"low":1,"high_or_critical":0,"total":1},"materialization":{"state":"partial","trust_core_complete":true,"fields_unavailable":["tool_security_inventory","security_posture_summary","write_action_governance","capability_taxonomy","remediations"],"materialized_at":"2026-09-12T12:09:43.123324+00:00"}},"agent_commerce":{},"latest_claim":null,"maintainer_profile_slug":null,"watch_summary":{},"partial":true,"fields_unavailable":["tool_security_inventory","security_posture_summary","write_action_governance","capability_taxonomy","remediations"],"trust_evaluated_at":"2026-09-12T12:09:43.123324+00:00","evidence_revision":"c54c11538d18ca72c7952ecf","active_alert_summary":{"critical":0,"high":0,"medium":0,"low":1,"high_or_critical":0,"total":1},"materialization":{"state":"partial","trust_core_complete":true,"fields_unavailable":["tool_security_inventory","security_posture_summary","write_action_governance","capability_taxonomy","remediations"],"materialized_at":"2026-09-12T12:09:43.123324+00:00"},"owner_opted_out":false},"latest_validation":null,"current_snapshot":{"schema_version":"verify.trust_snapshot.v1","snapshot_id":"trustsnap_cbe6c78c4b9bb52c","generated_at":"2026-09-12T12:09:43.123324+00:00","trust_evaluated_at":"2026-09-12T12:09:43.123324+00:00","evidence_revision":"c54c11538d18ca72c7952ecf","source":"current_snapshot","server":"awesome-clipkit-video/clipkit","last_validated_at":"2026-09-12T09:33:34.449596+00:00","validation_age_hours":2.6,"freshness":{"schema_version":"verify.freshness_profile.v1","last_validated_at":"2026-09-12T09:33:34.449596+00:00","age_hours":2.6,"bucket":"verified_last_24h","label":"Verified in last 24h","badges":["verified_last_24h"],"freshness_sla_hours":720.0,"freshness_sla_status":"met","stale_score_suppressed":false,"display_score":59.9,"raw_score":59.9,"confidence_score":88.8,"confidence_weighted_score":53.2,"tier_status":[{"tier":"community","label":"Community","freshness_sla_hours":720,"met":true,"priority_revalidation":false},{"tier":"pro","label":"Pro","freshness_sla_hours":168,"met":true,"priority_revalidation":true},{"tier":"enterprise","label":"Enterprise","freshness_sla_hours":24,"met":true,"priority_revalidation":true}]},"current_status":"healthy","current_score":59.9,"display_score":59.9,"stale_score_suppressed":false,"production_trust_decision":{"schema_version":"verify.executive_verdict.v1","decision":"Block for production","why":"write/admin actions need confirmation safeguards + score below evaluation threshold","next_action":"revalidate, add safeguards, export policy","reason_count":2},"production_readiness_class":{"code":"needs_remediation","label":"Needs remediation","reason":"Current validation evidence shows operational or discovery gaps that should be fixed first."},"evidence_confidence":{"score":88.75,"label":"high","validation_age_hours":2.6,"live_check_count":15,"basis":{"evidence_bearing_validations":11,"affirmative_live_check_count":15,"validation_age_hours":2.6,"freshness_threshold_hours":24}},"active_alerts":[{"code":"instruction_tool_reference_mismatch","severity":"low","title":"Server instructions name unavailable tools"}],"active_alert_summary":{"critical":0,"high":0,"medium":0,"low":1,"high_or_critical":0,"total":1},"materialization":{"state":"partial","trust_core_complete":true,"fields_unavailable":["tool_security_inventory","security_posture_summary","write_action_governance","capability_taxonomy","remediations"],"materialized_at":"2026-09-12T12:09:43.123324+00:00"}},"trust_snapshot":{"schema_version":"verify.trust_snapshot.v1","snapshot_id":"trustsnap_cbe6c78c4b9bb52c","generated_at":"2026-09-12T12:09:43.123324+00:00","trust_evaluated_at":"2026-09-12T12:09:43.123324+00:00","evidence_revision":"c54c11538d18ca72c7952ecf","source":"current_snapshot","server":"awesome-clipkit-video/clipkit","last_validated_at":"2026-09-12T09:33:34.449596+00:00","validation_age_hours":2.6,"freshness":{"schema_version":"verify.freshness_profile.v1","last_validated_at":"2026-09-12T09:33:34.449596+00:00","age_hours":2.6,"bucket":"verified_last_24h","label":"Verified in last 24h","badges":["verified_last_24h"],"freshness_sla_hours":720.0,"freshness_sla_status":"met","stale_score_suppressed":false,"display_score":59.9,"raw_score":59.9,"confidence_score":88.8,"confidence_weighted_score":53.2,"tier_status":[{"tier":"community","label":"Community","freshness_sla_hours":720,"met":true,"priority_revalidation":false},{"tier":"pro","label":"Pro","freshness_sla_hours":168,"met":true,"priority_revalidation":true},{"tier":"enterprise","label":"Enterprise","freshness_sla_hours":24,"met":true,"priority_revalidation":true}]},"current_status":"healthy","current_score":59.9,"display_score":59.9,"stale_score_suppressed":false,"production_trust_decision":{"schema_version":"verify.executive_verdict.v1","decision":"Block for production","why":"write/admin actions need confirmation safeguards + score below evaluation threshold","next_action":"revalidate, add safeguards, export policy","reason_count":2},"production_readiness_class":{"code":"needs_remediation","label":"Needs remediation","reason":"Current validation evidence shows operational or discovery gaps that should be fixed first."},"evidence_confidence":{"score":88.75,"label":"high","validation_age_hours":2.6,"live_check_count":15,"basis":{"evidence_bearing_validations":11,"affirmative_live_check_count":15,"validation_age_hours":2.6,"freshness_threshold_hours":24}},"active_alerts":[{"code":"instruction_tool_reference_mismatch","severity":"low","title":"Server instructions name unavailable tools"}],"active_alert_summary":{"critical":0,"high":0,"medium":0,"low":1,"high_or_critical":0,"total":1},"materialization":{"state":"partial","trust_core_complete":true,"fields_unavailable":["tool_security_inventory","security_posture_summary","write_action_governance","capability_taxonomy","remediations"],"materialized_at":"2026-09-12T12:09:43.123324+00:00"}},"partial":true,"fields_unavailable":["tool_security_inventory","security_posture_summary","write_action_governance","capability_taxonomy","remediations"],"snapshot_invariant":{"schema_version":"verify.snapshot_invariant.v1","server":"awesome-clipkit-video/clipkit","ok":true,"surface_snapshot_ids":{"page":"trustsnap_cbe6c78c4b9bb52c","badge":null,"report":"trustsnap_cbe6c78c4b9bb52c","policy":null},"checked_surfaces":["page","report"],"unchecked_surfaces":["badge","policy"],"checked_count_surfaces":[],"count_mismatches":{},"checked_at":"2026-09-12T12:09:43.112477+00:00"},"history":{},"production_readiness":{"code":"needs_remediation","label":"Needs remediation","reason":"Current validation evidence shows operational or discovery gaps that should be fixed first.","badge":"score-low","critical_alerts":0,"high_or_critical_alerts":0,"degraded_by_active_alerts":false},"agent_commerce_readiness":{},"evidence_confidence":{"score":88.75,"label":"high","reason":"Based on 11 recent validations, 15 captured checks, and validation age of 2.6 hours.","live_check_count":15,"validation_age_hours":2.6,"basis":{"evidence_bearing_validations":11,"affirmative_live_check_count":15,"validation_age_hours":2.6,"freshness_threshold_hours":24}},"recommended_for":[],"active_alerts":[{"code":"instruction_tool_reference_mismatch","severity":"low","title":"Server instructions name unavailable tools","message":"Initialize instructions reference tools not present in tools/list: an, render_video, this, url","addressee":"publisher"}],"remediations":[],"cache_note":"Fast fallback response; full report evidence is deferred to protect web capacity. Fields listed in fields_unavailable (including tool_security_inventory, and write_action_governance) are not yet computed on this response -- an empty value means unchecked, not confirmed clean. Treat partial responses as indeterminate.","publisher_claim":{"verified":false,"status":"unclaimed","claim_url":"https://verify.sentinelsignal.io/claim?server=awesome-clipkit-video%2Fclipkit&source=report_json","reason_code":"claim_to_publish_metadata","reason":"Claim this profile to verify publisher identity, add evidence, and manage trust metadata.","score_neutral":true},"observed_attention":{"schema":"verify.observed_attention.v1","window_days":30,"level":"none","label":"No observed attention","summary":"No recent machine-readable trust or discovery activity observed for this server.","segments":{"useful_ai_user":{"level":"none","observed":false,"description":"AI-assisted user sessions such as ChatGPT/User or Claude/User."},"machine_trust_evaluator":{"level":"none","observed":false,"description":"Synthetic sessions inspecting multiple trust surfaces such as report, policy, ledger, badge, trust-summary, or compare."},"possible_agent_or_script":{"level":"none","observed":false,"description":"Structured direct sessions with rapid profile, compare, report, policy, badge, or trust-surface fan-out."},"isolated_machine_surface":{"level":"none","observed":false,"description":"Aged-out direct synthetic singleton sessions that touched a machine-readable trust surface without becoming a broader evaluator."},"ai_crawler":{"level":"none","observed":false,"description":"Known AI crawler activity such as ClaudeBot, GPTBot, or similar crawlers."},"search_crawler":{"level":"none","observed":false,"description":"Search and SEO crawler activity."},"browser_like_automation":{"level":"none","observed":false,"description":"Browser-like synthetic sessions with rapid structured endpoint activity."},"confirmed_human":{"level":"none","observed":false,"description":"Confirmed browser-session human activity."}},"surfaces_observed":{"server_profile":false,"compare":false,"compare_json":false,"compare_api":false,"report_json":false,"policy":false,"ledger":false,"badge_metadata":false,"badge_svg":false,"trust_summary":false,"mcp_tool":false},"claim_prompt":{"recommended":false,"reason":"No claim prompt is recommended from observed attention in the current window."},"notes":["Observed attention is based on segmented first-party telemetry.","Crawler and evaluator activity is not treated as confirmed human demand.","Public levels are bucketed to avoid exposing raw traffic counts."]},"owner_activation":{"claim_recommended":false,"reason":"no_observed_attention"},"related_machine_surfaces":{"compare_index":"/compare.json","compare_api":"/v1/compare?server=awesome-clipkit-video%2Fclipkit","trust_summary":"/v1/servers/awesome-clipkit-video/clipkit/trust-summary","ledger":"/v1/servers/awesome-clipkit-video/clipkit/ledger","policy":"/v1/servers/awesome-clipkit-video/clipkit/policy","report":"/v1/servers/awesome-clipkit-video/clipkit/report"},"intelligence_api":{"available":true,"signup_url":"https://verify.sentinelsignal.io/verify-intelligence-api","use_case":"Programmatic MCP server trust, comparison, policy, and evidence enrichment."},"trust_evaluated_at":"2026-09-12T12:09:43.123324+00:00","evidence_revision":"c54c11538d18ca72c7952ecf","active_alert_summary":{"critical":0,"high":0,"medium":0,"low":1,"high_or_critical":0,"total":1},"materialization":{"state":"partial","trust_core_complete":true,"fields_unavailable":["tool_security_inventory","security_posture_summary","write_action_governance","capability_taxonomy","remediations"],"materialized_at":"2026-09-12T12:09:43.123324+00:00"}}