Framelane runs a hosted, authenticated MCP (Model Context Protocol) action server at:
https://mcp.framelane.io/mcp
Any MCP host — Claude, Claude Desktop, Cursor, VS Code, LangChain, CrewAI, etc. — can
connect and drive Framelane over MCP without writing HTTP integration code. It speaks the
Streamable HTTP transport, and every tool requires authentication (see below). It
is an action server — it renders, runs tasks, and edits projects. For documentation,
agents read the static channels instead: llms.txt, the
installable skill, or any docs page as raw markdown
(append .md to its URL).
Onboarding happens over REST, not MCP — an MCP client connects after it holds a key:
POST /v1/signup with a workspace_name and an email you control — returns an fl_
API key and emails a 6-digit code.
POST /v1/signup/verify with the code. The key works the moment this succeeds; until
then authed calls return 403 email_not_verified.
GET /v1/capabilities (no auth) returns an onboarding block describing this loop plus the
action_mcp_url in machine-readable form. See Self-provisioning for
agents. Interactive clients can instead authenticate via
OAuth 2.1 (PKCE) — Framelane is an OAuth resource server and delegates the login to
Clerk as the authorization server; MCP hosts discover it automatically from the server’s
WWW-Authenticate challenge.
Every tool requires a credential (an fl_ key or a Clerk OAuth token), and every tool that
acts on your workspace requires a verified one — get_capabilities is the exception, so
an unverified workspace can still read the onboarding block that explains how to verify.
The full set, 30 tools (one of them panel-only):
Tool
Does
get_capabilities
The machine-readable catalog, sliced: call it bare for an index of every section and its size, then section='request_schema' / 'element_schemas.text' / 'catalogs'
create_render
Submit a render job
wait_for_render / get_render
Await terminal status (streams progress) / fetch status
get_render_download
Signed download URL for a completed render
list_renders / cancel_render
List / cancel renders
run_task
Run an AI task (transcribe)
wait_for_task / get_task
Await / fetch a task — structured output arrives on result (transcribe returns its transcript there)
get_task_download
Signed download URL for a task whose artifact is a file; not_found for transcribe, which has none
create_preview
Validate (dry_run) or cheaply preview a composition
get_render_preview
See a finished preview as an inline image — a contact_sheet preview returns the grid PNG inline for a single vision call; a full-video render returns a download_url instead
The stateful, versioned edit loop. Every project receipt carries an editor_url that opens the project in the console for a person to edit; list_projects is cursor-paginated, newest first; get_project takes view (full, the default, or summary to omit the composition) and also reads a published template from the gallery, read-only
list_templates
Templates to start from: the public gallery plus your workspace’s own, newest first and cursor-paginated. Filter with scope (public, workspace, or all, the default), category, q (name, title or description), aspect and family (the sizes of one design). Start a project from one with create_project and its id as source_project_id, or swap one into an open project with an apply_template op in edit_project
Pull a palette, fonts, logo and corner feel from a website URL and reuse them across renders
list_assets
The workspace’s reusable media (video, audio, images), newest first. Each item’s source URL drops straight into an element. Filter with kind
get_asset
One asset by id, including one not listed yet. ready: false means the duration probe is still running, so poll it rather than treating the id as missing. bytes_uploaded: false is the other answer: the row is a reservation whose upload never completed
get_asset_preview
See an image asset as an inline image. The surest way to tell one stored logo from another: an object key is a uuid, and filename is caller-supplied and absent on older assets. SVG/TIFF and oversized files come back as a URL and a note
create_upload
Put a local video, audio or image file into the workspace. On hosts that render interactive panels (Claude.ai, Claude Desktop, ChatGPT, Cursor, VS Code) a file picker appears with the result: the user drops the file there and the panel reports the final asset id. Under 256 KB, send the bytes as base64 and the one call stores them (uploaded: true) — no outbound request of your own, which is what makes it work inside a sandbox. Larger, it returns a signed upload URL you PUT to yourself; those bytes never pass through the server, so a successful call means a slot exists, not that a file arrived
replace_upload_slot
Panel-only (hidden from the model): swaps an empty slot for one matching the file the user actually picked, deleting the empty one
create_upload_link / get_upload_link
Hand a person a one-hour web page to upload one file when you can neither PUT from where you run nor show a panel; poll the link for the asset it produced
Start by calling the get_capabilities tool. Call it with no argument for a small
index naming every catalog section and its byte size, then fetch the sections you need.
There are three tiers, and an agent needs all three:
section='catalogs' — every flat catalog in one slice (effects, motions,
transitions, blend_modes, easings, word_animations, motion_scopes,
output_formats, input_formats, element_types, task_types, rate_limits) —
what exists, each flagged with whether it is supported today. Fetch one on its own
with section='effects', section='motions', and so on.
section='element_schemas' — the full JSON Schema of every field each element type
accepts (video, audio, text, image, shape), nested motion /
effects / counter / glow / trim_keyframes included. It is by far the largest
section — the index reports its exact size — so prefer one type at a time:
section='element_schemas.text'.
section='request_schema' — the composition-level body: groups,
custom_animations, motion_blur, background_gradient, watermark_url,
transitions, canvas and output settings. None of these appear in the flat catalogs.
Read per-element fields from element_schemas, not request_schema — the latter
deliberately elides them. motions[] also carries a per-preset surface matrix
(element_entrance / element_exit / character / text_block); check it before picking
a preset, since a preset used on the wrong surface is a 422.Every response is wrapped as {section, api_version, data} — the slice you asked for
is under data. An unknown section is an invalid_request error carrying
valid_sections and a suggestion, so a typo is self-correcting.
Composition bodies are plain JSON objects.create_render.body,
create_preview.request, create_project.body.render_request and the replace_request
op all take the composition as an ordinary object; their schema comes from
get_capabilities, deliberately not inlined into the tool definitions. That keeps
every tool’s input schema under 4 KB, so size-capped MCP clients keep the whole toolset
instead of silently dropping the ones that create things.Validation is unchanged: the server validates each body against the same model the REST
API uses, unknown keys are still rejected, and a bad field comes back as
invalid_request with an RFC 6901 pointer (e.g. /elements/0/font_wight) and a
suggestion (font_weight).
For visual review, use the native preview.create_preview (or preview_project)
at a frame or with a contact_sheet option → wait_for_render → get_render_preview
returns what the export engine renders, as an inline image, before you pay for a full
render.Open the browser only for editor interaction, such as handing a project to a person
through its editor_url. The editor draws its own preview in the browser, which can
differ from the export (text shaping, for example), so it is not evidence of how a render
looks.
Media an agent can reach.list_assets serves exactly the rows
GET /v1/workspace/assets serves (one shared query, so kind and readiness cannot
drift), and its cursors are interchangeable with the REST ones.Two timing models, and mixing them up is the usual 422. For video and audio,
duration is the source file’s full length: omit an element’s out_point and the
server fills it from that, or set in_point / out_point to trim. An image has no
duration (always null, on every path) and no out_point; its element carries a
requiredduration of its own, in seconds on screen, which you choose.ready is derived, not stored: video and audio become ready when the duration probe
lands, an image as soon as its bytes are in the bucket. list_assets shows only ready
assets, so get_asset is what you poll after an upload.To add a local file, call create_upload (or POST /v1/uploads over REST). There
are two routes and the size decides which. Under 256 KB — logos, crests, icons,
SVG overlays — pass the file’s bytes as base64 and that single call stores them; it
makes no outbound request on your behalf and asks none of you, so it is the route that
works from a sandbox whose egress is allow-listed per host. Larger files come back
with a signed upload URL you PUT to, in one request, with the same Content-Type;
those bytes go straight to storage, so the server cannot confirm them and get_asset
has to check — it answers bytes_uploaded. If your own network refuses that host,
the result also carries a first-party upload URL on uploads.framelane.io — the same
session, one Framelane domain to allow instead of Google’s. If that is refused too,
the file is either small enough to inline or needs to be reachable at a public URL.Passing the duration you probed locally makes a video or audio asset usable
immediately instead of waiting on the server probe. A file already at a public https
URL needs none of this — pass the URL as an element’s source URL and it is fetched at
the workspace on first render.
Waiting is one tool call, not a loop.wait_for_render / wait_for_task block until the
job is terminal, streaming progress as it advances. If a call returns still_running: true
(the server hold elapsed), call again. Ensure your MCP client’s per-tool timeout is ≥ 30s.
Tool errors carry a stable machine code. A domain failure is not a transport error: it
comes back in the tool result as {"error": {"code", "message", "details"}} —
branch on code. The set is invalid_request, not_found, conflict,
quota_exceeded, rate_limited, email_not_verified, unauthorized,
invalid_source (extract_brand_kit could not read the URL),
unsupported_content_type (create_upload was given a type we do not store; the
details.allowed list says what to re-encode to), inline_too_large (the base64 you
sent create_upload is past the 256 KB inline cap — call it again without the bytes
and PUT to the upload_url it returns), plan_required (create_project or
edit_project made or used one of your own templates while your plan does not include
custom templates; details.required_plan names the plan, and public templates are
never gated) and internal. Only an unexpected exception surfaces as a real tool error.rate_limited is per workspace, not per IP, and only the submission tools are
metered — create_render, render_project and run_task. Previews
(create_preview / preview_project), reads and get_capabilities are unmetered.
Its details carry limit_per_minute and retry_after_seconds; wait that long and
retry rather than backing off blindly.
The Framelane Agent Skills are served by this server: each
workflow skill is an MCP prompt, and every skill is also a get_capabilities section named
skills.<name> (section='skills' lists them) for hosts that do not show prompts. Load
one, then drive the calls through the tools above. How-tos for the REST API are in the
Guides.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Framelane","colors":{"primary":"#0070f3","light":"#3b82f6","dark":"#1d4ed8"},"logo":{"light":"https://mintcdn.com/itis-c7dc7146/I-4ZfymyiJyBmcDs/logo/framelane-light.png?fit=max\u0026auto=format\u0026n=I-4ZfymyiJyBmcDs\u0026q=85\u0026s=a819adfb6e5b9a97575f20c034d41c77","dark":"https://mintcdn.com/itis-c7dc7146/I-4ZfymyiJyBmcDs/logo/framelane-dark.png?fit=max\u0026auto=format\u0026n=I-4ZfymyiJyBmcDs\u0026q=85\u0026s=14e93bd67dd1fedf3c655cbebd17e046"},"favicon":"https://mintcdn.com/itis-c7dc7146/I-4ZfymyiJyBmcDs/favicon.ico?fit=max\u0026auto=format\u0026n=I-4ZfymyiJyBmcDs\u0026q=85\u0026s=a0cbeea069121bb5f6014ae0b57c8b8b","api":{"playground":{"display":"interactive"}},"navbar":{"links":[{"href":"https://console.framelane.io","label":"Console"}],"primary":{"type":"button","label":"Get API Key","href":"https://console.framelane.io/signup"}},"footer":{"socials":{"x":"https://twitter.com/framelane","github":"https://github.com/framelane"}},"seo":{"metatags":{"og:type":"website","og:site_name":"Framelane","og:image":"https://cdn-assets.framelane.io/shared/images/og-docs.png","robots":"index, follow"},"indexing":"all"},"contextual":{"options":["copy","view","claude","chatgpt","download-spec"],"display":"header"},"styling":{"latex":false},"navigation":{"global":{"anchors":[{"anchor":"GitHub","icon":"github","href":"https://github.com/framelane"},{"anchor":"Status","icon":"signal-bars","href":"https://status.framelane.io"}]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"itis-c7dc7146","hasAudioNarration":false,"actualSubdomain":"itis-c7dc7146","customDomains":["docs.framelane.io"],"gitSource":{"type":"github","owner":"itishq","repo":"framelane-api","deployBranch":"main","contentDirectory":"docs","isPrivate":true,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6adf86d0737ceb5e7109b3ae","collectionId":"43553988-d245-46c7-9e69-6860d4cdb8de","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6adf86d0737ceb5e7109b3ae"},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6adf86d0737ceb5e7109b3ae","clientVersion":"0.0.3971","searchFilterCounts":[{"language":"default","tag":"API Reference","count":65},{"language":"default","tag":"Documentation","count":41},{"language":"default","tag":"Webhooks","count":13},{"language":"default","tag":"Projects","count":10},{"language":"default","tag":"Renders","count":10},{"language":"default","tag":"Get Started","count":9},{"language":"default","tag":"Render Examples","count":9},{"language":"default","tag":"Account","count":9},{"language":"default","tag":"Guides","count":7},{"language":"default","tag":"Tasks","count":7}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"basePath":"","hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"mcp/overview","slug":"/mcp/overview","pageMetadata":{"title":"MCP Server","description":"Connect Framelane to Claude, Cursor, or any MCP-capable agent framework.","href":"/mcp/overview"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Connect Framelane to Claude, Cursor, or any MCP-capable agent framework.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,em:`em`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{CodeBlock,Heading,Table,Tip}=_components;if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Tip)_missingMdxReference(`Tip`,true);return _jsxs(_Fragment,{children:[_jsx(Heading,{level:`2`,id:`overview`,children:`Overview`}),`\n`,_jsxs(_components.p,{children:[`Framelane runs a hosted, authenticated `,_jsx(_components.strong,{children:`MCP (Model Context Protocol) action server`}),` at:`]}),`\n`,_jsx(CodeBlock,{numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`https://mcp.framelane.io/mcp`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),`\n`,_jsxs(_components.p,{children:[`Any MCP host — Claude, Claude Desktop, Cursor, VS Code, LangChain, CrewAI, etc. — can\nconnect and drive Framelane over MCP without writing HTTP integration code. It speaks the\n`,_jsx(_components.strong,{children:`Streamable HTTP`}),` transport, and `,_jsx(_components.strong,{children:`every tool requires authentication`}),` (see below). It\nis an `,_jsx(_components.em,{children:`action`}),` server — it renders, runs tasks, and edits projects. For documentation,\nagents read the static channels instead: `,_jsx(_components.a,{href:`https://framelane.io/llms.txt`,children:_jsx(_components.code,{children:`llms.txt`})}),`, the\ninstallable `,_jsx(_components.a,{href:`https://framelane.io/skill.md`,children:`skill`}),`, or any docs page as raw markdown\n(append `,_jsx(_components.code,{children:`.md`}),` to its URL).`]}),`\n`,_jsx(Heading,{level:`2`,id:`getting-a-credential-first`,children:`Getting a credential first`}),`\n`,_jsxs(_components.p,{children:[`Onboarding happens over `,_jsx(_components.strong,{children:`REST`}),`, not MCP — an MCP client connects `,_jsx(_components.em,{children:`after`}),` it holds a key:`]}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`POST /v1/signup`}),` with a `,_jsx(_components.code,{children:`workspace_name`}),` and an `,_jsx(_components.code,{children:`email`}),` you control — returns an `,_jsx(_components.code,{children:`fl_`}),`\nAPI key and emails a 6-digit code.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`POST /v1/signup/verify`}),` with the code. The key works the moment this succeeds; until\nthen authed calls return `,_jsx(_components.code,{children:`403 email_not_verified`}),`.`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[_jsx(_components.code,{children:`GET /v1/capabilities`}),` (no auth) returns an `,_jsx(_components.code,{children:`onboarding`}),` block describing this loop plus the\n`,_jsx(_components.code,{children:`action_mcp_url`}),` in machine-readable form. See `,_jsx(_components.a,{href:`/introduction/self-provisioning`,children:`Self-provisioning for\nagents`}),`. Interactive clients can instead authenticate via\n`,_jsx(_components.strong,{children:`OAuth 2.1 (PKCE)`}),` — Framelane is an OAuth resource server and delegates the login to\nClerk as the authorization server; MCP hosts discover it automatically from the server’s\n`,_jsx(_components.code,{children:`WWW-Authenticate`}),` challenge.`]}),`\n`,_jsx(Heading,{level:`2`,id:`key-tools`,children:`Key tools`}),`\n`,_jsxs(_components.p,{children:[`Every tool requires a credential (an `,_jsx(_components.code,{children:`fl_`}),` key or a Clerk OAuth token), and every tool that\nacts on your workspace requires a `,_jsx(_components.strong,{children:`verified`}),` one — `,_jsx(_components.code,{children:`get_capabilities`}),` is the exception, so\nan unverified workspace can still read the `,_jsx(_components.code,{children:`onboarding`}),` block that explains how to verify.\nThe full set, 30 tools (one of them panel-only):`]}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{children:`Tool`}),_jsx(_components.th,{children:`Does`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`get_capabilities`})}),_jsxs(_components.td,{children:[`The machine-readable catalog, sliced: call it bare for an index of every section and its size, then `,_jsx(_components.code,{children:`section='request_schema'`}),` / `,_jsx(_components.code,{children:`'element_schemas.text'`}),` / `,_jsx(_components.code,{children:`'catalogs'`})]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`create_render`})}),_jsx(_components.td,{children:`Submit a render job`})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`wait_for_render`}),` / `,_jsx(_components.code,{children:`get_render`})]}),_jsx(_components.td,{children:`Await terminal status (streams progress) / fetch status`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`get_render_download`})}),_jsx(_components.td,{children:`Signed download URL for a completed render`})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`list_renders`}),` / `,_jsx(_components.code,{children:`cancel_render`})]}),_jsx(_components.td,{children:`List / cancel renders`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`run_task`})}),_jsxs(_components.td,{children:[`Run an AI task (`,_jsx(_components.code,{children:`transcribe`}),`)`]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`wait_for_task`}),` / `,_jsx(_components.code,{children:`get_task`})]}),_jsxs(_components.td,{children:[`Await / fetch a task — structured output arrives on `,_jsx(_components.code,{children:`result`}),` (`,_jsx(_components.code,{children:`transcribe`}),` returns its transcript there)`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`get_task_download`})}),_jsxs(_components.td,{children:[`Signed download URL for a task whose artifact is a `,_jsx(_components.strong,{children:`file`}),`; `,_jsx(_components.code,{children:`not_found`}),` for `,_jsx(_components.code,{children:`transcribe`}),`, which has none`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`create_preview`})}),_jsxs(_components.td,{children:[`Validate (`,_jsx(_components.code,{children:`dry_run`}),`) or cheaply preview a composition`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`get_render_preview`})}),_jsxs(_components.td,{children:[`See a finished preview `,_jsx(_components.strong,{children:`as an inline image`}),` — a `,_jsx(_components.code,{children:`contact_sheet`}),` preview returns the grid PNG inline for a single vision call; a full-video render returns a `,_jsx(_components.code,{children:`download_url`}),` instead`]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`create_project`}),` / `,_jsx(_components.code,{children:`list_projects`}),` / `,_jsx(_components.code,{children:`get_project`}),` / `,_jsx(_components.code,{children:`edit_project`}),` / `,_jsx(_components.code,{children:`preview_project`}),` / `,_jsx(_components.code,{children:`render_project`})]}),_jsxs(_components.td,{children:[`The stateful, versioned edit loop. Every project receipt carries an `,_jsx(_components.code,{children:`editor_url`}),` that opens the project in the console for a person to edit; `,_jsx(_components.code,{children:`list_projects`}),` is cursor-paginated, newest first; `,_jsx(_components.code,{children:`get_project`}),` takes `,_jsx(_components.code,{children:`view`}),` (`,_jsx(_components.code,{children:`full`}),`, the default, or `,_jsx(_components.code,{children:`summary`}),` to omit the composition) and also reads a published template from the gallery, read-only`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`list_templates`})}),_jsxs(_components.td,{children:[`Templates to start from: the public gallery plus your workspace’s own, newest first and cursor-paginated. Filter with `,_jsx(_components.code,{children:`scope`}),` (`,_jsx(_components.code,{children:`public`}),`, `,_jsx(_components.code,{children:`workspace`}),`, or `,_jsx(_components.code,{children:`all`}),`, the default), `,_jsx(_components.code,{children:`category`}),`, `,_jsx(_components.code,{children:`q`}),` (name, title or description), `,_jsx(_components.code,{children:`aspect`}),` and `,_jsx(_components.code,{children:`family`}),` (the sizes of one design). Start a project from one with `,_jsx(_components.code,{children:`create_project`}),` and its `,_jsx(_components.code,{children:`id`}),` as `,_jsx(_components.code,{children:`source_project_id`}),`, or swap one into an open project with an `,_jsx(_components.code,{children:`apply_template`}),` op in `,_jsx(_components.code,{children:`edit_project`})]})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`extract_brand_kit`}),` / `,_jsx(_components.code,{children:`list_brand_kits`}),` / `,_jsx(_components.code,{children:`get_brand_kit`})]}),_jsx(_components.td,{children:`Pull a palette, fonts, logo and corner feel from a website URL and reuse them across renders`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`list_assets`})}),_jsxs(_components.td,{children:[`The workspace’s reusable media (video, audio, images), newest first. Each item’s source URL drops straight into an element. Filter with `,_jsx(_components.code,{children:`kind`})]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`get_asset`})}),_jsxs(_components.td,{children:[`One asset by id, `,_jsx(_components.strong,{children:`including one not listed yet`}),`. `,_jsx(_components.code,{children:`ready: false`}),` means the duration probe is still running, so poll it rather than treating the id as missing. `,_jsx(_components.code,{children:`bytes_uploaded: false`}),` is the other answer: the row is a reservation whose upload never completed`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`get_asset_preview`})}),_jsxs(_components.td,{children:[`See an image asset `,_jsx(_components.strong,{children:`as an inline image`}),`. The surest way to tell one stored logo from another: an object key is a uuid, and `,_jsx(_components.code,{children:`filename`}),` is caller-supplied and absent on older assets. SVG/TIFF and oversized files come back as a URL and a note`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`create_upload`})}),_jsxs(_components.td,{children:[`Put a `,_jsx(_components.strong,{children:`local`}),` video, audio or image file into the workspace. On hosts that render interactive panels (Claude.ai, Claude Desktop, ChatGPT, Cursor, VS Code) a file picker appears with the result: the user drops the file there and the panel reports the final asset id. Under 256 KB, send the bytes as base64 and the one call stores them (`,_jsx(_components.code,{children:`uploaded: true`}),`) — no outbound request of your own, which is what makes it work inside a sandbox. Larger, it returns a signed upload URL you PUT to yourself; those bytes never pass through the server, so a successful call means a slot exists, not that a file arrived`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{children:_jsx(_components.code,{children:`replace_upload_slot`})}),_jsx(_components.td,{children:`Panel-only (hidden from the model): swaps an empty slot for one matching the file the user actually picked, deleting the empty one`})]}),_jsxs(_components.tr,{children:[_jsxs(_components.td,{children:[_jsx(_components.code,{children:`create_upload_link`}),` / `,_jsx(_components.code,{children:`get_upload_link`})]}),_jsx(_components.td,{children:`Hand a person a one-hour web page to upload one file when you can neither PUT from where you run nor show a panel; poll the link for the asset it produced`})]})]})]}),`\n`,_jsxs(Tip,{children:[_jsxs(_components.p,{children:[_jsxs(_components.strong,{children:[`Start by calling the `,_jsx(_components.code,{children:`get_capabilities`}),` tool.`]}),` Call it with no argument for a small\nindex naming every catalog section and its byte size, then fetch the sections you need.\nThere are three tiers, and an agent needs all three:`]}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`section='catalogs'`})}),` — every flat catalog in one slice (`,_jsx(_components.code,{children:`effects`}),`, `,_jsx(_components.code,{children:`motions`}),`,\n`,_jsx(_components.code,{children:`transitions`}),`, `,_jsx(_components.code,{children:`blend_modes`}),`, `,_jsx(_components.code,{children:`easings`}),`, `,_jsx(_components.code,{children:`word_animations`}),`, `,_jsx(_components.code,{children:`motion_scopes`}),`,\n`,_jsx(_components.code,{children:`output_formats`}),`, `,_jsx(_components.code,{children:`input_formats`}),`, `,_jsx(_components.code,{children:`element_types`}),`, `,_jsx(_components.code,{children:`task_types`}),`, `,_jsx(_components.code,{children:`rate_limits`}),`) —\nwhat exists, each flagged with whether it is supported today. Fetch one on its own\nwith `,_jsx(_components.code,{children:`section='effects'`}),`, `,_jsx(_components.code,{children:`section='motions'`}),`, and so on.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`section='element_schemas'`})}),` — the full JSON Schema of every field each element type\naccepts (`,_jsx(_components.code,{children:`video`}),`, `,_jsx(_components.code,{children:`audio`}),`, `,_jsx(_components.code,{children:`text`}),`, `,_jsx(_components.code,{children:`image`}),`, `,_jsx(_components.code,{children:`shape`}),`), nested `,_jsx(_components.code,{children:`motion`}),` /\n`,_jsx(_components.code,{children:`effects`}),` / `,_jsx(_components.code,{children:`counter`}),` / `,_jsx(_components.code,{children:`glow`}),` / `,_jsx(_components.code,{children:`trim_keyframes`}),` included. It is by far the largest\nsection — the index reports its exact size — so prefer one type at a time:\n`,_jsx(_components.code,{children:`section='element_schemas.text'`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`section='request_schema'`})}),` — the composition-level body: `,_jsx(_components.code,{children:`groups`}),`,\n`,_jsx(_components.code,{children:`custom_animations`}),`, `,_jsx(_components.code,{children:`motion_blur`}),`, `,_jsx(_components.code,{children:`background_gradient`}),`, `,_jsx(_components.code,{children:`watermark_url`}),`,\n`,_jsx(_components.code,{children:`transitions`}),`, canvas and output settings. None of these appear in the flat catalogs.`]}),`\n`]}),_jsxs(_components.p,{children:[`Read per-element fields from `,_jsx(_components.code,{children:`element_schemas`}),`, not `,_jsx(_components.code,{children:`request_schema`}),` — the latter\ndeliberately elides them. `,_jsx(_components.code,{children:`motions[]`}),` also carries a per-preset surface matrix\n(`,_jsx(_components.code,{children:`element_entrance`}),` / `,_jsx(_components.code,{children:`element_exit`}),` / `,_jsx(_components.code,{children:`character`}),` / `,_jsx(_components.code,{children:`text_block`}),`); check it before picking\na preset, since a preset used on the wrong surface is a `,_jsx(_components.code,{children:`422`}),`.`]}),_jsxs(_components.p,{children:[`Every response is wrapped as `,_jsx(_components.code,{children:`{section, api_version, data}`}),` — the slice you asked for\nis under `,_jsx(_components.code,{children:`data`}),`. An unknown section is an `,_jsx(_components.code,{children:`invalid_request`}),` error carrying\n`,_jsx(_components.code,{children:`valid_sections`}),` and a `,_jsx(_components.code,{children:`suggestion`}),`, so a typo is self-correcting.`]})]}),`\n`,_jsxs(Tip,{children:[_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Composition bodies are plain JSON objects.`}),` `,_jsx(_components.code,{children:`create_render.body`}),`,\n`,_jsx(_components.code,{children:`create_preview.request`}),`, `,_jsx(_components.code,{children:`create_project.body.render_request`}),` and the `,_jsx(_components.code,{children:`replace_request`}),`\nop all take the composition as an ordinary object; their schema comes from\n`,_jsx(_components.code,{children:`get_capabilities`}),`, deliberately `,_jsx(_components.strong,{children:`not`}),` inlined into the tool definitions. That keeps\nevery tool’s input schema under 4 KB, so size-capped MCP clients keep the whole toolset\ninstead of silently dropping the ones that create things.`]}),_jsxs(_components.p,{children:[`Validation is unchanged: the server validates each body against the same model the REST\nAPI uses, unknown keys are still rejected, and a bad field comes back as\n`,_jsx(_components.code,{children:`invalid_request`}),` with an RFC 6901 `,_jsx(_components.code,{children:`pointer`}),` (e.g. `,_jsx(_components.code,{children:`/elements/0/font_wight`}),`) and a\n`,_jsx(_components.code,{children:`suggestion`}),` (`,_jsx(_components.code,{children:`font_weight`}),`).`]})]}),`\n`,_jsxs(Tip,{children:[_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`For visual review, use the native preview.`}),` `,_jsx(_components.code,{children:`create_preview`}),` (or `,_jsx(_components.code,{children:`preview_project`}),`)\nat a frame or with a `,_jsx(_components.code,{children:`contact_sheet`}),` option → `,_jsx(_components.code,{children:`wait_for_render`}),` → `,_jsx(_components.code,{children:`get_render_preview`}),`\nreturns what the export engine renders, as an inline image, before you pay for a full\nrender.`]}),_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Open the browser only for editor interaction`}),`, such as handing a project to a person\nthrough its `,_jsx(_components.code,{children:`editor_url`}),`. The editor draws its own preview in the browser, which can\ndiffer from the export (text shaping, for example), so it is not evidence of how a render\nlooks.`]})]}),`\n`,_jsxs(Tip,{children:[_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Media an agent can reach.`}),` `,_jsx(_components.code,{children:`list_assets`}),` serves exactly the rows\n`,_jsx(_components.code,{children:`GET /v1/workspace/assets`}),` serves (one shared query, so kind and readiness cannot\ndrift), and its cursors are interchangeable with the REST ones.`]}),_jsxs(_components.p,{children:[`Two timing models, and mixing them up is the usual `,_jsx(_components.code,{children:`422`}),`. For `,_jsx(_components.strong,{children:`video and audio`}),`,\n`,_jsx(_components.code,{children:`duration`}),` is the source file’s full length: omit an element’s `,_jsx(_components.code,{children:`out_point`}),` and the\nserver fills it from that, or set `,_jsx(_components.code,{children:`in_point`}),` / `,_jsx(_components.code,{children:`out_point`}),` to trim. An `,_jsx(_components.strong,{children:`image`}),` has no\nduration (always `,_jsx(_components.code,{children:`null`}),`, on every path) and no `,_jsx(_components.code,{children:`out_point`}),`; its element carries a\n`,_jsx(_components.strong,{children:`required`}),` `,_jsx(_components.code,{children:`duration`}),` of its own, in seconds on screen, which you choose.`]}),_jsxs(_components.p,{children:[_jsx(_components.code,{children:`ready`}),` is derived, not stored: video and audio become ready when the duration probe\nlands, an image as soon as its bytes are in the bucket. `,_jsx(_components.code,{children:`list_assets`}),` shows only ready\nassets, so `,_jsx(_components.code,{children:`get_asset`}),` is what you poll after an upload.`]}),_jsxs(_components.p,{children:[`To add a `,_jsx(_components.strong,{children:`local`}),` file, call `,_jsx(_components.code,{children:`create_upload`}),` (or `,_jsx(_components.code,{children:`POST /v1/uploads`}),` over REST). There\nare two routes and the size decides which. `,_jsx(_components.strong,{children:`Under 256 KB`}),` — logos, crests, icons,\nSVG overlays — pass the file’s bytes as base64 and that single call stores them; it\nmakes no outbound request on your behalf and asks none of you, so it is the route that\nworks from a sandbox whose egress is allow-listed per host. `,_jsx(_components.strong,{children:`Larger`}),` files come back\nwith a signed upload URL you PUT to, in one request, with the same `,_jsx(_components.code,{children:`Content-Type`}),`;\nthose bytes go straight to storage, so the server cannot confirm them and `,_jsx(_components.code,{children:`get_asset`}),`\nhas to check — it answers `,_jsx(_components.code,{children:`bytes_uploaded`}),`. If your own network refuses that host,\nthe result also carries a first-party upload URL on `,_jsx(_components.code,{children:`uploads.framelane.io`}),` — the same\nsession, one Framelane domain to allow instead of Google’s. If that is refused too,\nthe file is either small enough to inline or needs to be reachable at a public URL.`]}),_jsx(_components.p,{children:`Passing the duration you probed locally makes a video or audio asset usable\nimmediately instead of waiting on the server probe. A file already at a public https\nURL needs none of this — pass the URL as an element’s source URL and it is fetched at\nthe workspace on first render.`})]}),`\n`,_jsx(Tip,{children:_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Waiting is one tool call, not a loop.`}),` `,_jsx(_components.code,{children:`wait_for_render`}),` / `,_jsx(_components.code,{children:`wait_for_task`}),` block until the\njob is terminal, streaming progress as it advances. If a call returns `,_jsx(_components.code,{children:`still_running: true`}),`\n(the server hold elapsed), call again. Ensure your MCP client’s per-tool timeout is ≥ 30s.`]})}),`\n`,_jsxs(Tip,{children:[_jsxs(_components.p,{children:[`Tool errors carry a stable machine code. A domain failure is not a transport error: it\ncomes back in the tool `,_jsx(_components.strong,{children:`result`}),` as `,_jsx(_components.code,{children:`{\"error\": {\"code\", \"message\", \"details\"}}`}),` —\nbranch on `,_jsx(_components.code,{children:`code`}),`. The set is `,_jsx(_components.code,{children:`invalid_request`}),`, `,_jsx(_components.code,{children:`not_found`}),`, `,_jsx(_components.code,{children:`conflict`}),`,\n`,_jsx(_components.code,{children:`quota_exceeded`}),`, `,_jsx(_components.code,{children:`rate_limited`}),`, `,_jsx(_components.code,{children:`email_not_verified`}),`, `,_jsx(_components.code,{children:`unauthorized`}),`,\n`,_jsx(_components.code,{children:`invalid_source`}),` (`,_jsx(_components.code,{children:`extract_brand_kit`}),` could not read the URL),\n`,_jsx(_components.code,{children:`unsupported_content_type`}),` (`,_jsx(_components.code,{children:`create_upload`}),` was given a type we do not store; the\n`,_jsx(_components.code,{children:`details.allowed`}),` list says what to re-encode to), `,_jsx(_components.code,{children:`inline_too_large`}),` (the base64 you\nsent `,_jsx(_components.code,{children:`create_upload`}),` is past the 256 KB inline cap — call it again without the bytes\nand PUT to the `,_jsx(_components.code,{children:`upload_url`}),` it returns), `,_jsx(_components.code,{children:`plan_required`}),` (`,_jsx(_components.code,{children:`create_project`}),` or\n`,_jsx(_components.code,{children:`edit_project`}),` made or used one of your own templates while your plan does not include\ncustom templates; `,_jsx(_components.code,{children:`details.required_plan`}),` names the plan, and public templates are\nnever gated) and `,_jsx(_components.code,{children:`internal`}),`. Only an unexpected exception surfaces as a real tool error.`]}),_jsxs(_components.p,{children:[_jsx(_components.code,{children:`rate_limited`}),` is per `,_jsx(_components.strong,{children:`workspace`}),`, not per IP, and only the submission tools are\nmetered — `,_jsx(_components.code,{children:`create_render`}),`, `,_jsx(_components.code,{children:`render_project`}),` and `,_jsx(_components.code,{children:`run_task`}),`. Previews\n(`,_jsx(_components.code,{children:`create_preview`}),` / `,_jsx(_components.code,{children:`preview_project`}),`), reads and `,_jsx(_components.code,{children:`get_capabilities`}),` are unmetered.\nIts `,_jsx(_components.code,{children:`details`}),` carry `,_jsx(_components.code,{children:`limit_per_minute`}),` and `,_jsx(_components.code,{children:`retry_after_seconds`}),`; wait that long and\nretry rather than backing off blindly.`]})]}),`\n`,_jsx(Heading,{level:`2`,id:`connecting`,children:`Connecting`}),`\n`,_jsxs(_components.p,{children:[`Point your MCP host at `,_jsx(_components.code,{children:`https://mcp.framelane.io/mcp`}),` and send your key as a Bearer token.`]}),`\n`,_jsx(Heading,{level:`3`,id:`cursor-/-vs-code`,children:`Cursor / VS Code`}),`\n`,_jsxs(_components.p,{children:[`Add to your MCP settings (`,_jsx(_components.code,{children:`~/.cursor/mcp.json`}),`, or the workspace `,_jsx(_components.code,{children:`.vscode/mcp.json`}),`):`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`10`,language:`json`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`json`,children:_jsxs(_components.code,{language:`json`,numberOfLines:`10`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"mcpServers\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"framelane\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"url\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"https://mcp.framelane.io/mcp\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"headers\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"Authorization\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:'\"Bearer ${FRAMELANE_API_KEY}\"'})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsxs(_components.p,{children:[`Then ask: `,_jsx(_components.em,{children:`“Use the Framelane MCP to render this composition…”`})]}),`\n`,_jsx(Heading,{level:`3`,id:`claude-desktop`,children:`Claude Desktop`}),`\n`,_jsxs(_components.p,{children:[`Add to `,_jsx(_components.code,{children:`claude_desktop_config.json`}),` (`,_jsx(_components.code,{children:`mcp-remote`}),` bridges the hosted server over stdio):`]}),`\n`,_jsx(CodeBlock,{filename:``,numberOfLines:`11`,language:`json`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`json`,children:_jsxs(_components.code,{language:`json`,numberOfLines:`11`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`{`})}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"mcpServers\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"framelane\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"command\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"npx\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`,`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"args\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: [`}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"-y\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"mcp-remote\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`, `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"https://mcp.framelane.io/mcp\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`],`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"env\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: {`})]}),`\n`,_jsxs(_components.span,{className:`line`,children:[_jsx(_components.span,{style:{color:`#116329`,\"--shiki-dark\":`#9CDCFE`},children:` \"FRAMELANE_API_KEY\"`}),_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`: `}),_jsx(_components.span,{style:{color:`#0A3069`,\"--shiki-dark\":`#CE9178`},children:`\"fl_live_...\"`})]}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:` }`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{style:{color:`#1F2328`,\"--shiki-dark\":`#D4D4D4`},children:`}`})}),`\n`]})})}),`\n`,_jsx(Heading,{level:`2`,id:`agent-skills`,children:`Agent Skills`}),`\n`,_jsxs(_components.p,{children:[`The Framelane `,_jsx(_components.a,{href:`/agent-skills/overview`,children:`Agent Skills`}),` are served by this server: each\nworkflow skill is an MCP prompt, and every skill is also a `,_jsx(_components.code,{children:`get_capabilities`}),` section named\n`,_jsx(_components.code,{children:`skills.\u003cname>`}),` (`,_jsx(_components.code,{children:`section='skills'`}),` lists them) for hosts that do not show prompts. Load\none, then drive the calls through the tools above. How-tos for the REST API are in the\n`,_jsx(_components.a,{href:`/guides/author-video`,children:`Guides`}),`.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"MCP Server","description":"Connect Framelane to Claude, Cursor, or any MCP-capable agent framework.","href":"/mcp/overview"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"Overview","slug":"overview","depth":2,"children":[]},{"title":"Getting a credential first","slug":"getting-a-credential-first","depth":2,"children":[]},{"title":"Key tools","slug":"key-tools","depth":2,"children":[]},{"title":"Connecting","slug":"connecting","depth":2,"children":[{"title":"Cursor / VS Code","slug":"cursor-/-vs-code","depth":3,"children":[]},{"title":"Claude Desktop","slug":"claude-desktop","depth":3,"children":[]}]},{"title":"Agent Skills","slug":"agent-skills","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"itis-c7dc7146","siteName":"Framelane","title":"MCP Server - Framelane","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-10-24T16:44:13.709Z","originalFileLocation":"mcp/overview.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Connect Framelane to Claude, Cursor, or any MCP-capable agent framework.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Framelane\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003cmeta data-mint-head name=\"robots\" content=\"index, follow\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"MCP Server - Framelane\">\u003cmeta data-mint-head property=\"og:description\" content=\"Connect Framelane to Claude, Cursor, or any MCP-capable agent framework.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Framelane\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.framelane.io/mcp/overview\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://cdn-assets.framelane.io/shared/images/og-docs.png\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"MCP Server - Framelane\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Connect Framelane to Claude, Cursor, or any MCP-capable agent framework.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://cdn-assets.framelane.io/shared/images/og-docs.png\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/itis-c7dc7146/3AM3-gs_iChWRW0W/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Framelane\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#0070f3\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.framelane.io/mcp/overview\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/mcp/overview.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.framelane.io/#organization\",\"name\":\"Framelane\",\"url\":\"https://docs.framelane.io\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/itis-c7dc7146/I-4ZfymyiJyBmcDs/logo/framelane-light.png?fit=max\u0026auto=format\u0026n=I-4ZfymyiJyBmcDs\u0026q=85\u0026s=a819adfb6e5b9a97575f20c034d41c77\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.framelane.io#website\",\"name\":\"Framelane\",\"url\":\"https://docs.framelane.io\",\"publisher\":{\"@id\":\"https://docs.framelane.io/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.framelane.io/mcp/overview#webpage\",\"url\":\"https://docs.framelane.io/mcp/overview\",\"name\":\"MCP Server\",\"description\":\"Connect Framelane to Claude, Cursor, or any MCP-capable agent framework.\",\"dateModified\":\"2026-10-24T16:44:13.709Z\",\"isPartOf\":{\"@id\":\"https://docs.framelane.io#website\"},\"breadcrumb\":{\"@id\":\"https://docs.framelane.io/mcp/overview#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.framelane.io/mcp/overview#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Framelane\",\"item\":\"https://docs.framelane.io\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"MCP Server\",\"item\":\"https://docs.framelane.io/mcp/overview\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.framelane.io/mcp/overview#article\",\"headline\":\"MCP Server\",\"name\":\"MCP Server\",\"description\":\"Connect Framelane to Claude, Cursor, or any MCP-capable agent framework.\",\"url\":\"https://docs.framelane.io/mcp/overview\",\"mainEntityOfPage\":{\"@id\":\"https://docs.framelane.io/mcp/overview#webpage\"},\"image\":\"https://cdn-assets.framelane.io/shared/images/og-docs.png\",\"dateModified\":\"2026-10-24T16:44:13.709Z\",\"publisher\":{\"@id\":\"https://docs.framelane.io/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.framelane.io#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Framelane\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Framelane","nav":{"global":{"anchors":[{"anchor":"GitHub","icon":"github","href":"https://github.com/framelane"},{"anchor":"Status","icon":"signal-bars","href":"https://status.framelane.io"}]},"tabs":[{"tab":"Documentation","pages":[{"title":"Documentation","href":"/introduction/overview"}]},{"tab":"Integrations","groups":[{"group":"MCP Server","pages":[{"title":"MCP Server","href":"/mcp/overview"}]},{"group":"Agent Skills","pages":[{"title":"Agent Skills","description":"Written workflows that teach an AI agent to make video with Framelane, served by the MCP server and installable as an agent-skills bundle.","href":"/agent-skills/overview"}]}]},{"tab":"API Reference","pages":[{"title":"API Reference","href":"/api-reference/introduction"}]}]},"title":"MCP Server - Framelane","buildId":"dpl_DFJrtvUi8HS5F9nkooKuDJsuD5BM","deploymentId":"dpl_DFJrtvUi8HS5F9nkooKuDJsuD5BM","dynamics":[],"chunks":[]}