Skip to main content
Glama
tvolodi

penpot-headless

by tvolodi

penpot-headless

A portable MCP server for headless Penpot project/file/content management, using Penpot's RPC API directly. No browser, no Penpot plugin session.

This package has zero project-specific knowledge — no hardcoded colors, fonts, or Penpot instance URL. Every consuming project supplies its own config and token file, so it can be installed from npm or copied as-is into another project.

What this does

  • Team/project/file/page CRUD (penpot_list_teams, penpot_create_file, etc.)

  • Shape creation — rect, frame, text, circle (ellipse), path, bool (boolean operations), and image — via penpot_add_shapes, with colors/fonts resolved either from a literal value or a { "token": "name" } reference against your project's token file. The same token-or-literal pattern applies to spacing (layout gaps/padding, layoutItem margins/min/max sizes), corner radii (r1-r4), and shadows (shadows, either an inline { style, color, offsetX, offsetY, blur, spread, opacity } object or { "token": "name" }). Shapes may be rotated via an optional rotation field (degrees, clockwise, about the shape's center).

  • Shape editing: penpot_update_shapes changes an existing shape in place by id — position, size, rotation, name, fill, stroke, shadows, corner radii, or (for text) content/font. Only the fields you pass are touched; everything else (children, component/variant tags, layout) is left as-is. clearStroke/clearShadows remove strokes/shadows entirely. Geometry changes automatically recompute the shape's selection box and transform, so a partial edit (e.g. just width) can't desync from the rest of the shape's geometry. Layout/layoutItem aren't editable this way — only settable at creation time via penpot_add_shapes.

  • Shape deletion: penpot_delete_shapes removes one or more existing shapes from a page by id. Deleting a frame or group also removes its children, matching Penpot's own delete behavior.

  • Shape grouping: penpot_group_shapes wraps one or more sibling shapes into a new group (Ctrl+G equivalent) — all shapes must share the same parent. The group is inserted at the topmost selected shape's z-order position and its bounding box is computed from the children's rotation-aware selrects. penpot_ungroup_shapes dissolves a group (Ctrl+Shift+G equivalent): children are reparented to the group's former parent at the group's z-order slot, then the group is deleted.

  • Shape duplication: penpot_clone_shapes duplicates one or more existing shapes (and, for frames/groups, their full descendant subtree), each with fresh ids — plain shape duplication, like Penpot's own Ctrl+D, not a component instance. Optional dx/dy offset each clone from its source (defaults to no offset, stacked directly on top of the original); optional parentId/frameId reparent every cloned root instead of leaving it alongside its source.

  • Shape stacking order: penpot_reorder_shapes changes a shape's z-order among its siblings, matching Penpot's own "Bring to front" / "Send to back" / "Forward" / "Backward" UI actions. action is front/back (top/bottom of the stack), forward/backward (swap with the next/ previous sibling — a no-op if already at that end), or before/after (place immediately relative to another sibling given as targetId). Only reorders among existing siblings — it doesn't reparent.

  • Align & distribute: penpot_align_shapes lines up two or more shapes on a common edge or center (left/right/top/bottom/center-h/center-v), and penpot_distribute_shapes spreads three or more shapes so the gaps between them are equal (horizontal/vertical) — matching Penpot's own one-click align/distribute actions, instead of the agent computing pixel positions itself. Both work on each shape's visible bounding box (its selrect), so rotated shapes line up by their rendered bounds; align never moves the group as a whole, and distribute leaves the two outermost shapes fixed. Aligning or distributing a frame/group carries its whole child subtree along by the same offset.

  • Batched edits: penpot_batch applies an ordered list of create/update/ delete/reorder operations as a single update-file change-set — one revn/vern round trip no matter how many shapes are touched, instead of one RPC call per shape (and the races on revn that come with that). Each op takes the same fields as the matching standalone tool (penpot_add_shapes for create, penpot_update_shapes for update, etc.); a create op may set an explicit id and be referenced as a later op's parentId/frameId or targeted by a later update/delete/reorder in the same call, so a whole form or card grid can be built in one round trip.

  • Undo point: penpot_checkpoint snapshots shapes and returns a checkpointId; penpot_restore_checkpoint undoes everything since — including a wrong penpot_delete_shapes call, which otherwise has no undo path short of Penpot's own UI. Supply pageId to scope the snapshot to a single page; omit pageId for a whole-file checkpoint that covers every page. Works by diffing current state against the snapshot and replaying corrective changes as a single update-file call (recreate what's missing, delete what's new, overwrite what changed back to its snapshotted fields), since Penpot's RPC has no "revert to revn X" primitive. Checkpoints live in the MCP server's memory (not the Penpot file, not disk) and are reusable — restore to the same point as many times as you like — until explicitly freed via penpot_discard_checkpoint or the server process restarts.

  • Shape lookup: penpot_get_shape fetches a single shape by id, without pulling the whole page via penpot_get_file_snapshot. By default nests the shape's full descendant subtree under shapes; includeDescendants and maxDepth control how much of the subtree comes back. Always includes a componentInfo field: linkState ("linked" / "detached" / "not-an-instance" / "main-component-root"), and — for same-file linked instances — a driftedFields array listing the camelCase field names that differ from the main component's current definition (empty = fully in sync).

  • Shape search: penpot_find_shapes searches every shape on a page against one or more predicates — type, name (exact match), nameContains (case-insensitive substring), textContains (case-insensitive substring against a text shape's rendered characters), isComponentInstance, and/or isRoot — all given filters must match (AND). Omit every filter to list the whole page. Returns each match's id/type/name/position/size plus linkState and (for linked same-file instances) driftedFields; use penpot_get_shape on a match's id for the full subtree and detailed componentInfo, optionally capped via limit.

  • Text measurement: penpot_measure_text computes the real rendered width/height of a string for a given font (family/size/weight), without creating or touching any shape — removing the guesswork around width/ height when calling penpot_add_shapes/penpot_update_shapes for text. Tries Google Fonts first (by family name, no API key needed); if the family isn't there, searches all Penpot teams accessible to the configured token for a matching custom/team font, so fonts uploaded directly to your Penpot instance are also measurable. Measures real glyph advance widths (plus kerning) so the numbers match what Penpot renders. Splits on explicit \n; pass maxWidth to also get word-wrapped line breaks for a fixed-width box.

  • Auto-layout: a frame may declare flex or grid layout via an optional layout field (direction, gap, padding, alignment; grid also takes row/column track definitions). Any shape may set layoutItem to control its own placement within an auto-layout parent (sizing, alignment, margins, min/max size, and — for grid parents — row/column/span).

  • Components: penpot_create_component registers a shape tree (same specs as penpot_add_shapes) as a component's main instance; give shapes explicit ids to nest them, and the one shape not parented to a sibling becomes the root. penpot_add_component_instance then places a full copy of that tree elsewhere on a page, linked back to the main instance via Penpot's shape-ref so it's recognized as a proper component copy — reusable any number of times.

  • Variants: penpot_create_variant_group groups two or more components (built the same way as penpot_create_component, one per variant) into a single container tagged with shared property axes (e.g. a "Button" with Type=Primary/Secondary). This wires up Penpot's actual variant-swap system — confirmed live via the Penpot plugin API, including instance.switchVariant(pos, value) correctly swapping a placed instance between variants. penpot_add_variant adds a new variant to an already-existing variant group container: accepts the same shape specs as one entry in penpot_create_variant_group's variants array and the containerId returned by that tool (or found via penpot_find_shapes), then appends the new variant's main instance to the container's shapes array and registers a new component — all in a single update-file call.

  • penpot_list_components enumerates a file's existing components (from its data.components map), instead of requiring the caller to have created them itself in the same session or parse penpot_get_file_snapshot's output by hand. Each entry includes the componentId (usable with penpot_add_component_instance), name, mainInstanceId/ mainInstancePage, and — for variant components — variantId/ variantProperties.

  • Media upload: penpot_upload_media uploads an image or other asset to a Penpot file and returns the media object metadata (id, width, height, mtype). Accepts a local file path (filePath), a remote URL (url — Penpot's server fetches it directly), or base64-encoded bytes (dataBase64 + mtype). The returned id is used as mediaId when creating an image shape.

  • Comments: penpot_list_comment_threads lists all threads for a file; penpot_get_comments fetches replies inside a thread; penpot_create_comment_thread pins a new thread to a canvas position (x/y, optional frameId); penpot_create_comment adds a reply; penpot_update_comment edits a comment's text; penpot_resolve_comment_thread marks a thread resolved or reopens it; penpot_delete_comment removes a single reply; penpot_delete_comment_thread removes a whole thread.

  • Version history: penpot_list_file_snapshots lists all named snapshots for a file; penpot_create_file_snapshot saves the current state as a named version (equivalent to "Save version" in the Penpot UI, with an optional label); penpot_restore_file_snapshot rolls the live file back to a snapshot (Penpot automatically saves a system backup before restoring, so restores are themselves undoable); penpot_rename_file_snapshot / penpot_delete_file_snapshot manage user-created versions; penpot_lock_file_snapshot / penpot_unlock_file_snapshot protect a version from accidental deletion; penpot_get_file_snapshot_data returns the full file content at a snapshot for read-only inspection.

  • Reading a file's current shape tree via penpot_get_file_snapshot.

  • Rendering shapes or pages to PNG/SVG/PDF via penpot_export_shape (single shape) and penpot_export_batch (multiple shapes in one call, returned in the same order, across any number of pages — use the shapes array for multi-page exports; the shapeIds shorthand is still available when all shapes share the same page) — requires PENPOT_LOGIN_EMAIL/PENPOT_LOGIN_PASSWORD, PENPOT_OIDC_USERNAME/PENPOT_OIDC_PASSWORD, or PENPOT_AUTH_TOKEN_COOKIE, see below — no browser tab or Penpot plugin session needed on your end.

Related MCP server: Soma Nexus MCP

Render/export capability

penpot_export_shape renders a single shape (or an entire page, by passing its root frame's id) to PNG, SVG, or PDF, using Penpot's own server-side exporter — the same headless-Chromium-backed service Penpot's self-hosted stack already runs (the penpotapp/exporter container). penpot_export_batch accepts either a shapeIds array (all shapes on the same pageId) or a shapes array where each entry can specify its own pageId, format, scale, and name — enabling whole-file exports across multiple pages in a single API call. All formats (PNG/SVG/PDF) are supported. No browser automation happens in this package; it's two HTTP calls (auth, then export) against your Penpot instance.

This needs a second, separate auth mode from the rest of this package: Penpot's export pipeline (POST /api/export) is a distinct microservice from the /api/rpc/command/* surface everything else here uses, and it authenticates purely via a session cookie — a personal access token in an Authorization header is never read on this path (confirmed by reading Penpot's exporter source: its wrap-auth looks for a literal auth-token cookie and nothing else), independently of PENPOT_ACCESS_TOKEN.

Three ways to supply this cookie — configure exactly one:

Option A — password login (PENPOT_LOGIN_EMAIL + PENPOT_LOGIN_PASSWORD): The server logs in with email/password to obtain the auth-token cookie and caches it for the process lifetime, re-logging-in automatically on expiry. Best for instances that expose password login. PENPOT_ACCESS_TOKEN and these credentials must belong to the same Penpot account (or at least accounts with access to the same team); mismatched accounts cause a silent 10-second render timeout because the exporter can't distinguish "access denied" from "shape not visible."

Option B — headless OIDC/SSO login (PENPOT_OIDC_USERNAME + PENPOT_OIDC_PASSWORD): The server drives the OIDC redirect flow over plain HTTP: it follows Penpot's OAuth endpoint redirect chain to the identity provider's login page, parses the HTML login form, submits the credentials, and captures the resulting auth-token cookie — no browser or external tooling needed. Re-logs in automatically on expiry. Works for form-based IdPs that render a standard HTML login form (Keycloak, Authentik, Dex, and similar). Does not work for JavaScript-driven login pages (Google, Microsoft, Okta hosted login) because their login UIs are SPA shells with no server-rendered form.

Set PENPOT_OIDC_PROVIDER to the OAuth provider name configured on your Penpot instance (default: "oidc"). Most self-hosted setups use this default; check your Penpot server flags if login doesn't start.

Option C — pre-obtained SSO/OIDC cookie (PENPOT_AUTH_TOKEN_COOKIE): Paste the raw value of the auth-token cookie from a browser session in which you already completed the OIDC/SSO login. The server uses it as-is and emits a clear error if it expires (since it has no credentials to re-login with). Works for instances where password login is disabled and the IdP uses a JavaScript-driven login page (where Option B can't reach). To obtain the cookie value:

  1. Open your Penpot instance in a browser and complete the SSO/OIDC login.

  2. Open DevTools → Application → Cookies → your Penpot domain.

  3. Copy the value of the auth-token cookie (not the whole Cookie: header — just the token value after auth-token=).

  4. Set PENPOT_AUTH_TOKEN_COOKIE to that value.

If none of the options is configured, penpot_export_shape is not registered and every other tool works exactly as before.

Setup

  1. Generate a Penpot access token: your Penpot instance → Account settings → Access tokens. (Self-hosted instances: this section is hidden unless the backend/frontend are started with PENPOT_FLAGS including enable-access-tokens.)

  2. Create a token file for your project (see design-tokens/*.tokens.json in the consuming repo for an example — or write your own matching the schema below).

  3. If you want penpot_export_shape, also configure one of:

    • Password login: set PENPOT_LOGIN_EMAIL/PENPOT_LOGIN_PASSWORD to the same account the access token above belongs to.

    • Headless OIDC login: set PENPOT_OIDC_USERNAME/PENPOT_OIDC_PASSWORD to your IdP credentials. Works for form-based IdPs (Keycloak, Authentik, Dex). Set PENPOT_OIDC_PROVIDER if your provider name differs from the default "oidc".

    • SSO/OIDC cookie: set PENPOT_AUTH_TOKEN_COOKIE to the raw auth-token cookie value from a browser session in which you completed the SSO login (see "Render/export capability" above). Use this for JavaScript-driven IdP login pages where headless OIDC can't reach.

    Skip this step entirely if you don't need rendering.

  4. (Optional) To persist checkpoints across server restarts, set PENPOT_CHECKPOINTS_PATH to a local directory path. When set, each penpot_checkpoint call writes a JSON file to that directory; on startup the server reloads any files already there, so a checkpoint survives the MCP server being restarted mid-way through a long edit. The directory is created automatically. Without this setting, checkpoints live only in process memory (the previous behaviour).

  5. Register this server in your MCP client config, e.g. (password-login variant):

    {
      "mcpServers": {
        "penpot-headless": {
          "command": "npx",
          "args": ["-y", "@ai-dala/penpot-headless"],
          "env": {
            "PENPOT_BASE_URL": "https://your-penpot-instance.example.com",
            "PENPOT_ACCESS_TOKEN": "your-token-here",
            "PENPOT_TOKENS_PATH": "/path/to/your-project/design-tokens/tokens.json",
            "PENPOT_LOGIN_EMAIL": "same-account-as-the-token-above@example.com",
            "PENPOT_LOGIN_PASSWORD": "that-account's-password"
          }
        }
      }
    }

    Or, for an SSO/OIDC-only instance with a form-based IdP (headless OIDC login):

    {
      "mcpServers": {
        "penpot-headless": {
          "command": "npx",
          "args": ["-y", "@ai-dala/penpot-headless"],
          "env": {
            "PENPOT_BASE_URL": "https://your-penpot-instance.example.com",
            "PENPOT_ACCESS_TOKEN": "your-token-here",
            "PENPOT_TOKENS_PATH": "/path/to/your-project/design-tokens/tokens.json",
            "PENPOT_OIDC_USERNAME": "your-idp-username-or-email",
            "PENPOT_OIDC_PASSWORD": "your-idp-password"
          }
        }
      }
    }

    Or, for an SSO/OIDC-only instance with a JavaScript-driven IdP login page:

    {
      "mcpServers": {
        "penpot-headless": {
          "command": "npx",
          "args": ["-y", "@ai-dala/penpot-headless"],
          "env": {
            "PENPOT_BASE_URL": "https://your-penpot-instance.example.com",
            "PENPOT_ACCESS_TOKEN": "your-token-here",
            "PENPOT_TOKENS_PATH": "/path/to/your-project/design-tokens/tokens.json",
            "PENPOT_AUTH_TOKEN_COOKIE": "the-auth-token-cookie-value-from-your-browser"
          }
        }
      }
    }

    Or, if working from a local clone instead of the published package:

    {
      "mcpServers": {
        "penpot-headless": {
          "command": "npx",
          "args": ["tsx", "/path/to/penpot-mcp/src/server.ts"],
          "env": {
            "PENPOT_BASE_URL": "https://your-penpot-instance.example.com",
            "PENPOT_ACCESS_TOKEN": "your-token-here",
            "PENPOT_TOKENS_PATH": "/path/to/your-project/design-tokens/tokens.json",
            "PENPOT_LOGIN_EMAIL": "same-account-as-the-token-above@example.com",
            "PENPOT_LOGIN_PASSWORD": "that-account's-password"
          }
        }
      }
    }

Token file schema

{
  "colors": {
    "accent": "#7AA2FF",
    "bg-surface": "#111726"
  },
  "fonts": {
    "sans": { "family": "Inter", "weights": ["400", "500", "600"] }
  },
  "spacing": {
    "sm": 8,
    "md": 16,
    "lg": 24
  },
  "radii": {
    "sm": 4,
    "md": 8,
    "pill": 999
  },
  "shadows": {
    "card": {
      "style": "drop-shadow",
      "color": "#000000",
      "opacity": 0.3,
      "offsetX": 4,
      "offsetY": 4,
      "blur": 8,
      "spread": 2
    }
  }
}

All four token tables are optional except colors. Any color-typed field in penpot_add_shapes/penpot_update_shapes accepts either a literal hex string or { "token": "accent" }, resolved against this file at call time. The same applies to:

  • Numeric fields (layout gaps/padding, layoutItem margins/min/max sizes, and corner radii r1-r4) — either a literal number or { "token": "sm" }, resolved against spacing (gaps/padding/margins) or radii (corner radii).

  • shadows on a shape — an array where each entry is either an inline { style, color, opacity, offsetX, offsetY, blur, spread } object (color itself may also be a { "token": "name" } reference) or { "token": "card" }, resolved against shadows. style is "drop-shadow" (default) or "inner-shadow".

Testing

Two suites, split because they need very different things:

  • Unit (npm test) — pure functions in shape-builders.ts (rotation matrix math, layout attribute mapping, the camelCase-in/kebab-case-out shape extraction penpot_update_shapes relies on) and font-metrics.ts (line-splitting/word-wrap logic, measured against a hand-built fake font so no network call is needed). No network, no Penpot account, runs anywhere. This is the suite CI runs on every push/PR.

  • Integration (npm run test:integration) — exercises the actual MCP tool handlers against a real Penpot instance via PENPOT_BASE_URL/ PENPOT_ACCESS_TOKEN (reads .env, same as the server itself). Each test creates its own scratch project and deletes it in a finally block, even on failure — see test/integration/helpers/scratch-project.ts. Skips itself (doesn't fail) if credentials aren't configured. Local/ manual only — deliberately not run in CI, since it mutates a real Penpot account and CI credentials for that are out of scope for this project. Run it yourself before trusting a change to Penpot's wire schema (rotation, layout, components, variants). font-metrics.test.ts also lives in this suite despite not needing Penpot credentials — it makes a real call to the public Google Fonts CDN, which the unit suite's "no network" rule excludes; it skips itself if that network call fails rather than needing PENPOT_ACCESS_TOKEN.

This split matters because Penpot's RPC schema accepting a change is not the same as Penpot's editor correctly rendering or recognizing it — the variant-id bug documented in shape-builders.ts (variantContainerAttrs) was only caught by live testing, not by the schema validating successfully. npm run test:all runs both.

Copying to another project

This package is published to npm as @ai-dala/penpot-headless, so most consuming projects can just reference it via npx (see above) without a local copy.

If you'd rather vendor it, this directory has its own package.json/tsconfig.json and depends only on @modelcontextprotocol/sdk, zod, and opentype.js (used by penpot_measure_text to parse real font files) — no framework dependencies. Copy the whole penpot-headless/ directory into the new project, run npm install, write a token file for that project, and register the server with a PENPOT_TOKENS_PATH pointing at it.

Known limitations

  • penpot_measure_text doesn't apply ligature substitution (e.g. rendering "fi" as one glyph) — it sums individual glyph advances (with real GPOS kerning) instead of using opentype.js's built-in shaping, because that shaping path throws on GSUB tables present in most current-generation Google Fonts, including Inter and Roboto (see measureLineWidth in font-metrics.ts). This is a deliberate tradeoff, not an open gap: the width difference from skipping ligatures is a few percent at most for typical UI text, next to a hard crash on most real-world fonts if the built-in shaping were used instead.

  • penpot_measure_text matches Penpot custom/team fonts by family name (case-insensitive) and weight. If a font variant exists in multiple styles (normal/italic) for the same weight, the "normal" style is preferred; if your use-case requires a specific non-normal style for measurement, this is not yet controllable via the tool's input parameters.

Possible future additions

Ideas for further reducing agent friction, roughly in priority order:

  • penpot_update_shapes — edit an existing shape's geometry/fill/stroke/ text in place. (Done — see above.)

  • penpot_delete_shapes — wraps del-obj. (Done — see above.)

  • penpot_get_shape — look up one shape (or a small subtree) by id, instead of pulling the whole page via penpot_get_file_snapshot and parsing a potentially large JSON blob client-side to find it. (Done — see above.)

  • penpot_measure_text — text width/height in penpot_add_shapes are just numbers the caller has to guess; Penpot only knows real rendered bounds after layout. (Done — see above. Measures against a real fetched Google Font via opentype.js rather than an approximation; see the "Text measurement" note below and font-metrics.ts for why it sums per-glyph advances instead of using opentype.js's built-in getAdvanceWidth.)

  • penpot_clone_shapes — plain-shape duplication (not the component-instance path already covered by penpot_add_component_instance). (Done — see above.)

  • penpot_reorder_shapes — bring-to-front/send-to-back/forward/backward/ before/after, by id. (Done — see above. Note: does not wrap reorder-children as originally planned — that RPC change type was never actually verified live. Instead it round-trips the parent shape through add-obj with a recomputed shapes array, the same mechanism penpot_update_shapes already uses; reorder-children may still work but wasn't worth the extra risk once the add-obj approach was confirmed live. One live-only pitfall found along the way: fields like transformInverse/hideFillOnExport on the object returned by get-file are real required fields, not stale camelCase duplicates — they must be renamed to their kebab-case form when round-tripped, not dropped, or update-file rejects the change with a malli validation error on transform-inverse.)

  • penpot_list_components — enumerate a file's existing components, instead of requiring the caller to have created them itself in the same session or parse the snapshot's components map by hand. (Done — see above.)

  • penpot_find_shapes — predicate/name-based search over a page (mirrors the plugin API's findShapes), instead of the caller walking the tree itself for "every text shape" or "the shape named X." (Done — see above.)

  • penpot_batch — apply an ordered list of create/update/delete/reorder ops as a single update-file change-set. (Done — see above. Ops are applied against an in-memory shadow of the page's shapes, mutated as each op is processed, so a later op sees the effect of every earlier op in the same call — mirroring how update-file applies its changes array sequentially server-side. This is also what lets a create op's caller-chosen id be referenced by a later op's parentId/frameId, or be the target of a later update/delete/ reorder, without an extra RPC round trip in between.)

  • penpot_checkpoint / penpot_restore_checkpoint / penpot_discard_checkpoint — snapshot shapes before a risky multi-step edit, and restore them after. (Done — see above. Penpot's RPC has no "revert to revn X" primitive, so restore works by diffing current state against the snapshot and replaying corrective add-obj/del-obj changes as a single update-file call, reusing the same camelCase-in/kebab-case-out normalization already verified live for penpot_update_shapes/ penpot_reorder_shapes — see restoreShapeAsAddObj in shape-builders.ts. Snapshots are held in the MCP server's own memory, not written to the Penpot file or disk, so a checkpoint doesn't survive a server restart. penpot_checkpoint now accepts an optional pageId: supply it to scope the snapshot to a single page; omit it for a whole-file checkpoint that covers every page. Restore applies all pages' corrective changes in one update-file call, so a multi-step edit that touched multiple pages is fully undone in a single round trip.)

  • penpot_align_shapes / penpot_distribute_shapes — the one-click align-left/center/distribute-evenly actions Penpot's own UI has, that this package doesn't. Without them, an agent has to compute pixel positions itself from penpot_get_shape results — exactly the kind of arithmetic LLMs get wrong. (Done — see above. Both operate on each shape's selrect (its rotation-aware visible bounding box), so rotated shapes line up by what's rendered; align never moves the group as a whole (its reference line comes from the shapes' own extent) and distribute leaves the two endpoints fixed. Aligning/distributing a frame or group moves its whole descendant subtree by the same delta — a container's children have absolute positions, so shifting only the container's own x/y would leave them behind. Reuses the same camelCase-in/kebab-case-out add-obj update path already verified live for penpot_update_shapes, applied as a single update-file change-set.)

  • Token file support for spacing/radii/shadows, not just colors and fonts. (Done — see above/Token file schema. Added a shadows wire field to rect/frame/text that didn't exist at all before this — verified live against a real instance's add-obj/get-file: the field is shadows (array, back-to-front like fills/strokes), style is "drop-shadow" | "inner-shadow" (not shadowType as the Penpot plugin API's Shadow type would suggest), offsets are kebab-case offset-x/offset-y on write and camelCase offsetX/ offsetY on read back, and color is a nested { color, opacity } object rather than flattened shadow-color/shadow-opacity keys — see the comment on Shadow in shape-builders.ts. Every numeric field that previously took a raw number (layout rowGap/columnGap/ padding, layoutItem margins/min/max sizes, corner radii r1-r4) now accepts a { token: "name" } reference resolved against the token file's new spacing/radii tables, resolved in content.ts before reaching shape-builders.ts — which stays token-agnostic by design, same seam resolveColor already used for fills/strokes.)

  • Path/ellipse shapes and boolean ops (union/subtract/intersect) in penpot_add_shapes. (Done — circle is an ellipse bounded by x/y/width/height; path accepts an array of { command, params } segments and derives its bounding box automatically from tight cubic-Bézier extrema; bool wraps children shapes (added with parentId = the bool's id, same pattern as frames) with a boolType of union, difference, intersection, or exclusion — the visual result path is computed by Penpot's editor when the file is opened, since the boolean geometry engine lives in Penpot's browser-side WASM module rather than on the server.)

  • Widen penpot_update_shapes to cover path/group/circle/svg-raw/image/bool (and any other shape type). (Done — circle and bool round-trip through their dedicated builders (preserving bool-type and shapes); path rebuilds geometry from the existing path content unless a new content array is supplied in the patch (x/y/width/height are always derived from content and cannot be set independently); group rebuilds geometry and shapes without touching children; image/svg-raw and any other type get a generic geometry-only rebuild, with all type-specific fields carried forward from existing via the { ...existing, ...obj } merge. Also added layout (frame shapes only) and layoutItem (any shape) to the patch schema so auto-layout can be set or replaced after creation — same schema as penpot_add_shapes. A new boolType patch field replaces a bool's operation type in place. computeShapeGeometry and group are now exported from shape-builders.ts and expandTranslateChanges in content.ts now correctly handles groups and paths in align/distribute subtrees.)

  • Penpot team/custom font lookup for penpot_measure_text — if the requested font family is not found on Google Fonts, all Penpot teams accessible to the configured access token are searched for a matching custom/team font variant (get-font-variantsdownload-font). The "normal" style is preferred when multiple variants exist for the same family+weight; if none match anywhere, a combined error message names both sources. (Done — see font-metrics.ts:fetchPenpotFontBytes; PenpotFontClient interface keeps the module decoupled from rpc-client.ts so unit tests can supply a plain mock.)

  • Add a variant to an already-existing component/group. penpot_add_variant appends a new variant to an existing variant group container by re-adding the container frame via add-obj with the new variant's root id appended to its shapes array (the same delete-and-recreate-through-add-obj trick used by penpot_reorder_shapes, avoiding the mov-objects silent-no-op pitfall noted below). The new component is registered in the same update-file call.

  • penpot_list_pages / penpot_rename_page / penpot_delete_pagepenpot_create_page exists and now so does the rest of the page CRUD: penpot_list_pages returns every page's id and name (in order) without pulling the full shape tree; penpot_rename_page renames a page in place; penpot_delete_page removes it (guards against deleting the last page of a file). Both mutations go through update-file with rename-page / del-page change types, the same single-round-trip path every other content tool uses.

  • SSO/OIDC-only instance support for penpot_export_shape via PENPOT_AUTH_TOKEN_COOKIE. (Done — in addition to the existing PENPOT_LOGIN_EMAIL/PENPOT_LOGIN_PASSWORD password-login path, you can now set PENPOT_AUTH_TOKEN_COOKIE to a raw auth-token cookie value obtained by completing the OIDC/SSO flow in a real browser. The profile-id is fetched via get-profile on first use; if the cookie expires a clear error message explains how to renew it. PENPOT_LOGIN_EMAIL/PENPOT_LOGIN_PASSWORD takes precedence when both are set. Fully headless OIDC automation — driving the redirect flow without a browser — is still not implemented, as the cookie-passthrough approach covers the SSO gap without adding a Playwright dependency.)

  • Unit coverage for rpc-client.ts, transit.ts, and tools/project-files.ts — currently zero unit tests, only reachable via the integration suite that's excluded from CI. Mocking fetch for status-code branching (204, 401/403 retry, non-2xx → PenpotRpcError) would catch regressions in CI instead of only on a manual integration run.

  • Gradient and image fills — Fill/Stroke in shape-builders.ts only carry a flat color/opacity; Penpot supports linear/radial gradient fills and image fills, neither reachable from any tool today. Likely the highest-value gap, since most real designs use at least one gradient or image fill somewhere. (Done — fills array on every non-text shape (rect, frame, circle, path, bool) in penpot_add_shapes/penpot_update_shapes/penpot_batch accepts entries of type "solid", "linear-gradient", "radial-gradient", or "image". Gradient stops may use { token: "name" } references against the token file's colors table. Image fills reference an already-uploaded Penpot media object by mediaId UUID — the caller must upload the image to Penpot first (via the Penpot UI or a separate tool). When fills is provided it overrides the legacy fillColor/fillOpacity shorthand entirely; backward compatibility is preserved for all existing callers using fillColor/fillOpacity. The Fill type in shape-builders.ts is now a union of SolidFill, GradientFill, and ImageFill; extractEditableFields round-trips all three types from get-file's camelCase response back to the kebab-case form add-obj expects.)

  • Image shapes / asset upload — there's no image shape builder and no upload-media RPC call anywhere; image/svg-raw shapes are only handled via a generic geometry-only fallback in penpot_update_shapes. Blocks any workflow that needs to bring in photos, icons, or existing SVG art. (Done — penpot_upload_media uploads a media asset to a Penpot file and returns { id, name, width, height, mtype }. Three source modes: filePath (the MCP server reads a local file and POSTs it as multipart), url (Penpot's server fetches the image directly — nothing passes through the MCP server, uses create-file-media-object-from-url), or dataBase64 (base64-encoded bytes, requires mtype). The returned id is then passed as mediaId when calling penpot_add_shapes with type: "image"; mediaWidth/mediaHeight/mtype come from the same response. The image shape builder sets type: "image" with a metadata: { id, width, height, mtype } block (Penpot's wire format, verified from schema:image-attrs in shape.cljc) plus the standard geometry and selrect/transform fields. penpot_update_shapes now handles image shapes with a dedicated case (instead of the previous generic fallback) and accepts mediaId/mediaWidth/mediaHeight/ mtype patch fields to swap the displayed image in place. MIME type is auto-detected from file extension for filePath uploads (.pngimage/png, .jpg/.jpegimage/jpeg, .svgimage/svg+xml, .webp, .gif, .avif).)

  • Opacity/hidden/locked/blend-mode flags (Done — all four fields now available in shape builders and penpot_update_shapes schema: opacity (0-1 numeric), hidden (boolean), blocked (boolean), blendMode (string). Extraction via extractEditableFields converts wire-format "blend-mode" to camelCase "blendMode" for consistent builder interface. Unit and integration tests verify round-trip through Penpot's RPC API.)

  • Rich per-range text formatting — text() in shape-builders.ts always emits a single paragraph with one style run; no per- character-range styling, text-align, line-height, letter-spacing, or text-decoration. (Done — penpot_add_shapes and penpot_update_shapes both accept a paragraphs array on text shapes. Each paragraph sets textAlign plus typography defaults (fontFamily, fontSize, fontWeight, fontStyle, lineHeight, letterSpacing, textDecoration, textTransform, fills/fillColor); its ranges array holds one or more text runs that may individually override any of those fields. Multiple paragraphs produce separate paragraph nodes (line-break boundaries). growType ("auto-width" | "auto-height" | "fixed") and verticalAlign ("top" | "center" | "bottom") are also now settable on text shapes. The legacy characters/fontFamily/fontSize/fontWeight/ fillColor shorthand is fully preserved for callers that don't need per-range control. extractEditableFields now returns a full paragraphs array for text shapes so a geometry-only update patch (x/y/width/height only) no longer collapses a rich-text shape back to single-paragraph. Verified field names against Penpot's common/src/app/common/types/text.cljc text-node-attrs / paragraph-attrs / root-attrs.)

  • Group/ungroup as first-class tools — group() already exists as an internal builder, but nothing exposes grouping existing shapes or ungrouping a group via an MCP tool. (Done — penpot_group_shapes takes a shapeIds array of sibling shapes (all must share the same parent) and wraps them in a new group at the topmost selected shape's z-order position; the group's bounding box is the union of the children's selrects (rotation-aware bounds). penpot_ungroup_shapes takes a groupId and dissolves it: each child is reparented to the group's former parent at the group's z-order position (preserving relative child order) and the group shape is deleted. Both are implemented as single update-file change-sets — group creation sends add-obj for the new group, one add-obj per child (to update parent-id) and an add-obj for the updated parent's shapes array; ungroup does the same in reverse then appends a del-obj. Requires siblings only — shapes with different parents must be moved to a common parent first.)

  • Resize constraints — Penpot's constraints-h/constraints-v (how a shape behaves when its parent resizes) are never set or exposed in EditableShapeFields or the update patch schema. (Done — constraintsH (left/right/leftright/center/scale) and constraintsV (top/bottom/topbottom/center/scale) are now accepted by every shape builder (rect, frame, text, circle, path, bool, group, image), emitted as constraints-h/ constraints-v in the wire format. Both fields are settable at creation time via penpot_add_shapes and patchable via penpot_update_shapes/ penpot_batch; extractEditableFields reads them back from get-file's camelCase response so a partial update patch preserves the existing constraints when neither field is included in the patch.)

  • Batch/whole-page export — penpot_export_shape takes exactly one shapeId per call, with no multi-shape or multi-page batch export, and only png/svg (no PDF). (Done — penpot_export_batch accepts either a shapeIds array (same pageId, any mix of format/scale) or a shapes array where each entry may carry its own pageId, format, scale, and name — enabling whole-file exports across multiple pages in a single POST /api/export call. Parsing each result's ~:uri/ ~:mtype/~:filename via matchAll on the transit+json response; asset downloads are parallelised. Both tools also accept pdf as a format value. Multi-page export was previously gated on the MCP schema, not the exporter-client, which already sent per-spec pageIds.)

  • Shared-library components — penpot_list_components and penpot_add_component_instance only look at the current file's own components map; there's no support for pulling components from a separate connected shared-library file. (Done — penpot_list_components now accepts includeLibraries: true, which calls get-file-libraries to enumerate all linked library files (direct and transitive) and then calls get-file on each to read its components map; each library component entry carries a libraryFileId and libraryFileName field in the response. penpot_add_component_instance now accepts an optional libraryFileId; when supplied, the component's main-instance tree is looked up in that library file instead of the current file, and the cloned instance's root component-file field is set to the library file's id (matching what Penpot's own editor writes for cross-file component instances). Note: get-file-libraries returns file metadata only — it does not include shape/component data — so a separate get-file call per library is necessary to enumerate components; this is the same two-step pattern Penpot's own frontend uses.)

  • Comments API — penpot_list_comment_threads lists all threads for a file; penpot_get_comments fetches the replies inside a thread; penpot_create_comment_thread pins a new thread to a canvas position (x/y + optional frameId); penpot_create_comment adds a reply; penpot_update_comment edits an existing comment's text; penpot_resolve_comment_thread marks a thread resolved or reopens it; penpot_delete_comment removes a single reply; and penpot_delete_comment_thread removes a whole thread and all its replies. All eight tools go through PenpotRpcClient methods that send kebab-case JSON params to the get-comment-threads / create-comment-thread / update-comment-thread / delete-comment / etc. RPC endpoints (same call pattern as every other tool). Wire format verified against Penpot's frontend source (data/comments.cljs): position is a nested { x, y } JSON object; frame-id is optional on creation (omit to place on the page root); is-resolved controls thread resolution state.

  • Text search-and-replace across a file — penpot_replace_text finds every text shape on a page whose runs contain a literal search string and rewrites all occurrences in a single update-file call. Matching is case-insensitive by default (caseSensitive: true to override); an optional limit caps the number of shapes touched; empty replacement deletes matches. Replacement is per text-run (leaf node): a search string that spans two adjacent runs within a paragraph is not matched — this covers the common case where all text is in a single run per paragraph. (Done — regex metacharacters in the search string are escaped so . / * / ( etc. are always matched literally, not as pattern operators.)

  • Cross-page/whole-file undo — penpot_checkpoint now accepts an optional pageId; omit it to snapshot every page in the file in one call. penpot_restore_checkpoint restores all snapshotted pages in a single update-file call. Checkpoints still live in server memory and don't survive a restart; persistence across restarts is still not implemented (callers needing that should snapshot via penpot_get_file_snapshot themselves). (Done — Checkpoint in checkpoints.ts now holds a pages map (pageId → objects) instead of a single pageId/objects pair; the restore loop iterates every entry and accumulates all changes before sending one update-file call — the same single-round-trip pattern used by penpot_batch.)

  • Fully headless OIDC/SSO login — drive the redirect flow without a browser, instead of requiring PENPOT_AUTH_TOKEN_COOKIE to be obtained manually beforehand. (Done — PENPOT_OIDC_USERNAME + PENPOT_OIDC_PASSWORD trigger a new 'oidc' auth mode in the exporter client. The server follows Penpot's /api/auth/oauth/{provider} redirect chain to the identity provider's login page via plain HTTP (Node.js http/https modules, no browser dependency), parses the HTML login form heuristically to identify username/email and password fields (including hidden CSRF/state fields, which are preserved and re-submitted verbatim), and captures the resulting auth-token cookie. Multi-step flows — where the IdP shows username on page 1 and password on page 2 (Authentik-style) — are handled by a MAX_FORM_STEPS loop that re-parses and re-submits until the cookie appears. 307/308 redirects preserve POST method+body; 301/302/303 switch to GET per RFC 7231. The cookie jar correctly captures Penpot's own state/CSRF cookies set on the initial redirect so they are forwarded to the IdP's callback. Re-logs in automatically on 401/403 (same as password mode). PENPOT_OIDC_PROVIDER controls the provider name (default "oidc"). Does not work for JavaScript-driven IdP login pages — use PENPOT_AUTH_TOKEN_COOKIE for those. Unit-tested via injected mock fetcher; integration test requires a live OIDC provider and is not in CI.)

  • Checkpoint persistence across server restarts — checkpoints (penpot_checkpoint/penpot_restore_checkpoint) currently live only in the MCP server's own memory and are lost on restart. Persisting them to disk (or letting the caller supply a checkpoint id to save/load) would let a long-running multi-step edit survive a server restart mid-way through. (Done — set PENPOT_CHECKPOINTS_PATH to a local directory; the server writes each checkpoint as <uuid>.json into that directory and reloads all files from it at startup. initCheckpointStore(dir) is called once in server.ts main; saveCheckpoint is now async and awaits the writeFile so the caller knows immediately if the write failed; deleteCheckpoint is also async and removes the corresponding file from disk. No change to the in-memory behaviour when the env var is unset.)

  • Single-call multi-page batch export — penpot_export_batch exports multiple shapes on one page in a single call; exporting across pages still requires one call per pageId. Worth revisiting if batch export becomes a bottleneck for whole-file exports. (Done — penpot_export_batch now accepts either shapeIds (single-page shorthand, backward-compatible) or a shapes array where each entry carries its own pageId, format, scale, and optional name. The exporter-client already sent per-spec pageIds in a single POST /api/export; the only change was widening the MCP tool's Zod schema to expose this capability. Cross-field validation (at least one of shapeIds/shapes; not both; pageId required for shapeIds; every shapes entry has a pageId either per-entry or via the top-level default) is done with .refine() on a separate exportBatchInput schema while exportBatchBaseSchema (the raw ZodObject) is passed to the MCP SDK's inputSchema to preserve .shape.)

  • Stale-write detection via revn — every write tool (penpot_update_shapes/penpot_batch/penpot_restore_checkpoint/etc.) currently sends update-file blind, without checking the file's current revn (revision number) first. If a human has the file open in the Penpot editor and edits concurrently with an agent session, whichever write lands second silently overwrites the other with no error. Penpot's own update-file RPC takes a revn and is expected to reject a change-set built against a stale one; today nothing here reads or checks it beforehand. Fix is to fetch current revn before writing and surface a clear "file changed underneath you, re-fetch and retry" error on rejection, rather than actually merging concurrent edits (which would need Penpot's own operational-transform logic). (Done — PenpotStaleWriteError is thrown by updateFile in rpc-client.ts whenever the update-file response includes a non-empty lagged array, meaning Penpot applied change-sets from another session before ours. Penpot's collaborative engine still applies our write on top, so the write succeeds at the HTTP level, but the error surfaces the concurrent-edit situation with a clear message that names the new revn and tells the caller to re-fetch via penpot_get_file_snapshot before making further edits. Applies to all write tools automatically — penpot_add_shapes, penpot_update_shapes, penpot_delete_shapes, penpot_batch, penpot_restore_checkpoint, penpot_group_shapes, penpot_ungroup_shapes, penpot_clone_shapes, penpot_reorder_shapes, penpot_align_shapes, penpot_distribute_shapes, page CRUD, etc. — since they all go through updateFile. PenpotStaleWriteError carries laggedCount (number of concurrent change-sets) and result (the full update-file response, including the new revn) so callers that need the new revision can read it from the error rather than requiring an additional round trip.)

  • Version history / branching support — penpot_list_file_snapshots lists all named snapshots for a file (both user-created versions and system auto-backups); penpot_create_file_snapshot saves the current state as a named version (optional label; Penpot generates a timestamp label when omitted); penpot_restore_file_snapshot rolls the live file back to any listed snapshot (Penpot automatically creates a system backup of the current state before applying the restore, so a restore is itself undoable by restoring the most-recent system entry); penpot_rename_file_snapshot renames a user-created snapshot; penpot_delete_file_snapshot removes one; penpot_lock_file_snapshot / penpot_unlock_file_snapshot pin/unpin a snapshot against accidental deletion (only the snapshot's creator can lock/unlock, and only user-created snapshots support locking — system backups expire automatically). penpot_get_file_snapshot_data returns the full file content at a specific snapshot for read-only inspection or comparison without touching the live file. Wire format verified against backend/src/app/rpc/commands/files_snapshot.clj and backend/src/app/features/file_snapshots.clj.

  • Component-instance drift/override visibility — Penpot's composition model is tokens → elements → components (with variants) → screens built from component instances, and today the tools that read shape state (penpot_get_shape, penpot_find_shapes) don't surface where a shape sits in that hierarchy. A component instance's shape-ref link to its main component (and whether it has drifted — fields overridden since being placed, or fully detached) is invisible: an agent editing an instance via penpot_update_shapes can't currently tell "this edit creates a per-instance override" from "this edit changes a raw shape," which matters for keeping instances honestly in sync with their component rather than silently diverging the way ad-hoc UI edits can. Scope: extend penpot_get_shape/penpot_find_shapes to report link state (linked / detached / not-an-instance) and, when linked, which fields differ from the main component's current definition — read-only visibility first; explicit detach/relink tools and discovery-first tool guidance (checking penpot_list_components before creating shapes that duplicate an existing component) are possible natural follow-ons once drift is at least visible. (Done — both penpot_get_shape and penpot_find_shapes now include component link state in their output. penpot_get_shape adds a top-level componentInfo field with linkState ("linked" / "detached" / "not-an-instance" / "main-component-root"), the componentId and componentFileId when present, mainInstanceId / mainInstancePage for same-file linked instances, and a driftedFields array (camelCase field names) listing which visual properties on this instance differ from the main component's current definition — empty array means fully in sync. penpot_find_shapes adds linkState and componentId to each match entry, plus driftedFields for same-file linked instances. Drift is compared over: name, fills, strokes, shadows, opacity, hidden, blendMode, width, height, constraintsH, constraintsV, and content (text shapes); position x/y is excluded since every placed instance lives at a different canvas location by design. Library components (componentFile ≠ current file) report linkState: "linked" but driftedFields is omitted since the library file's pages would require an extra RPC call — use penpot_get_shape on a library instance root, then follow its mainInstanceId/mainInstancePage manually if cross-file drift is needed.)

Available Tools

54 tools
penpot_add_component_instanceA

Place a copy (instance) of an existing component at a new position on a page. Clones the component's full main-instance shape tree with fresh ids, linked back to the main via shape-ref so Penpot recognizes it as a component copy. For components from a connected shared-library file, pass the library file's id as libraryFileId (returned by penpot_list_components with includeLibraries: true).

ParametersJSON Schema
NameRequiredDescriptionDefault
xYes
yYes
fileIdYes
pageIdYes
frameIdNo00000000-0000-0000-0000-000000000000
parentIdNo00000000-0000-0000-0000-000000000000
componentIdYes
libraryFileIdNoThe id of the shared-library file that owns this component. Returned as libraryFileId by penpot_list_components when includeLibraries is true. Omit when the component belongs to the current file.

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses that the tool clones the shape tree, assigns fresh ids, and maintains a shape-ref link for Penpot recognition. This adds value beyond a simple 'add instance' statement. However, it does not mention permissions, reversibility, or potential side effects (e.g., affecting the main component).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise: three sentences covering purpose, behavior, and library usage. It is front-loaded with the core action and includes essential context without redundancy. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 8 parameters, no output schema, and no annotations, the description covers the main functionality well but omits details on optional parameters (frameId, parentId) and does not mention return values or error conditions. It feels just adequate but not fully complete for a tool of moderate complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is only 13%, so the description must compensate. It adds significant value for libraryFileId, explaining its purpose and source. x and y are implied as position coordinates but lack format details. frameId and parentId are not mentioned at all, despite having defaults. Overall, the description partially compensates for low schema coverage but leaves gaps.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly specifies the action (place a copy/instance of a component), the resource (existing component on a page), and the result (cloned shape tree with fresh ids linked via shape-ref). It distinguishes itself from sibling tools like penpot_create_component (creates a component from shapes) and penpot_clone_shapes (clones arbitrary shapes) by focusing on component instances and mentioning library support.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear guidance on when to use the libraryFileId parameter (for components from shared-library files) and when to omit it (current file). It references penpot_list_components as a sibling to retrieve library IDs. However, it does not explicitly state when not to use this tool or compare to alternatives like penpot_clone_shapes for non-component shapes.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_add_shapesB

Add one or more shapes (rect, frame, text, circle, path, bool, image) to a page in a Penpot file. "circle" is an ellipse bounded by x/y/width/height (width === height for a true circle). "path" takes a "content" array of path commands ({ command: "move-to"|"line-to"|"curve-to"|"close-path", params: {x,y,...} }); its bounding box is derived automatically from the commands. "bool" is a boolean operation (boolType: "union"|"difference"|"intersection"|"exclusion") over its children — add the bool shape first (with an explicit "id"), then add children with "parentId" matching that id (same pattern as frames); Penpot's editor computes the visual result when the file is opened. "image" displays an uploaded media object — first call penpot_upload_media to get a mediaId, then provide mediaId/mediaWidth/mediaHeight (source pixel dimensions) along with x/y/width/height for the canvas placement. Colors accept either a literal hex string or a { token: "name" } reference resolved against the project token file; the same applies to numeric spacing/radius fields (layout gaps/padding, layoutItem margins/min/max sizes, corner radii r1-r4), resolved against the token file's "spacing"/"radii" tables, and to a shape's "shadows" array (each entry either an inline { style, color, opacity, offsetX, offsetY, blur, spread } object or a { token: "name" } reference into the token file's "shadows" table). Shapes may be rotated via the "rotation" field (degrees, clockwise, about the shape's center). Frames may declare flex or grid auto-layout via "layout"; any shape may set "layoutItem" to control its own placement within an auto-layout parent (sizing, alignment, margins, and, for grid parents, row/column).

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
pageIdYes
shapesYes
tokensPathNo/app/tokens.json

TDQS

B3.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It transparently explains behavioral details: how shapes are added, the prerequisite for images (calling another tool), token resolution via file, rotation mechanics, and auto-layout rules. It also notes that bool shapes' visual result is computed when the file is opened.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single dense paragraph that covers extensive detail. While the first sentence is clear and front-loaded, the overall length could be trimmed or structured with bullet points for easier parsing. It is comprehensive but not concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complex input schema and no output schema, the description is fairly complete. It covers shape types, token references, auto-layout, prerequisites (image upload), and rotation. It lacks details on return values, error handling, and effects on the page, but the core functionality is well explained.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must add meaning. It thoroughly explains the 'shapes' parameter with all shape types and their sub-properties. However, it provides little to no explanation for the 'fileId', 'pageId', and 'tokensPath' parameters, leaving the agent to infer their roles.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool adds one or more shapes to a Penpot page and lists the specific shape types (rect, frame, text, etc.). This distinguishes it from sibling tools like penpot_update_shapes or penpot_delete_shapes, though it does not explicitly contrast with penpot_add_component_instance.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides detailed guidance on when to use each shape type within the tool, but it does not discuss when to prefer this tool over alternatives (e.g., penpot_update_shapes for modifying existing shapes, penpot_clone_shapes for duplicating). No 'when not to use' or sibling comparisons are given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_add_variantB

Add a new variant to an already-existing variant group container (created via penpot_create_variant_group). Accepts the same shape specs as one entry in penpot_create_variant_group's "variants" array. The containerId must be the id of the variant group's container frame (returned as "containerId" by penpot_create_variant_group, or found via penpot_find_shapes/penpot_list_components). Returns the new componentId and mainInstanceId, usable with penpot_add_component_instance.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
pageIdYes
variantYes
groupNameYes
tokensPathNo/app/tokens.json
containerIdYes

TDQS

B3.2/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries full burden. It fails to disclose whether this operation is destructive, what permissions are needed, rate limits, or error conditions. The description only covers function, not behavioral impact.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured paragraph with no fluff. It front-loads purpose and then explains containerId source and return values. Slightly more structure (e.g., bullet points for parameters) could improve it.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 6 parameters (5 required) and no output schema, the description omits explanations for fileId, pageId, and groupName. It relies on cross-references to other tools without fully explaining the variant parameter. The agent would struggle to correctly invoke this tool without additional knowledge.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%. Despite a highly complex variant parameter with extensive nested objects, the description only vaguely mentions it mirrors shape specs. It provides no guidance on required structure, property meanings, or usage patterns beyond a cross-reference to another tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb (add) and resource (new variant to existing variant group). It distinguishes from siblings by referencing the prerequisite tool (penpot_create_variant_group) and explaining containerId origin. This makes the purpose specific and differentiated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when to use this tool (after creating a variant group) and how to obtain the containerId. It mentions the return values link to penpot_add_component_instance. However, it does not explicitly state when not to use it or compare with alternatives like penpot_create_variant_group.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_align_shapesA

Align two or more shapes to a common edge or center, matching Penpot's own align actions — instead of the caller computing pixel positions itself from penpot_get_shape results. "edge" is one of: "left"/"right" (snap every shape's left/right edge to the leftmost/rightmost shape's), "top"/"bottom" (same, vertically), "center-h" (center every shape horizontally on the group's mid-x), or "center-v" (center vertically on the group's mid-y). Aligns on each shape's visible bounding box (its selrect), so rotated shapes line up by their rendered bounds; the group as a whole never moves (the reference line comes from the shapes' own extent). Applied as a single update-file change-set; shapes already on the reference line are left untouched.

ParametersJSON Schema
NameRequiredDescriptionDefault
edgeYes
fileIdYes
pageIdYes
shapeIdsYes
tokensPathNo/app/tokens.json

TDQS

A4.1/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully covers behavioral traits: aligns on selrect, group immobility, atomicity, and idempotency. This is comprehensive and adds value beyond the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but well-structured, front-loading purpose and then detailing behavior. Every sentence adds value, though bullet points could improve readability for edge options.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Behavior is well covered, but the description lacks information about return values, errors, or prerequisites. Given no output schema, some completeness is lost.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must explain all parameters. It only explains the 'edge' parameter in detail. Standard parameters like fileId, pageId, shapeIds, tokensPath are not described, leaving a significant gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'align', the resource 'shapes', and specifies the alignment follows Penpot's own actions. It distinguishes from manual computation and implies a unique value proposition.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly contrasts with manual computation, providing clear usage context. It does not explicitly list when not to use or compare to sibling tools like distribute, but the intent is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_batchA

Apply an ordered list of create/update/delete/reorder operations to a page as a single update-file change-set — one revn/vern round trip no matter how many shapes are touched, instead of one RPC call per shape (and the races on revn that come with that). Each op is one of: { op: "create", shape: }, { op: "update", patch: }, { op: "delete", shapeId }, or { op: "reorder", shapeId, action, targetId? } (same actions as penpot_reorder_shapes). Ops are applied in array order and each sees the effect of every earlier op in the same call — a "create" can set an explicit "id" (a UUID) and be referenced as a later shape's parentId/frameId (to build a frame and its children in one call), and a later op can update/delete/reorder a shape created earlier in the same batch. Returns one result entry per op, in the same order as "ops".

ParametersJSON Schema
NameRequiredDescriptionDefault
opsYes
fileIdYes
pageIdYes
tokensPathNo/app/tokens.json

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses that ops are applied in array order, each sees effects of prior ops, and create ops can set explicit IDs for later reference. Returns one result per op. This covers key behavioral traits beyond what schema alone provides.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the key benefit (single round trip) and efficiently covers operation types, ordering semantics, and inter-op references without wasted words. Every sentence adds distinct value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of batch operations and the absence of an output schema, the description adequately covers purpose, usage, operation details, and return format. It does not mention error handling or limits, but the core functionality is fully described.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so description must compensate. It thoroughly explains the 'ops' parameter structure (four operation types, shape spec references penpot_add_shapes, update patch references penpot_update_shapes, etc.). However, it does not document 'fileId', 'pageId', or 'tokensPath' beyond their existence, leaving those parameters with only schema defaults/constraints.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb+resource: 'Apply an ordered list of create/update/delete/reorder operations to a page as a single update-file change-set'. It clearly distinguishes from siblings like penpot_add_shapes and penpot_update_shapes by emphasizing batch execution and reduced round trips.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when to use (batching multiple shape operations) and contrasts with individual RPC calls ('instead of one RPC call per shape'), but does not explicitly define when not to use or list alternatives beyond referencing sibling specs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_checkpointA

Snapshot shapes so a subsequent penpot_restore_checkpoint call can undo whatever happens between now and then — including a wrong penpot_delete_shapes call, which otherwise has no undo path short of Penpot's own UI. When pageId is supplied only that page is snapshotted; omit pageId to snapshot every page in the file (whole-file checkpoint). Reusable across multiple restores until explicitly discarded via penpot_discard_checkpoint. When the server is configured with PENPOT_CHECKPOINTS_PATH the checkpoint is also written to disk and will survive a server restart; without that setting it lives only in process memory and is lost on restart. Call this immediately before a risky multi-step edit; pass the returned checkpointId to penpot_restore_checkpoint to undo everything since.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
pageIdNo

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite no annotations, the description fully discloses behavioral traits: snapshot scope, persistence (disk vs memory based on server config), reusability, and lifecycle. It also explains the relationship with other checkpoint tools.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single well-structured paragraph that front-loads the core purpose and progressively adds detail. It is concise without unnecessary words, though bullet points could improve scanability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 2 parameters and no output schema, the description comprehensively covers input semantics, side effects, persistence behavior, and lifecycle. It provides all necessary context for an agent to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description adds significant meaning: clarifies that pageId scopes snapshot to a single page and omitting it creates a whole-file checkpoint. fileId is implied but not elaborated, but the schema already marks it required.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Snapshot shapes so a subsequent penpot_restore_checkpoint call can undo whatever happens between now and then' and gives a concrete scenario (undo delete_shapes). It distinguishes itself from sibling tools like penpot_restore_checkpoint and penpot_discard_checkpoint.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit guidance on when to use ('Call this immediately before a risky multi-step edit'), how to supply pageId vs omit it for whole-file checkpoint, and what to do with the returned checkpointId. The description also clarifies reuse and discard lifecycle.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_clone_shapesA

Duplicate one or more existing shapes (and, for frames/groups, their full descendant subtree) on a page, each with fresh ids. This is plain shape duplication — like Penpot's own Ctrl+D — not a component instance; use penpot_add_component_instance instead if you want a copy linked back to a component's main instance via shape-ref. Optional dx/dy offset each clone from its source (default: no offset, stacked directly on top of the original); optional parentId/frameId reparent every cloned root onto a new parent instead of staying alongside its source. If a cloned shape already carries component/variant tags (it is itself a component's main instance or an existing instance), those tags are carried over unchanged.

ParametersJSON Schema
NameRequiredDescriptionDefault
dxNo
dyNo
fileIdYes
pageIdYes
frameIdNo
parentIdNo
shapeIdsYes

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description covers key behaviors: fresh IDs, optional offset (dx/dy), reparenting (parentId/frameId), and carrying over component/variant tags. It lacks information on error handling (e.g., non-existent shapeIds) or performance considerations, but adequately discloses the core behavioral traits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single dense paragraph containing multiple sentences. While it front-loads the main purpose, the structure could be improved with clearer separation of details (e.g., bullet points for optional parameters). It is moderately concise but could be more reader-friendly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the core functionality and key optional behaviors, but leaves gaps: no mention of error scenarios, success confirmation, or idempotency. Given the lack of output schema and annotations, a more complete description would include these aspects.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must explain parameters. It describes dx/dy, parentId/frameId, and shapeIds in context, but does not explicitly define fileId and pageId beyond their required status. The explanation is functional but could be more systematic for each parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it duplicates shapes with fresh IDs, using the verb 'duplicate' and specifying the resource 'shapes on a page'. It also distinguishes itself from the sibling tool penpot_add_component_instance, which creates linked component instances.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly contrasts this tool with penpot_add_component_instance, guiding when to use plain duplication vs. component instances. However, it does not mention other alternatives like penpot_add_shapes for creating new shapes, leaving some usage scenarios implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_create_commentB

Add a reply comment to an existing comment thread.

ParametersJSON Schema
NameRequiredDescriptionDefault
contentYes
threadIdYes

TDQS

B3.3/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It only says 'add a reply comment,' which implies a write operation, but does not disclose any behavioral traits such as required permissions, side effects, or what happens if the thread does not exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single sentence of 8 words, with no wasted text. It is front-loaded and efficiently conveys the tool's purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no output schema and two required parameters, the description lacks important context such as return value, error conditions, and confirmation actions. It leaves gaps that an agent needs to handle correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It indirectly ties threadId to 'existing comment thread' and content to 'reply comment,' but adds no explicit meaning beyond property names. This is minimal and insufficient to fully document the parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Add' and the resource 'reply comment to an existing comment thread.' It distinguishes from sibling tools like penpot_create_comment_thread (creates new thread) and penpot_update_comment (updates existing comment).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for replying to an existing thread but does not explicitly state when to use this tool versus creating a new thread or updating a comment. No direct guidance on prerequisites or alternatives is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_create_comment_threadA

Create a new comment thread pinned to a canvas position on a page. x/y are the canvas coordinates where the comment pin appears. Optionally supply frameId to attach the thread to a specific frame; omit it to place the thread on the page root.

ParametersJSON Schema
NameRequiredDescriptionDefault
xYes
yYes
fileIdYes
pageIdYes
contentYes
frameIdNo

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden. It discloses the coordinate and frame attachment behavior, but does not mention any potential side effects, permission requirements, or limits. The tool appears additive, so a score of 3 reflects adequate but not thorough transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no wasted words. Front-loaded with the main action, followed by parameter clarifications. Efficient and to the point.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 6 parameters and no output schema, the description covers the purpose, coordinate semantics, and optional frame attachment. It does not describe the response format, but that is acceptable without output schema. Missing minor details like content being the initial comment text, but overall sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must add meaning. It explains x, y, and frameId, providing value beyond the schema. However, it does not explain fileId, pageId, or content (though these are partially inferable). The description partly compensates for the low coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it creates a new comment thread pinned to a canvas position on a page. The verb 'create' and resource 'comment thread' are specific, and it distinguishes from sibling tools like penpot_create_comment (single comment) and penpot_list_comment_threads (listing).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description tells when to use frameId (to attach to a specific frame) and when to omit it (to place on page root), providing clear context. However, it does not explicitly mention when not to use this tool versus alternative comment-related tools like penpot_create_comment.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_create_componentA

Register a new shape tree as a Penpot component (its "main instance"). Accepts the same shape specs as penpot_add_shapes (rect/frame/text, with layout/rotation/etc.); give each shape an explicit "id" so children can nest under a sibling via matching "parentId"/"frameId". The one shape not parented to a sibling in this call becomes the component root. Returns the componentId, usable with penpot_add_component_instance to place copies elsewhere.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
pageIdYes
shapesYes
tokensPathNo/app/tokens.json
componentNameYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It describes that the tool registers a component (a creation action) and returns a componentId. It explains the shape tree structure and root detection, but does not disclose whether the operation is destructive, requires specific permissions, or if it modifies the page content. The disclosure is adequate but lacks depth.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, with three sentences that front-load the main action, then detail the shapes parameter, and finally state the return value. Every sentence contributes valuable information. There is no fluff, though a more structured format (e.g., bullet points) could improve scanability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (5 params, no output schema), the description covers the core concepts (shapes structure, root detection, return value) but misses the 'tokensPath' parameter and any prerequisites (e.g., fileId/pageId must exist). It does not mention error conditions or constraints like unique component names. Completeness is adequate but not thorough.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 0% description coverage, so the description must compensate. It adds significant meaning for the 'shapes' parameter by explaining id, parentId, and frameId usage, and that the unparented shape becomes the root. However, it does not explain the 'tokensPath' parameter at all. This leaves a gap, but overall improves understanding beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that the tool registers a new shape tree as a Penpot component, specifying the verb 'register' and the resource 'component'. It distinguishes the tool from siblings by mentioning the returned componentId is usable with penpot_add_component_instance, but does not explicitly differentiate from penpot_add_shapes. The purpose is specific and actionable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for using the tool: to create a component from a shape tree. It explains the relationship with penpot_add_shapes (same shape specs) and how nesting works via id/parentId, establishing when this tool should be used. It does not explicitly state when not to use it, but the guidance is sufficient for an AI agent to infer the correct use case.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_create_fileC

Create a new file within a Penpot project.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
projectIdYes

TDQS

C2.5/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations, so description must fully disclose behavior. It only states the basic action, omitting details like idempotency, permission requirements, or error conditions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

While concise, it is under-specified for a tool with two required parameters and no output schema, lacking necessary detail.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema or annotations, the description fails to explain return values, side effects, or constraints, making it incomplete for informed tool use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%; description adds no meaning to parameters like projectId or name, leaving the agent to infer their format or sources.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Create' and the resource 'a new file within a Penpot project', differentiating it from siblings like penpot_create_page and penpot_create_project.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives, no prerequisites or context provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_create_file_snapshotA

Create a named snapshot of the current state of a Penpot file (equivalent to "Save version" in the Penpot UI). The optional label names the version; if omitted Penpot generates a timestamp-based label. Returns the new snapshot's metadata including its id. Only user-created snapshots can later be renamed, deleted, or locked.

ParametersJSON Schema
NameRequiredDescriptionDefault
labelNo
fileIdYes

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, description carries full burden. It explains creation, return of metadata including id, and constraints on later operations. However, it doesn't disclose potential side effects like file modification dates or authorization requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences efficiently convey purpose, parameter details, and a behavioral note. No fluff; every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple create tool with 2 params and no output schema, description covers purpose, parameter behavior, and return value. Lacks mention of prerequisites or rate limits, but sufficient for typical use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 0% description coverage. Description adds meaning: fileId is required, label is optional with auto-generated timestamp; also explains consequences of omitting label. Significantly augments schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states verb (create), resource (snapshot of a file), and purpose, and distinguishes from siblings like get_file_snapshot and delete_file_snapshot.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains optional label and auto-generation behavior, and notes that only user-created snapshots can be renamed/deleted/locked. Lacks explicit when-not-to-use or alternatives, but provides clear context for usage decisions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_create_pageC

Create a new, empty page in a Penpot file.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
fileIdYes

TDQS

C2.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, and the description only states that the tool creates a page, implying mutation. It does not disclose side effects, required permissions, validation rules, or any potential limitations. Given the lack of annotations, more detail is necessary.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single concise sentence with no fluff, but it is too brief to convey essential information. Being concise is positive, but it sacrifices completeness for brevity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With only 2 parameters, no output schema, and no annotations, the description should provide more context about the expected behavior, return value, and validation. The current description leaves significant gaps for an AI agent to use correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 0% description coverage and neither 'fileId' nor 'name' are explained in the description. The agent receives no semantic context beyond the parameter names and types, so it cannot infer formatting, constraints, or the relationship between parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool creates a new empty page within a Penpot file. It uses a specific verb ('Create') and resource ('page'), distinguishing it from sibling tools like penpot_create_file or penpot_create_project. However, it does not explicitly clarify that the file must already exist.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives, nor any prerequisites (e.g., file must exist). The description lacks context for appropriate usage scenarios or exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_create_projectC

Create a new project within a Penpot team.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
teamIdYes

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries full burden. It only states creation without disclosing any behavioral traits like destructive potential, authentication needs, or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single concise sentence that front-loads the action. While efficient, it could benefit from additional structure for longer descriptions.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (2 params, no output schema), the description is minimal but insufficient. It lacks parameter explanations and behavioral context that would make it complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the description adds no meaning to parameters teamId and name beyond their names. No explanation of what teamId refers to or constraints on name.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Create a new project within a Penpot team' clearly states the verb (create) and resource (project within a Penpot team), distinguishing it from sibling tools like penpot_create_file and penpot_create_page.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool vs alternatives, no prerequisites or context provided. The description lacks any when-to-use or when-not-to-use information.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_create_variant_groupC

Create a Penpot variant group: a container frame physically grouping two or more component main instances that share property axes (e.g. a "Button" with Type=Primary/Secondary variants), enabling Penpot's variant swap UI/switchVariant on instances of these components. Each entry in "variants" accepts the same shape specs as penpot_create_component. Returns one componentId per variant, usable with penpot_add_component_instance.

ParametersJSON Schema
NameRequiredDescriptionDefault
xYes
yYes
widthYes
fileIdYes
heightYes
layoutNo
pageIdYes
variantsYes
groupNameYes
tokensPathNo/app/tokens.json

TDQS

C2.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses that it creates a container frame and returns component IDs, but does not mention side effects, permissions, rate limits, or any destructive behavior. For a creation tool, more behavioral context (e.g., whether it modifies existing shapes) is needed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single paragraph that efficiently conveys the core concept without excessive verbosity. However, it could benefit from a more structured breakdown (e.g., bullet points for key aspects) but remains sufficiently concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (10 parameters, no output schema, no annotations), the description is incomplete. It omits details on required parameters, return value format, and prerequisites. The reference to penpot_create_component is helpful but insufficient for an agent to use the tool correctly without external knowledge.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%: the description does not explain any of the 10 parameters except a vague reference to 'variants' accepting same specs as penpot_create_component. No parameter meanings, formats, or valid values are clarified, leaving the agent dependent solely on the schema, which lacks descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool creates a variant group and explains its purpose with a concrete example (Button with Type variants). It distinguishes from sibling tools by name, but does not explicitly contrast with penpot_add_variant, so slightly less than perfect.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies when to use (when you need variant groups with property axes) but provides no explicit guidance on when not to use or alternatives like penpot_add_variant. No usage context beyond the basic function.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_delete_commentA

Delete a single comment (reply) from a thread. Only the comment author can delete their own comment.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYes

TDQS

A3.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses author-only deletion, but lacks details on side effects (e.g., thread behavior), idempotency, or error conditions. With no annotations, the description carries the full burden and is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences with no redundant words. Action and constraint are front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Adequate for a simple delete operation with one parameter and no output schema. However, missing information on return value, error handling, or prerequisites beyond authorship.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Single parameter 'id' has no description; the tool description does not explicitly define it as the comment ID. Schema coverage is 0%, and the description fails to compensate by explaining that the id parameter is the unique identifier of the comment.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states 'Delete a single comment (reply) from a thread', specifying the action and resource. Distinguishes from sibling tools like penpot_create_comment and penpot_delete_comment_thread.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States 'Only the comment author can delete their own comment', providing a clear usage condition. Does not explicitly compare to alternatives, but the context of delete vs. update or create is implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_delete_comment_threadA

Delete an entire comment thread and all its replies. Only the thread owner can delete it.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYes

TDQS

A3.7/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description explains the deletion of entire thread and replies and owner restriction. However, lacks details about reversibility or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences, each adding necessary information with no waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Adequate for a simple delete tool with one parameter, but lacks parameter meaning. Otherwise covers purpose and key constraint.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Only parameter 'id' has no description. The tool description does not clarify that 'id' refers to the comment thread ID, leaving ambiguity.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the action (delete) and resource (comment thread and all replies), distinguishing it from sibling tools like penpot_delete_comment and penpot_resolve_comment_thread.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides ownership constraint ('Only the thread owner can delete it'), but does not explicitly guide when to use versus alternatives like penpot_delete_comment for individual replies.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_delete_fileC

Delete a Penpot file.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes

TDQS

C2.3/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description must carry the full burden. It only says 'Delete a Penpot file' without disclosing permanence, side effects, or required permissions. This is insufficient for a destructive operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very short (one sentence) but is under-specified. It lacks necessary detail, making it insufficient rather than efficiently concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool deletes files (destructive), has no annotations, no output schema, and only one unexplained parameter, the description is far from complete. An agent has too little information to use the tool safely.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description should explain the parameter `fileId`. It does not, providing no added meaning beyond the schema's type and requirement.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Delete a Penpot file' which specifies the verb (delete) and the resource (Penpot file). It is clear and distinguishes from siblings that operate on different resources like projects.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives like penpot_rename_file or penpot_delete_project. There are no hints about prerequisites or contexts where deletion is appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_delete_file_snapshotA

Delete a user-created snapshot. Only snapshots with createdBy: "user" can be deleted; system-created backups expire automatically and cannot be manually deleted. Locked snapshots (those with a lockedBy field set) cannot be deleted — call penpot_unlock_file_snapshot first.

ParametersJSON Schema
NameRequiredDescriptionDefault
snapshotIdYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It explains that system-created backups cannot be deleted, locked snapshots require prior unlocking, and only user-created snapshots are deletable. It does not mention whether deletion is irreversible or what response to expect, but the core behavioral constraints are well covered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences long. The first sentence clearly states the purpose, and the second adds vital constraints. Every sentence earns its place, and the information is front-loaded with no extraneous content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one parameter, no output schema), the description provides sufficient context for the core operation and constraints. However, it could be more complete by mentioning what happens after deletion (e.g., success response) or that the snapshotId must belong to a file, but these are minor omissions for a basic delete tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage for the single parameter is 0% (no descriptions in schema). The description does not add any meaning to snapshotId beyond what the schema provides (a required string). It fails to explain the format, source, or how to obtain the snapshotId, leaving the agent without guidance on constructing the argument.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Delete a user-created snapshot.' It specifies the condition for deletable snapshots (createdBy: 'user') and distinguishes from system backups, which cannot be manually deleted. This differentiates it from sibling tools like penpot_create_file_snapshot, penpot_restore_file_snapshot, and penpot_lock_file_snapshot.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool (only for user-created snapshots) and when not to (locked snapshots or system backups). It also provides an explicit alternative: if a snapshot is locked, call penpot_unlock_file_snapshot first. This gives clear usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_delete_pageB

Delete a page from a Penpot file. The file must have at least two pages; deleting the last page is not allowed.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
pageIdYes

TDQS

B3.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Without annotations, the description carries the full burden of disclosing behavioral traits. It mentions the page count constraint but does not describe side effects (e.g., deletion of contained shapes), authorization requirements, reversibility, or error conditions beyond the last-page restriction. The disclosure is minimal.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two succinct sentences: one for the action, one for a critical constraint. No redundant information or extraneous detail. Every word serves a purpose, making it highly efficient for an AI to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with two parameters and no output schema, the description covers the essential purpose and a key constraint. However, it omits context such as the destructive nature of deletion, potential cascading effects, or how to recover (e.g., via snapshots). An agent has enough to perform basic invocation but lacks completeness for robust decision-making.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema defines fileId and pageId with no descriptions; the description adds no further meaning to these parameters. An agent could infer their role from names, but the description misses an opportunity to clarify how to obtain these IDs (e.g., from list results) or their expected format.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'Delete a page from a Penpot file', specifying the verb and resource, and distinguishes this from sibling tools that delete other entities (files, projects, shapes, etc.). The constraint about file page count adds clarity without confusion.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The constraint that the file must have at least two pages gives a clear when-not-to-use condition. However, no explicit guidance is provided on when to choose this tool over other delete tools (e.g., penpot_delete_shapes) or what prerequisites exist (e.g., permissions). Usage context is implied but not comprehensive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_delete_projectC

Delete a Penpot project.

ParametersJSON Schema
NameRequiredDescriptionDefault
projectIdYes

TDQS

C2.1/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden but only states the action. It does not disclose whether deletion is irreversible, what happens to associated files, or any other behavioral traits beyond the obvious 'delete' operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very short (one sentence) but under-specified. It lacks essential context, making it insufficient rather than concise. It does not earn its place because it omits important usage and behavioral information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the low complexity (1 parameter, no output schema, no annotations), the description is grossly incomplete. It fails to explain what happens on deletion, the return value, or any prerequisites, leaving the agent without enough information to use the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description makes no mention of the single parameter (projectId) at all. The description adds no meaning beyond what the schema provides, failing to compensate for the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (delete) and the resource (Penpot project), which is specific and distinct from sibling tools like create, rename, or list. No ambiguity or tautology.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives, no prerequisites, no context about when deletion is appropriate, and no mention of side effects or dependencies.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_delete_shapesA

Delete one or more existing shapes from a page in a Penpot file, by id. Deleting a frame or group also removes its children, matching Penpot's own delete behavior. This is the only way to remove a shape — there is no undo via this tool once the change is sent.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
pageIdYes
shapeIdsYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description successfully discloses key behaviors: shape removal, cascading delete for frames/groups, and irreversibility. This goes beyond what the input schema conveys.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences with no fluff. Critical details (cascading delete, no undo) are included without extra verbiage.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a delete tool with three parameters and no output schema, the description covers the primary behavior and important caveats. It is slightly lacking in parameter specifics but otherwise complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, yet the description adds minimal parameter info beyond 'by id'. It does not explain fileId, pageId, or shapeIds beyond the schema, so the description fails to compensate for the low coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (delete), the resource (shapes from a page in a Penpot file), and method (by id). It also distinguishes from siblings by specifying it is the only deletion tool, contrasting with clone, find, etc.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when to use (to remove shapes) and warns about no undo and cascading deletion of children. It does not explicitly list when not to use, but the context is clear enough.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_discard_checkpointA

Free a checkpoint taken via penpot_checkpoint without restoring it, once it is no longer needed. Removes both the in-memory entry and, when disk persistence is enabled (PENPOT_CHECKPOINTS_PATH), the corresponding file on disk.

ParametersJSON Schema
NameRequiredDescriptionDefault
checkpointIdYes

TDQS

A3.6/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations exist, so the description carries full burden. It discloses that the tool removes in-memory entry and optionally a file on disk based on configuration, but does not mention permanence, authorization, or side effects like irreversibility.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no redundancy. First sentence states the core action and condition, second adds implementation details. Every word serves a purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple discard tool, the description covers the action and data removal locations. However, it lacks error handling, return value information (no output schema), and fails to clarify if checkpointId is a required path or ID. Could be more complete given no annotations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 0% description coverage for the single parameter checkpointId. The description only says it references a checkpoint from penpot_checkpoint, failing to specify format (e.g., UUID), how to obtain it, or validation rules beyond minLength 1.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool discards (frees) a checkpoint without restoring it, and contrasts with penpot_checkpoint and penpot_restore_checkpoint. It specifies the action on both in-memory and disk entries, making the purpose unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description says 'once it is no longer needed,' implying when to use, but does not explicitly list when not to use or compare with alternatives like penpot_restore_checkpoint. No guidance on prerequisites or context is provided.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_distribute_shapesA

Distribute three or more shapes so the gaps between adjacent shapes are equal, matching Penpot's own "distribute horizontal/vertical spacing" actions — instead of the caller computing even spacing itself. "axis" is "horizontal" (equalize left-to-right gaps) or "vertical" (top-to-bottom). The two outermost shapes stay put and the ones between them slide so every gap is identical; distributes on each shape's visible bounding box (selrect), accounting for differing shape sizes. Applied as a single update-file change-set; shapes already evenly spaced (and the two endpoints) are left untouched.

ParametersJSON Schema
NameRequiredDescriptionDefault
axisYes
fileIdYes
pageIdYes
shapeIdsYes
tokensPathNo/app/tokens.json

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavior: outer shapes stay put, inner shapes slide, uses selrect bounding box, applies as single change-set, and leaves already evenly spaced shapes untouched. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four sentences clearly convey purpose and behavior. Minor structure improvement possible by explicitly listing parameters, but overall concise and well-organized.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description thoroughly explains behavior and key parameters. The only gap is the missing tokensPath parameter, but the core functionality is well-covered.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description explains axis and implicitly covers fileId, pageId, shapeIds. However, the tokensPath parameter is not mentioned at all, and schema description coverage is 0%, leaving one parameter undocumented.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool distributes three or more shapes to equalize gaps, matching Penpot's own distribute actions. It distinguishes from siblings like penpot_align_shapes by specifying the exact behavior of spacing equalization.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when to use the tool (to distribute shapes with equal gaps) and contrasts with caller-computed spacing. It does not explicitly mention alternatives or when not to use, but the sibling list provides context for differentiation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_find_shapesA

Search a page for shapes matching one or more predicates, instead of walking the tree returned by penpot_get_file_snapshot by hand. Combine "type", "name" (exact match), "nameContains" (case-insensitive substring), "textContains" (case-insensitive substring against text shapes' rendered characters), "isComponentInstance", and/or "isRoot" — all given filters must match (AND). Omit every filter to list every shape on the page. Returns each match's id, type, name, position/size, and component link state ("linkState": "not-an-instance" | "linked" | "detached" | "main-component-root"), without descendants (use penpot_get_shape on a match's id for its full subtree including detailed componentInfo with driftedFields).

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNo
typeNo
limitNo
fileIdYes
isRootNo
pageIdYes
nameContainsNo
textContainsNo
isComponentInstanceNo

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description details return fields (id, type, name, position/size, linkState), filter combination logic (AND), and the subtree caveat. Does not cover performance or auth, but sufficient for a read-only search.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads purpose and usage, then details parameters and returns. Slightly verbose in enumerating linkState values, but all sentences add value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 9 parameters, no output schema, and no annotations, the description covers input filters and output structure well. Missing details on limit behavior and error conditions, but adequate for a search tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description explains all filter parameters (type, name, nameContains, textContains, isComponentInstance, isRoot) and their semantics. Does not explain fileId, pageId, limit, but those are understandable from context or schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the verb 'Search' and resource 'shapes on a page', distinguishes from the sibling penpot_get_file_snapshot by positioning as an alternative to walking the tree.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly describes when to use (to find shapes by predicates instead of tree walking) and provides an alternative penpot_get_shape for full subtree. Lists all filters and explains omitting filters lists all shapes.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_get_commentsA

Get all reply comments within a comment thread. Returns each comment's id, content, author, and timestamps.

ParametersJSON Schema
NameRequiredDescriptionDefault
threadIdYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Describes return data (id, content, author, timestamps), which is behavioral. No annotations provided, so description carries burden. Does not disclose authentication or rate limits, but for a read-only operation, this is acceptable.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences: first states purpose, second lists return fields. No fluff, front-loaded. Efficient and clear.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read tool with one parameter and no output schema, description covers operation and return data. Lacks details on pagination or ordering, but likely not needed for listing thread replies. Sufficient for low complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has one parameter (threadId) with no description (0% coverage). Description mentions 'within a comment thread', linking to the parameter. Adds context about the thread context but doesn't explain format or requirement. Compensates for schema lack.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the action 'Get all reply comments' and the resource 'comment thread'. Distinguishes from sibling tools like penpot_list_comment_threads (lists threads) and penpot_create_comment (creates a comment).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Implies usage context ('within a comment thread') but does not explicitly state when to use this tool vs alternatives (e.g., when you have a thread ID from list_comment_threads). No exclusion or alternative mention.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_get_file_snapshotA

Read a Penpot file, including its pages, shape tree, and current revn/vern (needed before any content mutation).

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses read behavior and version info, but lacks details on permissions, rate limits, or side effects. Adequate but not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence, no wasted words. Information is front-loaded and efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool is simple with one parameter and no output schema. Description covers purpose, usage, and content. Lacks response structure but acceptable given context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has one required parameter 'fileId' with 0% description coverage. The description does not elaborate on the parameter beyond the schema, failing to add meaning (e.g., format, source).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states 'Read a Penpot file' and specifies contents (pages, shape tree, revn/vern), distinguishing it from sibling tools like penpot_add_shapes or penpot_delete_file. The verb+resource is specific and informative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'needed before any content mutation,' providing clear when-to-use guidance. No explicit when-not-to-use or alternatives, but sibling list provides context for other operations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_get_file_snapshot_dataA

Retrieve the full file content (pages, shape tree) as it existed at a specific snapshot — for read-only inspection or comparison, without modifying the live file. Returns the same structure as penpot_get_file_snapshot but sourced from the historical snapshot data. Use penpot_restore_file_snapshot to actually roll back.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
snapshotIdYes

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It discloses that the tool is read-only and does not modify the live file, which is the key behavioral trait. It also notes the return structure matches another snapshot tool. However, it does not mention potential limitations like pagination or size constraints, but given the read-only nature, the disclosure is adequate.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise, consisting of two sentences. The first sentence states the main action and context, and the second adds a comparison and usage tip. Every sentence adds value without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given two simple parameters and no output schema, the description covers the main aspects: what is returned (full file content, pages, shape tree), that it is read-only, and its relationship to sibling tools. It is complete enough for a read-only snapshot retrieval tool, though missing details on snapshotId format are minor.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. While the description implicitly references the parameters (fileId and snapshotId) by describing the action 'retrieve ... at a specific snapshot', it does not explicitly explain their meaning, format, or how to obtain them. This adds minimal value beyond parameter names.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool retrieves full file content (pages, shape tree) at a specific snapshot for read-only inspection. It distinguishes itself from sibling tools like penpot_get_file_snapshot (same structure but from historical data) and penpot_restore_file_snapshot (for rollback), making the purpose specific and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool: 'for read-only inspection or comparison, without modifying the live file.' It also specifies when not to use it by pointing to penpot_restore_file_snapshot for actual rollback, providing clear usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_get_shapeA

Look up a single shape by id on a page, without pulling the whole page via penpot_get_file_snapshot. By default, nests the shape's full descendant subtree (frames/groups' children) under its "shapes" field instead of leaving them as bare ids; set includeDescendants to false for just the shape itself, or maxDepth to cap how many levels deep the nesting goes. Always includes a "componentInfo" field reporting the shape's component link state: "not-an-instance" (plain shape), "main-component-root" (the component's own main instance), "linked" (a placed copy linked to a component — also includes driftedFields listing any field names whose value on this instance differs from the main component's current definition), or "detached" (componentId present but the component no longer exists in this file).

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
pageIdYes
shapeIdYes
maxDepthNo
includeDescendantsNo

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must fully disclose behavior. It explains the nesting behavior (descendants under 'shapes' field) and the componentInfo field with its four states. It does not mention error conditions, authentication, or rate limits, but as a read-only lookup, the covered aspects are sufficient for typical use.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, well-structured paragraph. It starts with the main purpose, then explains the key behavioral detail (nesting), and concludes with the componentInfo logic. Every sentence adds value, and there is no redundant or extraneous text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With 5 parameters, no output schema, and no annotations, the description covers the core functionality well: it explains what the tool returns (shapes and componentInfo) and how parameters affect the output. It does not describe error responses or full shape structure, but for a lookup tool, the coverage is adequate for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It explains the purpose of includeDescendants and maxDepth in detail, and implies the required parameters (fileId, pageId, shapeId) through context. However, it does not explicitly describe the required parameters, which are self-explanatory but could be named. Overall, the description adds significant value beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description explicitly states the tool's purpose: 'Look up a single shape by id on a page'. It distinguishes itself from a sibling tool (penpot_get_file_snapshot) by specifying 'without pulling the whole page'. The verb and resource are clear and specific.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear guidance on when to use this tool (for a single shape lookup) and mentions an alternative (penpot_get_file_snapshot). It also explains the behavior of parameters like includeDescendants and maxDepth, helping users decide how to configure the call. However, it does not explicitly state when not to use it or list exclusions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_group_shapesA

Group one or more existing sibling shapes into a new group, matching Penpot's own Ctrl+G behavior. All supplied shapeIds must share the same parent on the page. The new group is inserted into the parent at the z-order position of the topmost selected shape; the grouped shapes become the group's children and keep their existing absolute canvas positions. Returns the new groupId and the parent's updated shapes order. Use penpot_ungroup_shapes to reverse this operation.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoGroup
fileIdYes
pageIdYes
groupIdNo
shapeIdsYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description bears full burden. Explains z-order behavior (inserted at topmost shape's position), that shapes keep absolute positions, and return value. Could mention optional groupId parameter but it's not critical.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four sentences, front-loaded with purpose, no fluff. Each sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Describes constraints, return values, and reverse operation. No output schema exists, so return description is adequate. Could note if groupId is optional (parameter existence) but not required.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so description must compensate. It explains shapeIds must be siblings and that groupId is returned, but does not describe fileId, pageId, or name parameters individually. Adds partial meaning beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the tool groups sibling shapes, matching Penpot's Ctrl+G behavior. Distinguishes from penpot_ungroup_shapes by naming the reverse operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly indicates when to use (group shapes) and when to use the reverse tool (ungroup). Mentions constraint that shapes must share the same parent. Does not exhaustively list alternatives but provides sufficient guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_list_comment_threadsA

List all comment threads in a Penpot file. Returns each thread's id, position, content (the opening message), resolution status, and participant list.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes

TDQS

A3.8/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided. Describes read-only retrieval and return fields, but omits pagination, ordering, or error behavior. Adequate but gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences: purpose then return details. No redundant words. Front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers purpose, resource, and return structure. Missing pagination and error handling, but acceptable for a simple list tool with one param.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 1 param fileId with 0% description coverage. Description mentions 'in a Penpot file' but doesn't further describe fileId beyond the name. Partially compensates but could add format or source info.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states verb 'list' and resource 'comment threads in a Penpot file'. Distinct from siblings like create, resolve, delete comment threads. Specifies exact return fields.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Implies usage for listing threads but no explicit when-to-use or when-not-to-use. No comparison with siblings like get_comments.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_list_componentsA

List a file's existing components (from its components map), instead of requiring the caller to have created them itself in the same session or parse penpot_get_file_snapshot's data.components by hand. Each entry includes the componentId (usable with penpot_add_component_instance), name, mainInstanceId/mainInstancePage, and — for variant components — variantId/variantProperties. Set includeLibraries: true to also include components from connected shared-library files (each library component entry will carry a libraryFileId field for use with penpot_add_component_instance).

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
includeLibrariesNoWhen true, also list components from all shared-library files that are linked to this file. Each library component entry will include a libraryFileId and libraryFileName field. Pass the libraryFileId to penpot_add_component_instance to place an instance of that component.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Without annotations, the description carries the full burden. It details the returned fields (componentId, name, mainInstanceId/mainInstancePage, variantId/variantProperties) and the effect of includeLibraries (adds libraryFileId). However, it does not mention error handling, performance, or read-only nature explicitly, though it is implied.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single paragraph but highly efficient. It front-loads the core purpose, uses bullet points within text for clarity, and every sentence adds essential information without redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema or annotations, the description provides a solid understanding of input/output and integration with sibling tools. However, it lacks information on error conditions, prerequisites (e.g., file existence), and pagination limits. Overall, it is complete enough for an AI agent, with minor gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has 2 parameters with 50% description coverage. The description adds value by explaining fileId's role (identifies the file) and expanding on includeLibraries: it specifies the output includes libraryFileId and libraryFileName, and how to use them with penpot_add_component_instance. This goes beyond the minimal schema description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states that the tool lists a file's existing components, explicitly contrasting it with alternative approaches like creating components manually or parsing snapshot data. It also distinguishes from sibling tools like penpot_get_file_snapshot by emphasizing the ease of use.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool: 'instead of requiring the caller to have created them itself in the same session or parse penpot_get_file_snapshot's data.components by hand.' It also provides guidance on the includeLibraries parameter, explaining when to set it true and the resulting behavior.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_list_filesB

List all files within a Penpot project.

ParametersJSON Schema
NameRequiredDescriptionDefault
projectIdYes

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description implies a read-only operation by stating 'List', but lacks explicit disclosure of side effects, rate limits, or pagination. Without annotations, it adequately conveys non-destructive behavior but could add safety details.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single clear sentence with no unnecessary words. However, it is borderline under-specified by omitting parameter details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple list tool, the description covers the basic purpose, but omits information about the return format, pagination, or any limitations. Given the lack of output schema, additional details would improve completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% and the description does not explain the sole parameter projectId, such as its format or how to obtain it. The description fails to compensate for missing schema documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb 'List' and resource 'files' with scope 'within a Penpot project', clearly distinguishing it from sibling tools like create, delete, or rename files.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool versus alternatives, such as penpot_create_file or penpot_delete_file. The description simply states the action without context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_list_file_snapshotsA

List all named snapshots (version history) for a Penpot file. Returns each snapshot's id, label, revn, createdAt, createdBy, and lock state. createdBy: "user" entries are snapshots saved explicitly (from the UI or via penpot_create_file_snapshot); createdBy: "system" entries are automatic backups Penpot creates before a restore — they expire automatically. Use the returned id with penpot_restore_file_snapshot to roll back to a version.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description discloses key behaviors: types of snapshots (user vs system), expiration of system backups, and the return fields (id, label, revn, createdAt, createdBy, lock state). This adds value beyond what is in the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is three sentences with clear structure: purpose, return details, and usage tip. It is concise without unnecessary words, though it could be slightly more compact.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple list tool with one parameter and no output schema, the description covers the essential information—return fields, distinction of snapshot types, and how to use the result. It is complete for its complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter, fileId, has 0% schema description coverage. The description does not explicitly describe the parameter; it only implies its necessity ('for a Penpot file'). It adds minimal meaning beyond the schema's type and requirement.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states 'List all named snapshots (version history) for a Penpot file.' It specifies the resource (file snapshots) and the action (list), distinguishing it from sibling tools like penpot_create_file_snapshot and penpot_restore_file_snapshot.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when to use the tool: to view snapshots for version history or rollback. It differentiates between explicit and system snapshots and directs the user to use the returned id with penpot_restore_file_snapshot. It does not explicitly state when not to use it, but the context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_list_pagesA

List all pages in a Penpot file, returning each page's id and name in order.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes

TDQS

A4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Without annotations, the description carries full burden for behavioral disclosure. It clearly indicates a read operation (listing pages) and specifies output fields and ordering. However, it does not explicitly state non-destructiveness or side effects, though none are implied.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single sentence of 19 words, front-loaded with the key information ('List all pages') and additional details on return values and ordering. No redundant information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple list tool with one parameter and no output schema, the description covers purpose, returned fields, and ordering. It could specify the ordering criteria (e.g., creation order) but is otherwise complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description adds minimal meaning beyond the parameter name and type. It mentions 'in a Penpot file' but does not clarify how to obtain the fileId or expected format. The single parameter is simple, so the gap is acceptable but not fully compensated.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool lists all pages in a Penpot file and returns each page's id and name in order. This specific verb-resource combination distinguishes it from sibling tools like penpot_create_page or penpot_delete_page.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for retrieving page listings but does not provide when-to-use guidance or mention alternatives. No explicit context for selecting this tool over others is given.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_list_projectsB

List all projects within a Penpot team.

ParametersJSON Schema
NameRequiredDescriptionDefault
teamIdYes

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, and description only says 'list all projects', implying a read operation but lacks details on return format, pagination, or side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

One sentence, no redundant information. Efficient and to the point.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Missing details on output schema (no response description), auth requirements, or error handling. Incomplete given the lack of annotations and output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and description does not explain the teamId parameter beyond what the schema provides (string, minLength 1). No additional semantics.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states action (list) and resource (projects within a Penpot team). It distinguishes from sibling tools like list_files and list_teams.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives, no prerequisites, no exclusions. Only states the basic function.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_list_teamsA

List all Penpot teams accessible to the configured access token.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are present, so the description carries full burden. It does not disclose pagination, authentication specifics, or error handling. However, the tool is simple and the description is truthful.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence, no fluff, clearly stated. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given zero parameters and no output schema, the description is mostly complete. It could mention that teams are the root organizational unit, but the agent can infer this from context.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are no parameters, so baseline is 4. The description adds no parameter information but none is needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses specific verb 'List' and resource 'Penpot teams', with clear scope 'accessible to the configured access token'. It distinguishes from sibling tools like penpot_list_files and penpot_list_projects.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit when-to-use or when-not-to-use guidance is provided. Usage is implied as teams are a top-level entity, but alternatives (e.g., list projects after selecting a team) are not mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_load_token_configB

Read and validate the project design-token file, returning the resolved colors/fonts/spacing/radii/shadows tables.

ParametersJSON Schema
NameRequiredDescriptionDefault
tokensPathNo/app/tokens.json

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Without annotations, the description implies a read-only operation ('read and validate'), but does not disclose error behavior if the file is missing or invalid, nor any state changes. Adequately describes the main behavior but lacks detail on side-effects and auth requirements.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single sentence is concise and front-loaded with the action ('Read and validate') and output summary. Could benefit from brief structure for clarity, but it's effective.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read tool with one optional parameter and no output schema, the description covers the core functionality and return content. Lacks error handling details but is sufficient given the tool's simplicity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description only reiterates the parameter name (tokensPath) without adding meaning (e.g., format restrictions, resolution order). The default value is provided but not explained.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it reads and validates a design-token file and returns resolved tables for colors, fonts, spacing, radii, shadows. Differentiates from sibling tools by specifying the exact resource (token config file) and the resolved output.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives (e.g., other read tools like penpot_get_file_snapshot, penpot_get_shape). No mention of prerequisites or conditions under which it should be preferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_lock_file_snapshotA

Lock a user-created snapshot to prevent accidental deletion. A locked snapshot cannot be deleted until it is unlocked via penpot_unlock_file_snapshot. Only the snapshot's creator can lock it, and only user-created snapshots can be locked.

ParametersJSON Schema
NameRequiredDescriptionDefault
snapshotIdYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully covers behavioral traits: locking prevents deletion, creator-only constraint, user-created snapshots only, and need to unlock to delete. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two efficient sentences with no wasted words. The main action is front-loaded, and restrictions are clearly stated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one param, no output schema), the description is complete. It covers purpose, restrictions, and counter-action (unlock). No gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The single parameter snapshotId is implied by context but not explicitly described in the description. Schema coverage is 0%, so description should add more detail. However, the tool's purpose makes the parameter clear, so adequacy is borderline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool locks a user-created snapshot to prevent deletion. It specifies the scope (only user-created, only by creator) and distinguishes from siblings like unlock and delete.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use (to prevent deletion of user-created snapshots) and restrictions (only creator can lock, only user-created snapshots). References the unlock sibling tool, providing clear guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_measure_textA

Measure the real rendered width/height of a text string for a given font, without creating or touching any shape. Tries Google Fonts first (by family name, no API key needed); if the family is not on Google Fonts, searches all Penpot teams accessible to the configured token for a matching custom/team font. Computes glyph advance widths so the numbers match what Penpot would actually render — removing the guesswork around width/height when calling penpot_add_shapes or penpot_update_shapes for a text shape. Splits on explicit "\n" for multi-line text; pass maxWidth to also get word-wrapped line breaks for a fixed-width box. Returns { width, height, lineHeight, lines }, where width/height are the natural (or wrapped) bounding box and lines is a per-line breakdown of text/width.

ParametersJSON Schema
NameRequiredDescriptionDefault
fontSizeNo
maxWidthNo
charactersYes
fontFamilyNoInter
fontWeightNo400

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden. It discloses font lookup behavior, glyph advance computation, and handling of newlines and maxWidth. It does not mention auth needs or rate limits, but covers core behavior well.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is comprehensive at ~7 sentences, front-loading the main purpose. While slightly verbose, every sentence adds value and it is well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no output schema, the description fully specifies return values (width, height, lineHeight, lines) and explains per-line breakdown. It covers multi-line and word-wrapping behavior, making it complete for the tool's purpose.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so description must explain parameters. It explains characters, maxWidth, fontFamily, fontSize, fontWeight semantics, including that fontFamily is used for font lookup. It does not repeat defaults from schema but adds context beyond parameter names.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states it measures real rendered width/height of a text string for a given font without creating or touching any shape. It distinguishes itself from siblings like penpot_add_shapes and penpot_update_shapes by removing guesswork for text dimensions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when to use it (before creating/updating text shapes) and how it handles font lookup (Google Fonts first, then team fonts). It also discusses multi-line and maxWidth parameter usage. However, it lacks explicit 'when not to use' guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_rename_fileC

Rename an existing Penpot file.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
fileIdYes

TDQS

C2.8/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Description does not disclose side effects, permissions, or implications of renaming (e.g., URL changes, impact on collaborators), which is insufficient given no annotations are provided.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is concise with one sentence, no wasted words. However, it could be slightly more informative without harming conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple rename tool with no output schema and no parameter descriptions, the description lacks completeness; it does not specify return values or error conditions, leaving contextual gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description does not explain the parameters (fileId, name) beyond their types and constraints, failing to add meaning for the agent.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the specific action (rename) and resource (existing Penpot file), effectively distinguishing from sibling tools like create, delete, list.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives; lacks explicit when-to-use or when-not-to-use information, leaving the agent to infer from context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_rename_file_snapshotA

Rename an existing user-created snapshot. Only snapshots with createdBy: "user" can be renamed; system-created automatic backups will return an error.

ParametersJSON Schema
NameRequiredDescriptionDefault
labelYes
snapshotIdYes

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Lacking annotations, the description carries full burden. It discloses the critical constraint (user-created only) but omits other behavioral details like return value, confirmation of success, or idempotency. Adequate for a simple rename operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no redundant information. The action and constraint are front-loaded, achieving maximum clarity with minimal words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple rename tool with two required parameters and no output schema, the description covers the essential constraint. Minor gap: could mention that 'label' is the new name, but schema names imply it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, yet description adds no parameter details beyond the names 'snapshotId' and 'label'. It does not explain the label is the new name or any constraints like max length or uniqueness. Fails to compensate for missing schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Rename') and resource ('existing user-created snapshot'), and distinguishes from sibling tools like penpot_create_file_snapshot and penpot_lock_file_snapshot by specifying the constraint that only user-created snapshots can be renamed.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Description explicitly states when to use (only for snapshots with createdBy 'user') and when not to (system-created automatic backups will error), providing clear usage boundaries. No alternative tools mentioned, but the constraint is sufficient.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_rename_pageB

Rename an existing page in a Penpot file.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
fileIdYes
pageIdYes

TDQS

B3.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It states the function but does not disclose side effects, idempotency, or error behavior. Acceptable for a simple rename but lacks depth.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Single concise sentence with no unnecessary words. Front-loaded and clear.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Minimal context provided. With no output schema and no annotations, the description omits return values, error cases, and any additional behavioral details. However, for a simple rename, it is barely adequate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, meaning no parameter descriptions. The description does not explain the parameters (fileId, pageId, name) or their meaning, leaving the agent with only the schema names.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states the verb (rename) and resource (existing page in a Penpot file). Differentiates from sibling rename tools (rename_file, rename_project) by specifying 'page'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool vs alternatives. Does not mention any prerequisites or when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_rename_projectC

Rename an existing Penpot project.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
projectIdYes

TDQS

C2.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided. The description only states 'rename' which implies a mutation, but does not disclose any side effects, permissions required, or whether the operation is reversible. Lacks behavioral detail.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very brief (one sentence), which is concise but under-specified. It is not wasteful but lacks essential details that could be added without exceeding typical length.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simplicity (2 required params, no enums, no output schema), the description could still be more complete. It does not mention return value, success/failure conditions, or any constraints like name uniqueness. Incomplete for an agent to use confidently.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has two parameters with only minLength constraints and no descriptions. Schema description coverage is 0%. The tool description does not add any meaning beyond the parameter names, so the agent has no guidance on what valid inputs are.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action 'Rename' and the target resource 'existing Penpot project'. It distinguishes from sibling tools like penpot_rename_file and penpot_create_project. However, it does not specify the scope or any unique aspects.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance on when to use this tool versus alternatives like penpot_create_project or penpot_rename_file. No prerequisites or context for invoking the rename operation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_reorder_shapesA

Change a shape's stacking (z-)order among its siblings, matching Penpot's own "Bring to front" / "Send to back" / "Forward" / "Backward" UI actions. Shapes have no explicit z-index — order is implicit in their parent's child list, where later entries render on top. "front"/"back" move the shape to the top/bottom of the stack; "forward"/"backward" swap it with the next/previous sibling (a no-op if already at that end); "before"/"after" place it immediately before/after another sibling given as targetId. Only reorders among existing siblings — does not reparent (use penpot_update_shapes's parentId/frameId for that).

ParametersJSON Schema
NameRequiredDescriptionDefault
actionYes
fileIdYes
pageIdYes
shapeIdYes
targetIdNo

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It thoroughly describes behavioral traits: implicit z-order, no reparenting, actions and their effects, and no-op conditions. Covers edge cases.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is information-dense and well-structured, front-loading purpose then detailing actions. Every sentence adds value; no wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given complexity and no output schema, description covers action semantics, constraints, and key differentiators. Lacks mention of error conditions or prerequisites, but is otherwise complete for a reorder tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but description adds significant meaning: explains action enum values and their semantics, mentions targetId for before/after. FileId, pageId, shapeId are standard context from sibling tools.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool changes stacking order among siblings, matching Penpot UI actions. Distinguishes from sibling tools like penpot_update_shapes by explicitly noting that reparenting is not done here.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly explains when to use each action (front, back, forward, backward, before, after), including no-op conditions for forward/backward at ends. Provides alternative for reparenting (penpot_update_shapes).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_replace_textA

Find and replace text across all text shapes on a page in a single update-file call. Searches each text run (leaf node) of every text shape for the literal "search" string and replaces every occurrence with "replacement". Matching is case-insensitive by default (set caseSensitive: true to override). Replacement is per text-run: a search string that spans two adjacent runs in the same paragraph will not be matched. Returns the ids and names of every shape that was modified and the total number of occurrences replaced.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
fileIdYes
pageIdYes
searchYes
replacementYes
caseSensitiveNo

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses key behavioral traits: case-insensitive by default, per-text-run matching (limitation), returns modified shape ids and count, and atomicity via a single call. This is thorough and honest.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the main action and has no redundant sentences. Every sentence adds value: purpose, matching details, case sensitivity, edge case (adjacent runs), and return value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description explicates the return value (shape ids/names and occurrence count) and warns about edge cases. It is complete for a text replacement tool with no nested objects or complex output.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%. The description explains search, replacement, and caseSensitive (with default) but does not explain fileId, pageId, or limit. While fileId and pageId are common, the omission of limit is a gap. It partially compensates but not fully.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool does find and replace text across all text shapes on a page. This is a specific verb-resource combination that distinguishes it from siblings like penpot_measure_text or penpot_find_shapes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for bulk text replacement on a page, but does not explicitly state when not to use it or provide alternatives. The context of a single update-file call is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_resolve_comment_threadA

Mark a comment thread as resolved (isResolved: true) or reopen it (isResolved: false). Resolved threads are hidden by default in Penpot's UI but remain accessible.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYes
isResolvedYes

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It explains the toggle behavior and UI implication (hidden vs. accessible), but lacks details on permissions, reversibility, or error cases. The disclosure is adequate for a simple operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, no fluff. The key action and Boolean usage are front-loaded. Every word adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (2 params, no output schema), the description covers the essential behavior and UI consequence. It could mention error handling or required permissions for completeness, but this is not critical for a toggle operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It explains the `isResolved` parameter with examples (`true` for resolved, `false` for reopen). The `id` parameter is implied as thread ID, but not explicitly documented.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the verb 'Mark...as resolved or reopen it' and specifies the resource 'comment thread'. It distinguishes this tool from siblings like create, delete, and list by focusing on resolution toggling.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states when to use this tool (to resolve or reopen a thread) and provides context about UI behavior. It does not specify when not to use it or direct alternatives, but the sibling set covers other actions.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_restore_checkpointA

Undo every shape change made since a penpot_checkpoint call, by diffing the page's current state against the snapshot and replaying corrective changes as a single update-file call: shapes the snapshot has but the page no longer does are recreated verbatim, shapes the page has that the snapshot didn't are deleted, and shapes present in both are overwritten back to their snapshotted fields (geometry, fills, children, layout — everything). When the checkpoint was taken without a pageId (whole-file checkpoint), all snapshotted pages are restored in a single update-file call. The checkpoint itself is NOT consumed — it can be restored to again, or discarded explicitly via penpot_discard_checkpoint. Throws if the checkpoint id is unknown (already discarded, or the server restarted since it was taken).

ParametersJSON Schema
NameRequiredDescriptionDefault
checkpointIdYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Without annotations, description fully discloses behavior: checkpoint not consumed, can be restored repeatedly, throws on unknown id, details corrective actions for shapes. Very transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with main purpose first, then mechanism, then edge cases. Slightly long but every sentence adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers both page-level and file-level checkpoints, mentions related tools, explains error condition. Complete for a tool with no output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Single parameter checkpointId has no schema description, but description adds meaning: it is a valid checkpoint id, and explains consequences of unknown id. Compensates for 0% schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool undoes shape changes since a checkpoint, with specific verb 'undo' and resource 'shape changes', and distinguishes it from siblings like penpot_discard_checkpoint and penpot_checkpoint.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides context for when to use (undo shape changes) and mentions alternatives (discard checkpoint) and prerequisites (checkpoint must exist). Lacks explicit when-not-to-use but overall clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_restore_file_snapshotA

Restore a Penpot file to the state captured in a named snapshot (equivalent to "Restore version" in the Penpot UI). Penpot automatically creates a system backup snapshot of the current file state before applying the restore, so you can undo a restore by listing snapshots again and restoring the most recent system entry. Requires the fileId and the snapshotId returned by penpot_list_file_snapshots. The restore replaces the file's live data — all agents and browser sessions editing the file will see the updated content.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
snapshotIdYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, description carries full burden. It discloses that restore replaces live data, affects all agents and browser sessions, and that an automatic backup snapshot is created. This gives full transparency about side effects and recoverability.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences: first states core action, second explains safety undo mechanism, third lists prerequisites. No filler, front-loaded with purpose, each sentence adds essential information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema, but description does not mention return value or success/failure indicators. However, for a mutation tool with clear side effects and undo capability, it covers the key aspects except output. Slightly incomplete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so description must add meaning. It explains that fileId and snapshotId come from penpot_list_file_snapshots, but does not elaborate on their format or constraints beyond the schema's minLength. Provides context but limited parameter-specific detail.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool restores a file to a snapshot state, using verb 'Restore' and specifying the resource (file via snapshot). It distinguishes from siblings like penpot_create_file_snapshot and penpot_get_file_snapshot by referencing the UI equivalent and linking to penpot_list_file_snapshots for obtaining IDs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states prerequisites: requires fileId and snapshotId from penpot_list_file_snapshots. Also notes that a system backup snapshot is created automatically, allowing undo. Does not specify when not to use, but purpose is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_ungroup_shapesA

Dissolve a group, returning its children to the group's parent at the group's z-order position — matching Penpot's own Ctrl+Shift+G / "Ungroup" behavior. The supplied groupId must refer to a shape of type "group". After ungrouping, each former child keeps its absolute canvas position and gets the group's parent and frame as its new parent. The group shape itself is deleted. Returns the ids of the released children and the parent's updated shapes order.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
pageIdYes
groupIdYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It details that children keep absolute position, get new parent/frame, group is deleted, and returns ids and parent's updated order. Highly transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three clear sentences, front-loaded with action, no extraneous words. Efficiently conveys behavior and outcome.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema or annotations, yet description covers the core behavior, return value, and post-conditions. Minor gaps: no error cases (e.g., if groupId is not a group) or exact return format.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so description must compensate. It explains groupId's required type but does not clarify fileId and pageId beyond their names. Partial compensation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states the tool dissolves a group, returning children to the parent at the group's z-order. It explicitly matches Penpot's 'Ungroup' behavior and distinguishes from grouping operations via sibling names.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States that groupId must refer to a shape of type 'group', implying when to use. Does not explicitly contrast with alternatives, but the context of ungrouping is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_unlock_file_snapshotA

Unlock a previously locked user-created snapshot, allowing it to be deleted again. Only the snapshot's creator can unlock it.

ParametersJSON Schema
NameRequiredDescriptionDefault
snapshotIdYes

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses the effect (unlocking allows deletion), the precondition (must be locked), and the authorization (creator-only). This provides sufficient transparency for a simple unlock operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short, clear sentences with no redundant information. Every word serves a purpose: verb, resource, effect, and constraint.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter tool with no output schema, the description covers the purpose, effect, and authorization. It lacks details on return values or error scenarios, but these are likely standard for similar operations. Overall adequate for the tool's simplicity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter `snapshotId` has no description in the schema (0% coverage) and the tool description does not elaborate on its meaning beyond the name. The agent would benefit from knowing it is the ID of the snapshot to unlock, but the description does not add any value over the field name.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description specifies the exact action (unlock), resource (previously locked user-created snapshot), and a key constraint (only creator can unlock). It clearly differentiates from sibling tools like lock_file_snapshot and delete_file_snapshot.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description states that unlocking allows the snapshot to be deleted, implying usage before a delete operation. It also identifies the authorization condition (creator-only). However, it does not explicitly mention when not to use or compare with alternatives, though the sibling tool list provides context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_update_commentB

Edit the text content of an existing comment (the opening message of a thread or a reply).

ParametersJSON Schema
NameRequiredDescriptionDefault
idYes
contentYes

TDQS

B3.1/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description must disclose behavioral traits. It only states the action (edit) but omits side effects, idempotency, permissions, or error behavior. For a mutation tool, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence of 17 words with no redundancy. Every word contributes to meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema and low schema coverage, the description leaves critical gaps: the format of id, length limits on content, success/error responses, and whether the update is atomic or has side effects.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate. It does not explain what the 'id' parameter refers to (e.g., comment ID or thread ID) or any constraints on 'content'. The description only gives vague context about editing text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Edit'), the resource ('existing comment'), and clarifies the scope (opening message or reply). This distinguishes it from sibling tools like penpot_create_comment and penpot_delete_comment.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is given on when to use this tool versus alternatives (e.g., when to edit a comment vs. create or delete). Prerequisites like the comment existing or required permissions are not mentioned.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_update_shapesA

Update one or more existing shapes in place, by id. Supports rect, frame, text, circle, bool, path, group, image, svg-raw, and any other shape type. Only the fields you pass are changed — everything else on the shape (child positions, component/variant tags, etc.) is left untouched. Geometry fields (x/y/width/height/rotation) automatically recompute the shape's selection box and transform, so partial geometry edits stay consistent. Exception: for path shapes, x/y/width/height are always derived from path content and cannot be set directly — provide "content" (an array of path commands) to relocate or reshape a path. For image shapes, use "mediaId"/"mediaWidth"/"mediaHeight"/"mtype" to swap the displayed image to a different already-uploaded media object. Colors, corner radii (r1-r4), and shadows each accept either a literal value or a { token: "name" } reference resolved against the project token file (colors/radii/shadows tables respectively); "clearStroke"/"clearShadows" remove strokes/shadows entirely (ignored if "stroke"/"shadows" is also given). "layout" (frame shapes only) sets or replaces the frame's auto-layout; "layoutItem" sets or replaces the shape's own placement within its parent auto-layout frame — both accept the same schema as penpot_add_shapes. Note: updating a group's x/y/width/height only adjusts the group's stored bounding box and does not move its children — use penpot_align_shapes or penpot_distribute_shapes to move a group as a unit.

ParametersJSON Schema
NameRequiredDescriptionDefault
fileIdYes
pageIdYes
patchesYes
tokensPathNo/app/tokens.json

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full burden. It reveals key behaviors: geometry auto-recomputation, path shape restrictions, token references, clearStroke/clearShadows interaction with stroke/shadows, and layout schema reuse. Missing details on idempotency or error handling, but covers many edge cases well.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but well-structured and front-loaded with the core action. Each sentence adds value, covering special cases and behaviors. Minor conciseness improvements possible, but appropriate for the tool's complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers shape behavior thoroughly and notes schema references to penpot_add_shapes. However, it does not explain the required parameters (fileId, pageId, patches) in detail, and with no output schema, return value context is missing. Adequate but has gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema has 0% description coverage; the description explains many parameters (geometry, content, media, colors, radii, shadows, layout) but not all (e.g., fills, stroke, opacity). It adds meaning for key fields, partially compensating for the gap, though some properties remain underspecified.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool updates existing shapes by id, lists supported shape types (rect, frame, etc.), and differentiates from siblings by noting that group updates do not move children and advising use of penpot_align_shapes or penpot_distribute_shapes for that purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly provides when to use (update shapes in place with partial changes) and when not (for moving group as a unit), along with exceptions for path and image shapes. Also mentions alternatives for specific tasks.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

penpot_upload_mediaA

Upload an image or other media asset to a Penpot file and return the media object metadata (id, width, height, mtype). The returned id is used as "mediaId" when creating an image shape via penpot_add_shapes (type: "image"). Supply exactly one of: "filePath" (a local filesystem path the MCP server can read), "url" (an HTTPS URL Penpot's server will fetch directly — nothing passes through the MCP server), or "dataBase64" (base64-encoded bytes, requires "mtype"). The media object is attached to the given Penpot file (isLocal: true by default). After uploading, pass the returned id/width/height/mtype directly into penpot_add_shapes as mediaId/mediaWidth/mediaHeight/mtype for the image shape.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlNo
nameYes
mtypeNo
fileIdYes
isLocalNo
filePathNo
dataBase64No

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full responsibility. It discloses that url images are fetched directly by Penpot's server (nothing passes through the MCP server) and that isLocal defaults to true. It does not mention any destructive or irreversible effects, which is appropriate for a create operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is multiple sentences but each sentence adds value. It is front-loaded with the primary purpose and uses a clear structure: purpose, output usage, input methods, additional details. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 7 parameters, no output schema, and zero schema descriptions, the description covers the tool's input variations, output meaning, and integration with another tool (penpot_add_shapes). It does not explain error cases or limits, but is sufficiently complete for an agent to use the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, yet the description adds meaning for several key parameters: filePath, url, dataBase64 are explained as mutually exclusive sources; dataBase64 requirement for mtype is noted; fileId is contextualized as the target file; isLocal default is mentioned. Not all parameters (e.g., name) are elaborated, but the essential ones are covered.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Upload an image or other media asset'), the target resource ('to a Penpot file'), and the output ('return the media object metadata'). It distinguishes from sibling tools like penpot_add_shapes by detailing how the returned id is used as mediaId for image shapes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains the three mutually exclusive methods for providing media (filePath, url, dataBase64) and notes that dataBase64 requires mtype. It also clarifies the relationship to penpot_add_shapes. However, it does not explicitly state when not to use this tool or contrast with other upload-like tools (none apparent in siblings).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 42 tool updatesv0.3.0
    • Addedpenpot_add_component_instance
    • Changedpenpot_add_shapes1 field changed
      • changedInput schema / properties / shapes / items / oneOf
        Previous value: -[
        -  {
        -    "properties": {
        -      "fillColor": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "properties": {
        -              "token": {
        -                "type": "string"
        -              }
        -            },
        -            "required": [
        -              "token"
        -            ],
        -            "type": "object"
        -          }
        -        ]
        -      },
        -      "fillOpacity": {
        -        "default": 1,
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "frameId": {
        -        "default": "00000000-0000-0000-0000-000000000000",
        -        "type": "string"
        -      },
        -      "height": {
        -        "exclusiveMinimum": 0,
        -        "type": "number"
        -      },
        -      "name": {
        -        "minLength": 1,
        -        "type": "string"
        -      },
        -      "parentId": {
        -        "default": "00000000-0000-0000-0000-000000000000",
        -        "type": "string"
        -      },
        -      "r1": {
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "r2": {
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "r3": {
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "r4": {
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "stroke": {
        -        "properties": {
        -          "alignment": {
        -            "default": "inner",
        -            "enum": [
        -              "inner",
        -              "outer",
        -              "center"
        -            ],
        -            "type": "string"
        -          },
        -          "color": {
        -            "anyOf": [
        -              {
        -                "type": "string"
        -              },
        -              {
        -                "properties": {
        -                  "token": {
        -                    "type": "string"
        -                  }
        -                },
        -                "required": [
        -                  "token"
        -                ],
        -                "type": "object"
        -              }
        -            ]
        -          },
        -          "opacity": {
        -            "default": 1,
        -            "maximum": 1,
        -            "minimum": 0,
        -            "type": "number"
        -          },
        -          "style": {
        -            "default": "solid",
        -            "enum": [
        -              "solid",
        -              "dotted",
        -              "dashed",
        -              "mixed"
        -            ],
        -            "type": "string"
        -          },
        -          "width": {
        -            "default": 1,
        -            "minimum": 0,
        -            "type": "number"
        -          }
        -        },
        -        "required": [
        -          "color"
        -        ],
        -        "type": "object"
        -      },
        -      "type": {
        -        "const": "rect",
        -        "type": "string"
        -      },
        -      "width": {
        -        "exclusiveMinimum": 0,
        -        "type": "number"
        -      },
        -      "x": {
        -        "type": "number"
        -      },
        -      "y": {
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "name",
        -      "x",
        -      "y",
        -      "width",
        -      "height"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "properties": {
        -      "fillColor": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "properties": {
        -              "token": {
        -                "type": "string"
        -              }
        -            },
        -            "required": [
        -              "token"
        -            ],
        -            "type": "object"
        -          }
        -        ]
        -      },
        -      "fillOpacity": {
        -        "default": 1,
        -        "maximum": 1,
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "frameId": {
        -        "default": "00000000-0000-0000-0000-000000000000",
        -        "type": "string"
        -      },
        -      "height": {
        -        "exclusiveMinimum": 0,
        -        "type": "number"
        -      },
        -      "name": {
        -        "minLength": 1,
        -        "type": "string"
        -      },
        -      "parentId": {
        -        "default": "00000000-0000-0000-0000-000000000000",
        -        "type": "string"
        -      },
        -      "r1": {
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "r2": {
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "r3": {
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "r4": {
        -        "minimum": 0,
        -        "type": "number"
        -      },
        -      "stroke": {
        -        "properties": {
        -          "alignment": {
        -            "default": "inner",
        -            "enum": [
        -              "inner",
        -              "outer",
        -              "center"
        -            ],
        -            "type": "string"
        -          },
        -          "color": {
        -            "anyOf": [
        -              {
        -                "type": "string"
        -              },
        -              {
        -                "properties": {
        -                  "token": {
        -                    "type": "string"
        -                  }
        -                },
        -                "required": [
        -                  "token"
        -                ],
        -                "type": "object"
        -              }
        -            ]
        -          },
        -          "opacity": {
        -            "default": 1,
        -            "maximum": 1,
        -            "minimum": 0,
        -            "type": "number"
        -          },
        -          "style": {
        -            "default": "solid",
        -            "enum": [
        -              "solid",
        -              "dotted",
        -              "dashed",
        -              "mixed"
        -            ],
        -            "type": "string"
        -          },
        -          "width": {
        -            "default": 1,
        -            "minimum": 0,
        -            "type": "number"
        -          }
        -        },
        -        "required": [
        -          "color"
        -        ],
        -        "type": "object"
        -      },
        -      "type": {
        -        "const": "frame",
        -        "type": "string"
        -      },
        -      "width": {
        -        "exclusiveMinimum": 0,
        -        "type": "number"
        -      },
        -      "x": {
        -        "type": "number"
        -      },
        -      "y": {
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "name",
        -      "x",
        -      "y",
        -      "width",
        -      "height"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "properties": {
        -      "characters": {
        -        "type": "string"
        -      },
        -      "fillColor": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "properties": {
        -              "token": {
        -                "type": "string"
        -              }
        -            },
        -            "required": [
        -              "token"
        -            ],
        -            "type": "object"
        -          }
        -        ]
        -      },
        -      "fontFamily": {
        -        "type": "string"
        -      },
        -      "fontSize": {
        -        "type": "string"
        -      },
        -      "fontWeight": {
        -        "type": "string"
        -      },
        -      "frameId": {
        -        "default": "00000000-0000-0000-0000-000000000000",
        -        "type": "string"
        -      },
        -      "height": {
        -        "exclusiveMinimum": 0,
        -        "type": "number"
        -      },
        -      "name": {
        -        "minLength": 1,
        -        "type": "string"
        -      },
        -      "parentId": {
        -        "default": "00000000-0000-0000-0000-000000000000",
        -        "type": "string"
        -      },
        -      "type": {
        -        "const": "text",
        -        "type": "string"
        -      },
        -      "width": {
        -        "exclusiveMinimum": 0,
        -        "type": "number"
        -      },
        -      "x": {
        -        "type": "number"
        -      },
        -      "y": {
        -        "type": "number"
        -      }
        -    },
        -    "required": [
        -      "type",
        -      "name",
        -      "x",
        -      "y",
        -      "width",
        -      "height",
        -      "characters"
        -    ],
        -    "type": "object"
        -  }
        -]New value: +[
        +  {
        +    "properties": {
        +      "blendMode": {
        +        "type": "string"
        +      },
        +      "blocked": {
        +        "type": "boolean"
        +      },
        +      "constraintsH": {
        +        "enum": [
        +          "left",
        +          "right",
        +          "leftright",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "constraintsV": {
        +        "enum": [
        +          "top",
        +          "bottom",
        +          "topbottom",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "fillColor": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "fillOpacity": {
        +        "default": 1,
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "fills": {
        +        "items": {
        +          "oneOf": [
        +            {
        +              "properties": {
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "solid",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "linear-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "radial-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "keepAspectRatio": {
        +                  "type": "boolean"
        +                },
        +                "mediaHeight": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mediaId": {
        +                  "format": "uuid",
        +                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +                  "type": "string"
        +                },
        +                "mediaWidth": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mtype": {
        +                  "default": "image/png",
        +                  "type": "string"
        +                },
        +                "name": {
        +                  "type": "string"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "image",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "mediaId",
        +                "mediaWidth",
        +                "mediaHeight"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "frameId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "height": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "hidden": {
        +        "type": "boolean"
        +      },
        +      "id": {
        +        "format": "uuid",
        +        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +        "type": "string"
        +      },
        +      "layoutItem": {
        +        "properties": {
        +          "absolute": {
        +            "type": "boolean"
        +          },
        +          "alignSelf": {
        +            "enum": [
        +              "start",
        +              "center",
        +              "end",
        +              "auto",
        +              "stretch"
        +            ],
        +            "type": "string"
        +          },
        +          "column": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "columnSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "horizontalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "margin": {
        +            "properties": {
        +              "m1": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m2": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m3": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m4": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "maxHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "maxWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "row": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "rowSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "verticalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "zIndex": {
        +            "type": "number"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "name": {
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      "opacity": {
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "parentId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "r1": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "r2": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "r3": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "r4": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "rotation": {
        +        "default": 0,
        +        "type": "number"
        +      },
        +      "shadows": {
        +        "items": {
        +          "anyOf": [
        +            {
        +              "properties": {
        +                "blur": {
        +                  "default": 0,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "offsetX": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "offsetY": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "spread": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "style": {
        +                  "default": "drop-shadow",
        +                  "enum": [
        +                    "drop-shadow",
        +                    "inner-shadow"
        +                  ],
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "token": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "token"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "stroke": {
        +        "properties": {
        +          "alignment": {
        +            "default": "inner",
        +            "enum": [
        +              "inner",
        +              "outer",
        +              "center"
        +            ],
        +            "type": "string"
        +          },
        +          "color": {
        +            "anyOf": [
        +              {
        +                "type": "string"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "opacity": {
        +            "default": 1,
        +            "maximum": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "style": {
        +            "default": "solid",
        +            "enum": [
        +              "solid",
        +              "dotted",
        +              "dashed",
        +              "mixed"
        +            ],
        +            "type": "string"
        +          },
        +          "width": {
        +            "default": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "color"
        +        ],
        +        "type": "object"
        +      },
        +      "type": {
        +        "const": "rect",
        +        "type": "string"
        +      },
        +      "width": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "x": {
        +        "type": "number"
        +      },
        +      "y": {
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "name",
        +      "x",
        +      "y",
        +      "width",
        +      "height"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "properties": {
        +      "blendMode": {
        +        "type": "string"
        +      },
        +      "blocked": {
        +        "type": "boolean"
        +      },
        +      "constraintsH": {
        +        "enum": [
        +          "left",
        +          "right",
        +          "leftright",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "constraintsV": {
        +        "enum": [
        +          "top",
        +          "bottom",
        +          "topbottom",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "fillColor": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "fillOpacity": {
        +        "default": 1,
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "fills": {
        +        "items": {
        +          "oneOf": [
        +            {
        +              "properties": {
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "solid",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "linear-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "radial-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "keepAspectRatio": {
        +                  "type": "boolean"
        +                },
        +                "mediaHeight": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mediaId": {
        +                  "format": "uuid",
        +                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +                  "type": "string"
        +                },
        +                "mediaWidth": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mtype": {
        +                  "default": "image/png",
        +                  "type": "string"
        +                },
        +                "name": {
        +                  "type": "string"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "image",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "mediaId",
        +                "mediaWidth",
        +                "mediaHeight"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "frameId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "height": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "hidden": {
        +        "type": "boolean"
        +      },
        +      "id": {
        +        "format": "uuid",
        +        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +        "type": "string"
        +      },
        +      "layout": {
        +        "oneOf": [
        +          {
        +            "properties": {
        +              "alignContent": {
        +                "enum": [
        +                  "start",
        +                  "center",
        +                  "end",
        +                  "stretch",
        +                  "space-between",
        +                  "space-around",
        +                  "space-evenly"
        +                ],
        +                "type": "string"
        +              },
        +              "alignItems": {
        +                "enum": [
        +                  "start",
        +                  "center",
        +                  "end",
        +                  "stretch"
        +                ],
        +                "type": "string"
        +              },
        +              "columnGap": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "dir": {
        +                "enum": [
        +                  "row",
        +                  "row-reverse",
        +                  "column",
        +                  "column-reverse"
        +                ],
        +                "type": "string"
        +              },
        +              "justifyContent": {
        +                "enum": [
        +                  "start",
        +                  "center",
        +                  "end",
        +                  "stretch",
        +                  "space-between",
        +                  "space-around",
        +                  "space-evenly"
        +                ],
        +                "type": "string"
        +              },
        +              "justifyItems": {
        +                "enum": [
        +                  "start",
        +                  "center",
        +                  "end",
        +                  "stretch"
        +                ],
        +                "type": "string"
        +              },
        +              "padding": {
        +                "properties": {
        +                  "p1": {
        +                    "anyOf": [
        +                      {
        +                        "type": "number"
        +                      },
        +                      {
        +                        "properties": {
        +                          "token": {
        +                            "type": "string"
        +                          }
        +                        },
        +                        "required": [
        +                          "token"
        +                        ],
        +                        "type": "object"
        +                      }
        +                    ]
        +                  },
        +                  "p2": {
        +                    "anyOf": [
        +                      {
        +                        "type": "number"
        +                      },
        +                      {
        +                        "properties": {
        +                          "token": {
        +                            "type": "string"
        +                          }
        +                        },
        +                        "required": [
        +                          "token"
        +                        ],
        +                        "type": "object"
        +                      }
        +                    ]
        +                  },
        +                  "p3": {
        +                    "anyOf": [
        +                      {
        +                        "type": "number"
        +                      },
        +                      {
        +                        "properties": {
        +                          "token": {
        +                            "type": "string"
        +                          }
        +                        },
        +                        "required": [
        +                          "token"
        +                        ],
        +                        "type": "object"
        +                      }
        +                    ]
        +                  },
        +                  "p4": {
        +                    "anyOf": [
        +                      {
        +                        "type": "number"
        +                      },
        +                      {
        +                        "properties": {
        +                          "token": {
        +                            "type": "string"
        +                          }
        +                        },
        +                        "required": [
        +                          "token"
        +                        ],
        +                        "type": "object"
        +                      }
        +                    ]
        +                  }
        +                },
        +                "type": "object"
        +              },
        +              "paddingType": {
        +                "enum": [
        +                  "simple",
        +                  "multiple"
        +                ],
        +                "type": "string"
        +              },
        +              "rowGap": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "type": {
        +                "const": "flex",
        +                "type": "string"
        +              },
        +              "wrapType": {
        +                "enum": [
        +                  "wrap",
        +                  "nowrap"
        +                ],
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "type"
        +            ],
        +            "type": "object"
        +          },
        +          {
        +            "properties": {
        +              "alignContent": {
        +                "enum": [
        +                  "start",
        +                  "center",
        +                  "end",
        +                  "stretch",
        +                  "space-between",
        +                  "space-around",
        +                  "space-evenly"
        +                ],
        +                "type": "string"
        +              },
        +              "alignItems": {
        +                "enum": [
        +                  "start",
        +                  "center",
        +                  "end",
        +                  "stretch"
        +                ],
        +                "type": "string"
        +              },
        +              "columnGap": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "columns": {
        +                "items": {
        +                  "properties": {
        +                    "type": {
        +                      "enum": [
        +                        "fixed",
        +                        "percent",
        +                        "flex",
        +                        "auto"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "anyOf": [
        +                        {
        +                          "type": "number"
        +                        },
        +                        {
        +                          "properties": {
        +                            "token": {
        +                              "type": "string"
        +                            }
        +                          },
        +                          "required": [
        +                            "token"
        +                          ],
        +                          "type": "object"
        +                        }
        +                      ]
        +                    }
        +                  },
        +                  "required": [
        +                    "type"
        +                  ],
        +                  "type": "object"
        +                },
        +                "type": "array"
        +              },
        +              "dir": {
        +                "enum": [
        +                  "row",
        +                  "column"
        +                ],
        +                "type": "string"
        +              },
        +              "justifyContent": {
        +                "enum": [
        +                  "start",
        +                  "center",
        +                  "end",
        +                  "stretch",
        +                  "space-between",
        +                  "space-around",
        +                  "space-evenly"
        +                ],
        +                "type": "string"
        +              },
        +              "justifyItems": {
        +                "enum": [
        +                  "start",
        +                  "center",
        +                  "end",
        +                  "stretch"
        +                ],
        +                "type": "string"
        +              },
        +              "padding": {
        +                "properties": {
        +                  "p1": {
        +                    "anyOf": [
        +                      {
        +                        "type": "number"
        +                      },
        +                      {
        +                        "properties": {
        +                          "token": {
        +                            "type": "string"
        +                          }
        +                        },
        +                        "required": [
        +                          "token"
        +                        ],
        +                        "type": "object"
        +                      }
        +                    ]
        +                  },
        +                  "p2": {
        +                    "anyOf": [
        +                      {
        +                        "type": "number"
        +                      },
        +                      {
        +                        "properties": {
        +                          "token": {
        +                            "type": "string"
        +                          }
        +                        },
        +                        "required": [
        +                          "token"
        +                        ],
        +                        "type": "object"
        +                      }
        +                    ]
        +                  },
        +                  "p3": {
        +                    "anyOf": [
        +                      {
        +                        "type": "number"
        +                      },
        +                      {
        +                        "properties": {
        +                          "token": {
        +                            "type": "string"
        +                          }
        +                        },
        +                        "required": [
        +                          "token"
        +                        ],
        +                        "type": "object"
        +                      }
        +                    ]
        +                  },
        +                  "p4": {
        +                    "anyOf": [
        +                      {
        +                        "type": "number"
        +                      },
        +                      {
        +                        "properties": {
        +                          "token": {
        +                            "type": "string"
        +                          }
        +                        },
        +                        "required": [
        +                          "token"
        +                        ],
        +                        "type": "object"
        +                      }
        +                    ]
        +                  }
        +                },
        +                "type": "object"
        +              },
        +              "paddingType": {
        +                "enum": [
        +                  "simple",
        +                  "multiple"
        +                ],
        +                "type": "string"
        +              },
        +              "rowGap": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "rows": {
        +                "items": {
        +                  "properties": {
        +                    "type": {
        +                      "enum": [
        +                        "fixed",
        +                        "percent",
        +                        "flex",
        +                        "auto"
        +                      ],
        +                      "type": "string"
        +                    },
        +                    "value": {
        +                      "anyOf": [
        +                        {
        +                          "type": "number"
        +                        },
        +                        {
        +                          "properties": {
        +                            "token": {
        +                              "type": "string"
        +                            }
        +                          },
        +                          "required": [
        +                            "token"
        +                          ],
        +                          "type": "object"
        +                        }
        +                      ]
        +                    }
        +                  },
        +                  "required": [
        +                    "type"
        +                  ],
        +                  "type": "object"
        +                },
        +                "type": "array"
        +              },
        +              "type": {
        +                "const": "grid",
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "type"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "layoutItem": {
        +        "properties": {
        +          "absolute": {
        +            "type": "boolean"
        +          },
        +          "alignSelf": {
        +            "enum": [
        +              "start",
        +              "center",
        +              "end",
        +              "auto",
        +              "stretch"
        +            ],
        +            "type": "string"
        +          },
        +          "column": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "columnSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "horizontalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "margin": {
        +            "properties": {
        +              "m1": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m2": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m3": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m4": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "maxHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "maxWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "row": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "rowSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "verticalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "zIndex": {
        +            "type": "number"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "name": {
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      "opacity": {
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "parentId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "r1": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "r2": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "r3": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "r4": {
        +        "anyOf": [
        +          {
        +            "type": "number"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "rotation": {
        +        "default": 0,
        +        "type": "number"
        +      },
        +      "shadows": {
        +        "items": {
        +          "anyOf": [
        +            {
        +              "properties": {
        +                "blur": {
        +                  "default": 0,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "offsetX": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "offsetY": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "spread": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "style": {
        +                  "default": "drop-shadow",
        +                  "enum": [
        +                    "drop-shadow",
        +                    "inner-shadow"
        +                  ],
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "token": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "token"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "stroke": {
        +        "properties": {
        +          "alignment": {
        +            "default": "inner",
        +            "enum": [
        +              "inner",
        +              "outer",
        +              "center"
        +            ],
        +            "type": "string"
        +          },
        +          "color": {
        +            "anyOf": [
        +              {
        +                "type": "string"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "opacity": {
        +            "default": 1,
        +            "maximum": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "style": {
        +            "default": "solid",
        +            "enum": [
        +              "solid",
        +              "dotted",
        +              "dashed",
        +              "mixed"
        +            ],
        +            "type": "string"
        +          },
        +          "width": {
        +            "default": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "color"
        +        ],
        +        "type": "object"
        +      },
        +      "type": {
        +        "const": "frame",
        +        "type": "string"
        +      },
        +      "width": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "x": {
        +        "type": "number"
        +      },
        +      "y": {
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "name",
        +      "x",
        +      "y",
        +      "width",
        +      "height"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "properties": {
        +      "blendMode": {
        +        "type": "string"
        +      },
        +      "blocked": {
        +        "type": "boolean"
        +      },
        +      "characters": {
        +        "type": "string"
        +      },
        +      "constraintsH": {
        +        "enum": [
        +          "left",
        +          "right",
        +          "leftright",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "constraintsV": {
        +        "enum": [
        +          "top",
        +          "bottom",
        +          "topbottom",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "fillColor": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "fontFamily": {
        +        "type": "string"
        +      },
        +      "fontSize": {
        +        "type": "string"
        +      },
        +      "fontWeight": {
        +        "type": "string"
        +      },
        +      "frameId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "growType": {
        +        "enum": [
        +          "auto-width",
        +          "auto-height",
        +          "fixed"
        +        ],
        +        "type": "string"
        +      },
        +      "height": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "hidden": {
        +        "type": "boolean"
        +      },
        +      "id": {
        +        "format": "uuid",
        +        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +        "type": "string"
        +      },
        +      "layoutItem": {
        +        "properties": {
        +          "absolute": {
        +            "type": "boolean"
        +          },
        +          "alignSelf": {
        +            "enum": [
        +              "start",
        +              "center",
        +              "end",
        +              "auto",
        +              "stretch"
        +            ],
        +            "type": "string"
        +          },
        +          "column": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "columnSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "horizontalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "margin": {
        +            "properties": {
        +              "m1": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m2": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m3": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m4": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "maxHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "maxWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "row": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "rowSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "verticalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "zIndex": {
        +            "type": "number"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "name": {
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      "opacity": {
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "paragraphs": {
        +        "items": {
        +          "properties": {
        +            "fillColor": {
        +              "anyOf": [
        +                {
        +                  "type": "string"
        +                },
        +                {
        +                  "properties": {
        +                    "token": {
        +                      "type": "string"
        +                    }
        +                  },
        +                  "required": [
        +                    "token"
        +                  ],
        +                  "type": "object"
        +                }
        +              ]
        +            },
        +            "fills": {
        +              "items": {
        +                "oneOf": [
        +                  {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "type": {
        +                        "const": "solid",
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "type",
        +                      "color"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  {
        +                    "properties": {
        +                      "endX": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "endY": {
        +                        "default": 0.5,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "startX": {
        +                        "default": 0,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "startY": {
        +                        "default": 0.5,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "stops": {
        +                        "items": {
        +                          "properties": {
        +                            "color": {
        +                              "anyOf": [
        +                                {
        +                                  "type": "string"
        +                                },
        +                                {
        +                                  "properties": {
        +                                    "token": {
        +                                      "type": "string"
        +                                    }
        +                                  },
        +                                  "required": [
        +                                    "token"
        +                                  ],
        +                                  "type": "object"
        +                                }
        +                              ]
        +                            },
        +                            "offset": {
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "opacity": {
        +                              "default": 1,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            }
        +                          },
        +                          "required": [
        +                            "color",
        +                            "offset"
        +                          ],
        +                          "type": "object"
        +                        },
        +                        "minItems": 2,
        +                        "type": "array"
        +                      },
        +                      "type": {
        +                        "const": "linear-gradient",
        +                        "type": "string"
        +                      },
        +                      "width": {
        +                        "default": 1,
        +                        "exclusiveMinimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "type",
        +                      "stops"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  {
        +                    "properties": {
        +                      "endX": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "endY": {
        +                        "default": 0.5,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "startX": {
        +                        "default": 0.5,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "startY": {
        +                        "default": 0.5,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "stops": {
        +                        "items": {
        +                          "properties": {
        +                            "color": {
        +                              "anyOf": [
        +                                {
        +                                  "type": "string"
        +                                },
        +                                {
        +                                  "properties": {
        +                                    "token": {
        +                                      "type": "string"
        +                                    }
        +                                  },
        +                                  "required": [
        +                                    "token"
        +                                  ],
        +                                  "type": "object"
        +                                }
        +                              ]
        +                            },
        +                            "offset": {
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "opacity": {
        +                              "default": 1,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            }
        +                          },
        +                          "required": [
        +                            "color",
        +                            "offset"
        +                          ],
        +                          "type": "object"
        +                        },
        +                        "minItems": 2,
        +                        "type": "array"
        +                      },
        +                      "type": {
        +                        "const": "radial-gradient",
        +                        "type": "string"
        +                      },
        +                      "width": {
        +                        "default": 1,
        +                        "exclusiveMinimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "type",
        +                      "stops"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  {
        +                    "properties": {
        +                      "keepAspectRatio": {
        +                        "type": "boolean"
        +                      },
        +                      "mediaHeight": {
        +                        "exclusiveMinimum": 0,
        +                        "maximum": 9007199254740991,
        +                        "type": "integer"
        +                      },
        +                      "mediaId": {
        +                        "format": "uuid",
        +                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +                        "type": "string"
        +                      },
        +                      "mediaWidth": {
        +                        "exclusiveMinimum": 0,
        +                        "maximum": 9007199254740991,
        +                        "type": "integer"
        +                      },
        +                      "mtype": {
        +                        "default": "image/png",
        +                        "type": "string"
        +                      },
        +                      "name": {
        +                        "type": "string"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "type": {
        +                        "const": "image",
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "type",
        +                      "mediaId",
        +                      "mediaWidth",
        +                      "mediaHeight"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "type": "array"
        +            },
        +            "fontFamily": {
        +              "type": "string"
        +            },
        +            "fontSize": {
        +              "type": "string"
        +            },
        +            "fontStyle": {
        +              "enum": [
        +                "normal",
        +                "italic"
        +              ],
        +              "type": "string"
        +            },
        +            "fontWeight": {
        +              "type": "string"
        +            },
        +            "letterSpacing": {
        +              "type": "string"
        +            },
        +            "lineHeight": {
        +              "type": "string"
        +            },
        +            "ranges": {
        +              "items": {
        +                "properties": {
        +                  "fillColor": {
        +                    "anyOf": [
        +                      {
        +                        "type": "string"
        +                      },
        +                      {
        +                        "properties": {
        +                          "token": {
        +                            "type": "string"
        +                          }
        +                        },
        +                        "required": [
        +                          "token"
        +                        ],
        +                        "type": "object"
        +                      }
        +                    ]
        +                  },
        +                  "fills": {
        +                    "items": {
        +                      "oneOf": [
        +                        {
        +                          "properties": {
        +                            "color": {
        +                              "anyOf": [
        +                                {
        +                                  "type": "string"
        +                                },
        +                                {
        +                                  "properties": {
        +                                    "token": {
        +                                      "type": "string"
        +                                    }
        +                                  },
        +                                  "required": [
        +                                    "token"
        +                                  ],
        +                                  "type": "object"
        +                                }
        +                              ]
        +                            },
        +                            "opacity": {
        +                              "default": 1,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "type": {
        +                              "const": "solid",
        +                              "type": "string"
        +                            }
        +                          },
        +                          "required": [
        +                            "type",
        +                            "color"
        +                          ],
        +                          "type": "object"
        +                        },
        +                        {
        +                          "properties": {
        +                            "endX": {
        +                              "default": 1,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "endY": {
        +                              "default": 0.5,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "opacity": {
        +                              "default": 1,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "startX": {
        +                              "default": 0,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "startY": {
        +                              "default": 0.5,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "stops": {
        +                              "items": {
        +                                "properties": {
        +                                  "color": {
        +                                    "anyOf": [
        +                                      {
        +                                        "type": "string"
        +                                      },
        +                                      {
        +                                        "properties": {
        +                                          "token": {
        +                                            "type": "string"
        +                                          }
        +                                        },
        +                                        "required": [
        +                                          "token"
        +                                        ],
        +                                        "type": "object"
        +                                      }
        +                                    ]
        +                                  },
        +                                  "offset": {
        +                                    "maximum": 1,
        +                                    "minimum": 0,
        +                                    "type": "number"
        +                                  },
        +                                  "opacity": {
        +                                    "default": 1,
        +                                    "maximum": 1,
        +                                    "minimum": 0,
        +                                    "type": "number"
        +                                  }
        +                                },
        +                                "required": [
        +                                  "color",
        +                                  "offset"
        +                                ],
        +                                "type": "object"
        +                              },
        +                              "minItems": 2,
        +                              "type": "array"
        +                            },
        +                            "type": {
        +                              "const": "linear-gradient",
        +                              "type": "string"
        +                            },
        +                            "width": {
        +                              "default": 1,
        +                              "exclusiveMinimum": 0,
        +                              "type": "number"
        +                            }
        +                          },
        +                          "required": [
        +                            "type",
        +                            "stops"
        +                          ],
        +                          "type": "object"
        +                        },
        +                        {
        +                          "properties": {
        +                            "endX": {
        +                              "default": 1,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "endY": {
        +                              "default": 0.5,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "opacity": {
        +                              "default": 1,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "startX": {
        +                              "default": 0.5,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "startY": {
        +                              "default": 0.5,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "stops": {
        +                              "items": {
        +                                "properties": {
        +                                  "color": {
        +                                    "anyOf": [
        +                                      {
        +                                        "type": "string"
        +                                      },
        +                                      {
        +                                        "properties": {
        +                                          "token": {
        +                                            "type": "string"
        +                                          }
        +                                        },
        +                                        "required": [
        +                                          "token"
        +                                        ],
        +                                        "type": "object"
        +                                      }
        +                                    ]
        +                                  },
        +                                  "offset": {
        +                                    "maximum": 1,
        +                                    "minimum": 0,
        +                                    "type": "number"
        +                                  },
        +                                  "opacity": {
        +                                    "default": 1,
        +                                    "maximum": 1,
        +                                    "minimum": 0,
        +                                    "type": "number"
        +                                  }
        +                                },
        +                                "required": [
        +                                  "color",
        +                                  "offset"
        +                                ],
        +                                "type": "object"
        +                              },
        +                              "minItems": 2,
        +                              "type": "array"
        +                            },
        +                            "type": {
        +                              "const": "radial-gradient",
        +                              "type": "string"
        +                            },
        +                            "width": {
        +                              "default": 1,
        +                              "exclusiveMinimum": 0,
        +                              "type": "number"
        +                            }
        +                          },
        +                          "required": [
        +                            "type",
        +                            "stops"
        +                          ],
        +                          "type": "object"
        +                        },
        +                        {
        +                          "properties": {
        +                            "keepAspectRatio": {
        +                              "type": "boolean"
        +                            },
        +                            "mediaHeight": {
        +                              "exclusiveMinimum": 0,
        +                              "maximum": 9007199254740991,
        +                              "type": "integer"
        +                            },
        +                            "mediaId": {
        +                              "format": "uuid",
        +                              "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +                              "type": "string"
        +                            },
        +                            "mediaWidth": {
        +                              "exclusiveMinimum": 0,
        +                              "maximum": 9007199254740991,
        +                              "type": "integer"
        +                            },
        +                            "mtype": {
        +                              "default": "image/png",
        +                              "type": "string"
        +                            },
        +                            "name": {
        +                              "type": "string"
        +                            },
        +                            "opacity": {
        +                              "default": 1,
        +                              "maximum": 1,
        +                              "minimum": 0,
        +                              "type": "number"
        +                            },
        +                            "type": {
        +                              "const": "image",
        +                              "type": "string"
        +                            }
        +                          },
        +                          "required": [
        +                            "type",
        +                            "mediaId",
        +                            "mediaWidth",
        +                            "mediaHeight"
        +                          ],
        +                          "type": "object"
        +                        }
        +                      ]
        +                    },
        +                    "type": "array"
        +                  },
        +                  "fontFamily": {
        +                    "type": "string"
        +                  },
        +                  "fontSize": {
        +                    "type": "string"
        +                  },
        +                  "fontStyle": {
        +                    "enum": [
        +                      "normal",
        +                      "italic"
        +                    ],
        +                    "type": "string"
        +                  },
        +                  "fontWeight": {
        +                    "type": "string"
        +                  },
        +                  "letterSpacing": {
        +                    "type": "string"
        +                  },
        +                  "lineHeight": {
        +                    "type": "string"
        +                  },
        +                  "text": {
        +                    "type": "string"
        +                  },
        +                  "textDecoration": {
        +                    "enum": [
        +                      "none",
        +                      "underline",
        +                      "line-through"
        +                    ],
        +                    "type": "string"
        +                  },
        +                  "textTransform": {
        +                    "enum": [
        +                      "none",
        +                      "uppercase",
        +                      "lowercase",
        +                      "capitalize",
        +                      "title-case"
        +                    ],
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "text"
        +                ],
        +                "type": "object"
        +              },
        +              "minItems": 1,
        +              "type": "array"
        +            },
        +            "textAlign": {
        +              "enum": [
        +                "left",
        +                "right",
        +                "center",
        +                "justify"
        +              ],
        +              "type": "string"
        +            },
        +            "textDecoration": {
        +              "enum": [
        +                "none",
        +                "underline",
        +                "line-through"
        +              ],
        +              "type": "string"
        +            },
        +            "textTransform": {
        +              "enum": [
        +                "none",
        +                "uppercase",
        +                "lowercase",
        +                "capitalize",
        +                "title-case"
        +              ],
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "ranges"
        +          ],
        +          "type": "object"
        +        },
        +        "minItems": 1,
        +        "type": "array"
        +      },
        +      "parentId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "rotation": {
        +        "default": 0,
        +        "type": "number"
        +      },
        +      "shadows": {
        +        "items": {
        +          "anyOf": [
        +            {
        +              "properties": {
        +                "blur": {
        +                  "default": 0,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "offsetX": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "offsetY": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "spread": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "style": {
        +                  "default": "drop-shadow",
        +                  "enum": [
        +                    "drop-shadow",
        +                    "inner-shadow"
        +                  ],
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "token": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "token"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "type": {
        +        "const": "text",
        +        "type": "string"
        +      },
        +      "verticalAlign": {
        +        "enum": [
        +          "top",
        +          "center",
        +          "bottom"
        +        ],
        +        "type": "string"
        +      },
        +      "width": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "x": {
        +        "type": "number"
        +      },
        +      "y": {
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "name",
        +      "x",
        +      "y",
        +      "width",
        +      "height"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "properties": {
        +      "blendMode": {
        +        "type": "string"
        +      },
        +      "blocked": {
        +        "type": "boolean"
        +      },
        +      "constraintsH": {
        +        "enum": [
        +          "left",
        +          "right",
        +          "leftright",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "constraintsV": {
        +        "enum": [
        +          "top",
        +          "bottom",
        +          "topbottom",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "fillColor": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "fillOpacity": {
        +        "default": 1,
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "fills": {
        +        "items": {
        +          "oneOf": [
        +            {
        +              "properties": {
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "solid",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "linear-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "radial-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "keepAspectRatio": {
        +                  "type": "boolean"
        +                },
        +                "mediaHeight": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mediaId": {
        +                  "format": "uuid",
        +                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +                  "type": "string"
        +                },
        +                "mediaWidth": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mtype": {
        +                  "default": "image/png",
        +                  "type": "string"
        +                },
        +                "name": {
        +                  "type": "string"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "image",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "mediaId",
        +                "mediaWidth",
        +                "mediaHeight"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "frameId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "height": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "hidden": {
        +        "type": "boolean"
        +      },
        +      "id": {
        +        "format": "uuid",
        +        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +        "type": "string"
        +      },
        +      "layoutItem": {
        +        "properties": {
        +          "absolute": {
        +            "type": "boolean"
        +          },
        +          "alignSelf": {
        +            "enum": [
        +              "start",
        +              "center",
        +              "end",
        +              "auto",
        +              "stretch"
        +            ],
        +            "type": "string"
        +          },
        +          "column": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "columnSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "horizontalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "margin": {
        +            "properties": {
        +              "m1": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m2": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m3": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m4": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "maxHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "maxWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "row": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "rowSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "verticalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "zIndex": {
        +            "type": "number"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "name": {
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      "opacity": {
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "parentId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "rotation": {
        +        "default": 0,
        +        "type": "number"
        +      },
        +      "shadows": {
        +        "items": {
        +          "anyOf": [
        +            {
        +              "properties": {
        +                "blur": {
        +                  "default": 0,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "offsetX": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "offsetY": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "spread": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "style": {
        +                  "default": "drop-shadow",
        +                  "enum": [
        +                    "drop-shadow",
        +                    "inner-shadow"
        +                  ],
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "token": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "token"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "stroke": {
        +        "properties": {
        +          "alignment": {
        +            "default": "inner",
        +            "enum": [
        +              "inner",
        +              "outer",
        +              "center"
        +            ],
        +            "type": "string"
        +          },
        +          "color": {
        +            "anyOf": [
        +              {
        +                "type": "string"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "opacity": {
        +            "default": 1,
        +            "maximum": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "style": {
        +            "default": "solid",
        +            "enum": [
        +              "solid",
        +              "dotted",
        +              "dashed",
        +              "mixed"
        +            ],
        +            "type": "string"
        +          },
        +          "width": {
        +            "default": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "color"
        +        ],
        +        "type": "object"
        +      },
        +      "type": {
        +        "const": "circle",
        +        "type": "string"
        +      },
        +      "width": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "x": {
        +        "type": "number"
        +      },
        +      "y": {
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "name",
        +      "x",
        +      "y",
        +      "width",
        +      "height"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "properties": {
        +      "blendMode": {
        +        "type": "string"
        +      },
        +      "blocked": {
        +        "type": "boolean"
        +      },
        +      "constraintsH": {
        +        "enum": [
        +          "left",
        +          "right",
        +          "leftright",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "constraintsV": {
        +        "enum": [
        +          "top",
        +          "bottom",
        +          "topbottom",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "content": {
        +        "items": {
        +          "oneOf": [
        +            {
        +              "properties": {
        +                "command": {
        +                  "const": "move-to",
        +                  "type": "string"
        +                },
        +                "params": {
        +                  "properties": {
        +                    "x": {
        +                      "type": "number"
        +                    },
        +                    "y": {
        +                      "type": "number"
        +                    }
        +                  },
        +                  "required": [
        +                    "x",
        +                    "y"
        +                  ],
        +                  "type": "object"
        +                }
        +              },
        +              "required": [
        +                "command",
        +                "params"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "command": {
        +                  "const": "line-to",
        +                  "type": "string"
        +                },
        +                "params": {
        +                  "properties": {
        +                    "x": {
        +                      "type": "number"
        +                    },
        +                    "y": {
        +                      "type": "number"
        +                    }
        +                  },
        +                  "required": [
        +                    "x",
        +                    "y"
        +                  ],
        +                  "type": "object"
        +                }
        +              },
        +              "required": [
        +                "command",
        +                "params"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "command": {
        +                  "const": "curve-to",
        +                  "type": "string"
        +                },
        +                "params": {
        +                  "properties": {
        +                    "c1x": {
        +                      "type": "number"
        +                    },
        +                    "c1y": {
        +                      "type": "number"
        +                    },
        +                    "c2x": {
        +                      "type": "number"
        +                    },
        +                    "c2y": {
        +                      "type": "number"
        +                    },
        +                    "x": {
        +                      "type": "number"
        +                    },
        +                    "y": {
        +                      "type": "number"
        +                    }
        +                  },
        +                  "required": [
        +                    "x",
        +                    "y",
        +                    "c1x",
        +                    "c1y",
        +                    "c2x",
        +                    "c2y"
        +                  ],
        +                  "type": "object"
        +                }
        +              },
        +              "required": [
        +                "command",
        +                "params"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "command": {
        +                  "const": "close-path",
        +                  "type": "string"
        +                },
        +                "params": {
        +                  "properties": {},
        +                  "type": "object"
        +                }
        +              },
        +              "required": [
        +                "command"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "minItems": 1,
        +        "type": "array"
        +      },
        +      "fillColor": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "fillOpacity": {
        +        "default": 1,
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "fills": {
        +        "items": {
        +          "oneOf": [
        +            {
        +              "properties": {
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "solid",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "linear-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "radial-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "keepAspectRatio": {
        +                  "type": "boolean"
        +                },
        +                "mediaHeight": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mediaId": {
        +                  "format": "uuid",
        +                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +                  "type": "string"
        +                },
        +                "mediaWidth": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mtype": {
        +                  "default": "image/png",
        +                  "type": "string"
        +                },
        +                "name": {
        +                  "type": "string"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "image",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "mediaId",
        +                "mediaWidth",
        +                "mediaHeight"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "frameId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "hidden": {
        +        "type": "boolean"
        +      },
        +      "id": {
        +        "format": "uuid",
        +        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +        "type": "string"
        +      },
        +      "layoutItem": {
        +        "properties": {
        +          "absolute": {
        +            "type": "boolean"
        +          },
        +          "alignSelf": {
        +            "enum": [
        +              "start",
        +              "center",
        +              "end",
        +              "auto",
        +              "stretch"
        +            ],
        +            "type": "string"
        +          },
        +          "column": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "columnSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "horizontalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "margin": {
        +            "properties": {
        +              "m1": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m2": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m3": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m4": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "maxHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "maxWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "row": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "rowSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "verticalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "zIndex": {
        +            "type": "number"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "name": {
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      "opacity": {
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "parentId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "rotation": {
        +        "default": 0,
        +        "type": "number"
        +      },
        +      "shadows": {
        +        "items": {
        +          "anyOf": [
        +            {
        +              "properties": {
        +                "blur": {
        +                  "default": 0,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "offsetX": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "offsetY": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "spread": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "style": {
        +                  "default": "drop-shadow",
        +                  "enum": [
        +                    "drop-shadow",
        +                    "inner-shadow"
        +                  ],
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "token": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "token"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "stroke": {
        +        "properties": {
        +          "alignment": {
        +            "default": "inner",
        +            "enum": [
        +              "inner",
        +              "outer",
        +              "center"
        +            ],
        +            "type": "string"
        +          },
        +          "color": {
        +            "anyOf": [
        +              {
        +                "type": "string"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "opacity": {
        +            "default": 1,
        +            "maximum": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "style": {
        +            "default": "solid",
        +            "enum": [
        +              "solid",
        +              "dotted",
        +              "dashed",
        +              "mixed"
        +            ],
        +            "type": "string"
        +          },
        +          "width": {
        +            "default": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "color"
        +        ],
        +        "type": "object"
        +      },
        +      "type": {
        +        "const": "path",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "name",
        +      "content"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "properties": {
        +      "blendMode": {
        +        "type": "string"
        +      },
        +      "blocked": {
        +        "type": "boolean"
        +      },
        +      "boolType": {
        +        "enum": [
        +          "union",
        +          "difference",
        +          "intersection",
        +          "exclusion"
        +        ],
        +        "type": "string"
        +      },
        +      "constraintsH": {
        +        "enum": [
        +          "left",
        +          "right",
        +          "leftright",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "constraintsV": {
        +        "enum": [
        +          "top",
        +          "bottom",
        +          "topbottom",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "fillColor": {
        +        "anyOf": [
        +          {
        +            "type": "string"
        +          },
        +          {
        +            "properties": {
        +              "token": {
        +                "type": "string"
        +              }
        +            },
        +            "required": [
        +              "token"
        +            ],
        +            "type": "object"
        +          }
        +        ]
        +      },
        +      "fillOpacity": {
        +        "default": 1,
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "fills": {
        +        "items": {
        +          "oneOf": [
        +            {
        +              "properties": {
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "solid",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "linear-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "endX": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "endY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startX": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "startY": {
        +                  "default": 0.5,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "stops": {
        +                  "items": {
        +                    "properties": {
        +                      "color": {
        +                        "anyOf": [
        +                          {
        +                            "type": "string"
        +                          },
        +                          {
        +                            "properties": {
        +                              "token": {
        +                                "type": "string"
        +                              }
        +                            },
        +                            "required": [
        +                              "token"
        +                            ],
        +                            "type": "object"
        +                          }
        +                        ]
        +                      },
        +                      "offset": {
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      },
        +                      "opacity": {
        +                        "default": 1,
        +                        "maximum": 1,
        +                        "minimum": 0,
        +                        "type": "number"
        +                      }
        +                    },
        +                    "required": [
        +                      "color",
        +                      "offset"
        +                    ],
        +                    "type": "object"
        +                  },
        +                  "minItems": 2,
        +                  "type": "array"
        +                },
        +                "type": {
        +                  "const": "radial-gradient",
        +                  "type": "string"
        +                },
        +                "width": {
        +                  "default": 1,
        +                  "exclusiveMinimum": 0,
        +                  "type": "number"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "stops"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "keepAspectRatio": {
        +                  "type": "boolean"
        +                },
        +                "mediaHeight": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mediaId": {
        +                  "format": "uuid",
        +                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +                  "type": "string"
        +                },
        +                "mediaWidth": {
        +                  "exclusiveMinimum": 0,
        +                  "maximum": 9007199254740991,
        +                  "type": "integer"
        +                },
        +                "mtype": {
        +                  "default": "image/png",
        +                  "type": "string"
        +                },
        +                "name": {
        +                  "type": "string"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "type": {
        +                  "const": "image",
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "type",
        +                "mediaId",
        +                "mediaWidth",
        +                "mediaHeight"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "frameId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "height": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "hidden": {
        +        "type": "boolean"
        +      },
        +      "id": {
        +        "format": "uuid",
        +        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +        "type": "string"
        +      },
        +      "layoutItem": {
        +        "properties": {
        +          "absolute": {
        +            "type": "boolean"
        +          },
        +          "alignSelf": {
        +            "enum": [
        +              "start",
        +              "center",
        +              "end",
        +              "auto",
        +              "stretch"
        +            ],
        +            "type": "string"
        +          },
        +          "column": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "columnSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "horizontalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "margin": {
        +            "properties": {
        +              "m1": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m2": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m3": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m4": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "maxHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "maxWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "row": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "rowSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "verticalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "zIndex": {
        +            "type": "number"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "name": {
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      "opacity": {
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "parentId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "rotation": {
        +        "default": 0,
        +        "type": "number"
        +      },
        +      "shadows": {
        +        "items": {
        +          "anyOf": [
        +            {
        +              "properties": {
        +                "blur": {
        +                  "default": 0,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "color": {
        +                  "anyOf": [
        +                    {
        +                      "type": "string"
        +                    },
        +                    {
        +                      "properties": {
        +                        "token": {
        +                          "type": "string"
        +                        }
        +                      },
        +                      "required": [
        +                        "token"
        +                      ],
        +                      "type": "object"
        +                    }
        +                  ]
        +                },
        +                "offsetX": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "offsetY": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "opacity": {
        +                  "default": 1,
        +                  "maximum": 1,
        +                  "minimum": 0,
        +                  "type": "number"
        +                },
        +                "spread": {
        +                  "default": 0,
        +                  "type": "number"
        +                },
        +                "style": {
        +                  "default": "drop-shadow",
        +                  "enum": [
        +                    "drop-shadow",
        +                    "inner-shadow"
        +                  ],
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "color"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "properties": {
        +                "token": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "token"
        +              ],
        +              "type": "object"
        +            }
        +          ]
        +        },
        +        "type": "array"
        +      },
        +      "stroke": {
        +        "properties": {
        +          "alignment": {
        +            "default": "inner",
        +            "enum": [
        +              "inner",
        +              "outer",
        +              "center"
        +            ],
        +            "type": "string"
        +          },
        +          "color": {
        +            "anyOf": [
        +              {
        +                "type": "string"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "opacity": {
        +            "default": 1,
        +            "maximum": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          },
        +          "style": {
        +            "default": "solid",
        +            "enum": [
        +              "solid",
        +              "dotted",
        +              "dashed",
        +              "mixed"
        +            ],
        +            "type": "string"
        +          },
        +          "width": {
        +            "default": 1,
        +            "minimum": 0,
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "color"
        +        ],
        +        "type": "object"
        +      },
        +      "type": {
        +        "const": "bool",
        +        "type": "string"
        +      },
        +      "width": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "x": {
        +        "type": "number"
        +      },
        +      "y": {
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "name",
        +      "x",
        +      "y",
        +      "width",
        +      "height",
        +      "boolType"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "properties": {
        +      "blendMode": {
        +        "type": "string"
        +      },
        +      "blocked": {
        +        "type": "boolean"
        +      },
        +      "constraintsH": {
        +        "enum": [
        +          "left",
        +          "right",
        +          "leftright",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "constraintsV": {
        +        "enum": [
        +          "top",
        +          "bottom",
        +          "topbottom",
        +          "center",
        +          "scale"
        +        ],
        +        "type": "string"
        +      },
        +      "frameId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "height": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "hidden": {
        +        "type": "boolean"
        +      },
        +      "id": {
        +        "format": "uuid",
        +        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +        "type": "string"
        +      },
        +      "layoutItem": {
        +        "properties": {
        +          "absolute": {
        +            "type": "boolean"
        +          },
        +          "alignSelf": {
        +            "enum": [
        +              "start",
        +              "center",
        +              "end",
        +              "auto",
        +              "stretch"
        +            ],
        +            "type": "string"
        +          },
        +          "column": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "columnSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "horizontalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "margin": {
        +            "properties": {
        +              "m1": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m2": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m3": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              },
        +              "m4": {
        +                "anyOf": [
        +                  {
        +                    "type": "number"
        +                  },
        +                  {
        +                    "properties": {
        +                      "token": {
        +                        "type": "string"
        +                      }
        +                    },
        +                    "required": [
        +                      "token"
        +                    ],
        +                    "type": "object"
        +                  }
        +                ]
        +              }
        +            },
        +            "type": "object"
        +          },
        +          "maxHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "maxWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minHeight": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "minWidth": {
        +            "anyOf": [
        +              {
        +                "type": "number"
        +              },
        +              {
        +                "properties": {
        +                  "token": {
        +                    "type": "string"
        +                  }
        +                },
        +                "required": [
        +                  "token"
        +                ],
        +                "type": "object"
        +              }
        +            ]
        +          },
        +          "row": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "rowSpan": {
        +            "exclusiveMinimum": 0,
        +            "maximum": 9007199254740991,
        +            "type": "integer"
        +          },
        +          "verticalSizing": {
        +            "enum": [
        +              "fill",
        +              "auto",
        +              "fix"
        +            ],
        +            "type": "string"
        +          },
        +          "zIndex": {
        +            "type": "number"
        +          }
        +        },
        +        "type": "object"
        +      },
        +      "mediaHeight": {
        +        "exclusiveMinimum": 0,
        +        "maximum": 9007199254740991,
        +        "type": "integer"
        +      },
        +      "mediaId": {
        +        "format": "uuid",
        +        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
        +        "type": "string"
        +      },
        +      "mediaWidth": {
        +        "exclusiveMinimum": 0,
        +        "maximum": 9007199254740991,
        +        "type": "integer"
        +      },
        +      "mtype": {
        +        "default": "image/png",
        +        "type": "string"
        +      },
        +      "name": {
        +        "minLength": 1,
        +        "type": "string"
        +      },
        +      "opacity": {
        +        "maximum": 1,
        +        "minimum": 0,
        +        "type": "number"
        +      },
        +      "parentId": {
        +        "default": "00000000-0000-0000-0000-000000000000",
        +        "type": "string"
        +      },
        +      "rotation": {
        +        "default": 0,
        +        "type": "number"
        +      },
        +      "type": {
        +        "const": "image",
        +        "type": "string"
        +      },
        +      "width": {
        +        "exclusiveMinimum": 0,
        +        "type": "number"
        +      },
        +      "x": {
        +        "type": "number"
        +      },
        +      "y": {
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "type",
        +      "name",
        +      "x",
        +      "y",
        +      "width",
        +      "height",
        +      "mediaId",
        +      "mediaWidth",
        +      "mediaHeight"
        +    ],
        +    "type": "object"
        +  }
        +]
    • Addedpenpot_add_variant
    • Addedpenpot_align_shapes
    • Addedpenpot_batch
    • Addedpenpot_checkpoint
    • Addedpenpot_clone_shapes
    • Addedpenpot_create_comment
    • Addedpenpot_create_comment_thread
    • Addedpenpot_create_component
    • Addedpenpot_create_file_snapshot
    • Addedpenpot_create_variant_group
    • Addedpenpot_delete_comment
    • Addedpenpot_delete_comment_thread
    • Addedpenpot_delete_file_snapshot
    • Addedpenpot_delete_page
    • Addedpenpot_delete_shapes
    • Addedpenpot_discard_checkpoint
    • Addedpenpot_distribute_shapes
    • Addedpenpot_find_shapes
    • Addedpenpot_get_comments
    • Addedpenpot_get_file_snapshot_data
    • Addedpenpot_get_shape
    • Addedpenpot_group_shapes
    • Addedpenpot_list_comment_threads
    • Addedpenpot_list_components
    • Addedpenpot_list_file_snapshots
    • Addedpenpot_list_pages
    • Addedpenpot_lock_file_snapshot
    • Addedpenpot_measure_text
    • Addedpenpot_rename_file_snapshot
    • Addedpenpot_rename_page
    • Addedpenpot_reorder_shapes
    • Addedpenpot_replace_text
    • Addedpenpot_resolve_comment_thread
    • Addedpenpot_restore_checkpoint
    • Addedpenpot_restore_file_snapshot
    • Addedpenpot_ungroup_shapes
    • Addedpenpot_unlock_file_snapshot
    • Addedpenpot_update_comment
    • Addedpenpot_update_shapes
    • Addedpenpot_upload_media
  2. 13 tool updatesv0.2.0
    • First observedpenpot_add_shapes
    • First observedpenpot_create_file
    • First observedpenpot_create_page
    • First observedpenpot_create_project
    • First observedpenpot_delete_file
    • First observedpenpot_delete_project
    • First observedpenpot_get_file_snapshot
    • First observedpenpot_list_files
    • First observedpenpot_list_projects
    • First observedpenpot_list_teams
    • First observedpenpot_load_token_config
    • First observedpenpot_rename_file
    • First observedpenpot_rename_project

TDQS

A3.5/5.0

Scored across 54 tools

Disambiguation5/5

Each tool targets a distinct operation or entity (file, page, shape, component, comment, checkpoint, media, etc.), with no overlapping purposes. Even similar tools like penpot_add_shapes and penpot_add_component_instance are clearly differentiated by their descriptions.

Naming Consistency5/5

All tools follow the penpot_{verb}_{noun(s)} pattern with imperative verbs (add, create, delete, get, list, update, etc.) and descriptive nouns. The naming is uniform and predictable across the entire set.

Tool Count2/5

With 54 tools, the set is significantly larger than the typical well-scoped range of 3-15. While the breadth of Penpot's feature set may justify many tools, the sheer number makes it unwieldy for an agent and suggests the server could benefit from consolidation or higher-level operations.

Completeness5/5

The tool surface covers the full lifecycle for files, pages, shapes, components, variants, comments, and media, plus supportive utilities like checkpoints, text measurement, and batching. There are no obvious gaps for the domain of headless Penpot operations.

Maintenance

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Unofficial local MCP bridge for reading the currently open Figma file through a Figma plugin. Provides tools to inspect selection, file info, and export selected nodes as SVG/PNG.
    MIT
  • F
    license
    B
    quality
    C
    maintenance
    MCP server that fetches Figma data (file trees, nodes, images, components, styles, variables) and serves as a foundation for Flutter code generation.
    13
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    A local MCP server that lets MCP clients inspect and edit the Figma document currently open in the Figma desktop app using a local bridge and development plugin.
    MIT