Skip to content
MCP: discovery

Documentation

Velloo documentation

Markdown for LLMs ↗

MCP: discovery

Read operations. List and fetch screens, boards and their flows, the component and snippet catalogue, canvas fidelity, the preview entry, the component sweep, the theme, annotations, notes, and comment threads, and query a screen tree with find_nodes.

Discovery operations are reads. With one exception they never mutate the design folder, and they are the recommended first calls of any session: list_components (index mode), get_theme, and list_boards, plus component_status before claiming an app component renders exactly. In a folder whose app has components of its own, preview_status comes first of all.

The exception is set_preview_entry, documented here beside the preview_status check it reruns rather than on the mutations page.

list_screens

List every screen in the design folder.

Arg Type Required Description
includeTree boolean no Embed each screen’s full tree: one round-trip instead of list_screens plus N get_screen calls. Default false

Returns { screens: [{ id, name, tree? }] }.

get_screen

Return the JSON for a single screen.

Arg Type Required Description
screenId string yes
mode string no "full" (default) returns the complete screen. "outline" returns { id, name, tree } where each node is stripped to ref (or snippet, or param), $id when present, a classSnippet of its className capped at 40 characters, and children. "jsx" returns the tree as the JSX compose reads
path locator no "jsx" only: the subtree to print. Default: the whole tree

Use mode: "outline" to scan a large screen before drilling in with inspect or @id locators. An unknown id returns ScreenNotFound.

mode: "jsx" is the read half of read, edit, and send back. It returns { screenId, path?, jsx }; edit the JSX and return it with compose in mode: "replace", passing the same path to replace only that subtree. Composing the printed JSX unchanged yields the same nodes.

{ "screenId": "home", "mode": "jsx", "path": "@pricing" }

A run of look-alike siblings prints as one .map over the values that differ, so a list reads as a list and one template edits every item. The fold is checked on every read: where the folded text would not compose back to the same nodes, the tree prints sibling by sibling instead. What has no JSX form (an instance’s $overrides, a node’s $emitAs) is listed in notInJsx; replacing the JSX drops those, so change them with their own operations. A path that resolves to nothing returns InvalidPath.

list_boards

List the folder’s boards in sidebar order.

Arg Type Required Description
includeFrames boolean no Embed each board’s frames and groups lists in one round-trip. Default false
includeArchived boolean no Also list archived boards. Default false; archived boards are hidden

Returns { boards: [{ id, name, frameCount, group?, archivedAt?, frames?, groups? }] }. group is the name of the sidebar group the board is filed under (absent means ungrouped); archivedAt appears only on archived boards.

get_board

Return one board’s full JSON: frames (placements of screens at chosen sizes), groups, and its flow arrows.

Arg Type Required Description
boardId string yes
mode string no "full" (default) returns the board. "flow" returns the flow spec alone

A board that has arrows carries them as stored in edges: [{ from, to, label?, state?, labelAt? }] (labelAt is canvas layout: where someone slid the label along its arrow), plus flow: the board read as a flow. A board with no arrows has neither field. mode: "flow" returns the same spec with boardId and name, and without the frames.

{
  "steps": [
    {
      "frame": "signup-1", "screen": "signup", "name": "Sign up", "route": "/signup",
      "states": [
        { "state": "loading", "frame": "signup-sending-1", "screen": "signup-sending", "when": "Submit" },
        { "state": "error", "frame": "signup-taken-1", "screen": "signup-taken", "when": "Email already taken", "after": "signup-sending-1" }
      ]
    },
    { "frame": "verify-1", "screen": "verify", "name": "Check your inbox" }
  ],
  "transitions": [{ "from": "signup-sending-1", "to": "verify-1", "when": "Account created" }],
  "starts": ["signup-1"],
  "ends": ["verify-1"],
  "note": "A state is its step in another condition, not another page: build each step as one route and one component that shows its states conditionally. Only a transition changes the route."
}
Field Meaning
steps Each step an arrow touches, in board order: its default frame, that frame’s screen, a name (the frame’s label, else the screen’s name), a route when the screen has one, and its states
steps[].states The step’s other frames: { state, frame, screen, when?, after? }. when is the arrow’s label. after names the state’s frame this one follows, when it doesn’t follow the default
transitions Arrows that move on: { from, to, when? }, ends as drawn
starts The steps the flow is entered at. Steps that lead round to each other are read as one loop, so a way back doesn’t hide the start
ends The frames nothing leaves, other than loading and error states. A flow can end at a state, and a frame nobody led on from appears here too
unconnected Frames no arrow touches, unless their screen is already in the flow at another size. Present only when there are some
gaps [{ step, missing, why }]. One rule today: a step with a loading state and no error state. Present only when there are some
note Present when any step has states: a state is its step in another condition, built on the step’s one route and one component. Only a transition changes the route

Frame ids are what connect_frames takes, and each frame carries the screen that emit_code takes. An unknown id returns BoardNotFound.

list_components

List the tag namespace that compose resolves: the app’s own components, library components, registered extensions, and snippets.

Arg Type Required Description
filter string no Case-insensitive substring match on ids (for snippets, the PascalCase tag)
mode string no "index" (default), "summary", or "full"
kind string no "repo", "library", "extension", or "snippet" narrows the result
unusedOnly boolean no List only snippets no screen reaches, directly or through another snippet. Implies kind: "snippet"

The three modes trade size for detail:

  • index returns families on shelves: { snapshotVersion, groups: [{ group, label, families: [{ id, pieces?, designModeNotes? }] }], totals: { components, families } }. A family’s sub-components are folded into pieces (Field lists FieldLabel, FieldDescription, FieldError), and extensions and snippets each get a shelf of their own. Status is reported only as exceptions: unavailableInDesign and notInstalledInApp list the ids that differ from the norm. Read this first.
  • summary: { snapshotVersion, components: [...] }, one entry per component with id, category, source, props (names only), designModeNotes, kind, availableInDesign, installedInApp, and importPath where known.
  • full: complete descriptors with prop types, enum values, and a working example. A prop that enumerates a huge value set (for example Icon.name, roughly 4,000 lucide names) is truncated to 40 values plus an enumValuesTruncated count.

Repo entries are your app’s own components, shelved first and grouped by source — Repo · this app's components, Repo · @mantine/core. Each carries importPath, exportName, member for a compound part, extracted props (with an inheritedProps count when some were folded away), acceptsChildren, styleProps, preview states, parts, and provenance naming where the app uses it. When a component reads data for itself, dataSources lists the hooks — props alone won’t fill a component that reads a store. An id that would collide with a provider component is qualified (Mantine.Button), and designModeNotes says why. In index mode a repo note reports each app’s preview-entry state, so check it before trusting the shelves.

The catalog also says what each component is:

  • What it is for. The doc comment above a component leads its designModeNotes: the one line its author wrote about when to use it.
  • Icons are counted, not listed. An app that uses sixty of a pack’s icons has not chosen sixty components. In index mode the repo note carries icons: [{ source, inUse, examples }] instead of a shelf entry each, and any icon the pack exports can be placed by name. kind: "repo" with mode: "summary", or a filter that names one, still lists them. An icon or a context provider carries role: "icon" or role: "provider".
  • Flat-named parts sit with their root. A PanelBody exported beside Panel from the same module is filed under Panel’s family, as a compound part would be.
  • One module is one component. A component the app imports as both @/components/panel and ./src/components/panel is listed once.

When the app has only pages, a router, or icons on its Repo shelves, the repo note says so, and no preview entry is needed.

Snippet entries use the snippet’s PascalCase tag as id and carry the persisted kebab-case id as snippetId. In full mode their props are the declared params, each with a derived required flag (true when the param has no default and isn’t optional). A snippet nothing reaches carries unused: true, and its designModeNotes say so in every mode.

installedInApp describes the host app only; false never blocks design. There is no install operation: emit_code returns componentsToInstall for the implementation handoff. Snippets have no separate listing either; they are the kind: "snippet" entries here, and get_snippet returns one definition.

component_status

Compile-check components for the browser canvas and report how each one actually renders, so a fidelity claim rests on a check rather than an assumption. Pass screen or ids, or neither for every component the app gives the design: the set check_components mounts.

Arg Type Required Description
screen string no Screen id: check the components that screen uses and whether it mounts
ids string[] no Component ids, as list_components returns them. Repo catalog ids are accepted
library string no With ids: the library to check. Default: the folder’s default library. A screen names its own

Each diagnostic carries the component id, a status, usually a note, and — when the status isn’t exact — a code and a remedy:

Status Meaning
exact Rendered from the app’s own source
adapted Rendered with a named canvas-safe adaptation
unstyled Rendered, but its stylesheet wasn’t loaded, so it can’t be reported exact
proxy Couldn’t render; the snippet named in $repo.proxy stands in
fallback Drawn from its server render inside the mount instead of the browser bundle. code says why: extension (one of the folder’s extensions) or static-fallback (its source didn’t compile)
unavailable Couldn’t render and has no proxy; a labelled frame stands in
server-rendered The adapter has no browser bundle, so the server render is what the screen shows (antd, Chakra UI, HTML)
unchecked Nothing here could establish a verdict: no bundler, or the library’s manifest failed to load. Don’t assume a fidelity
unknown Not a component this library or catalog has

An exact or adapted that no mounted frame has confirmed carries observed: false, and its note says it is a build check only: the source compiles and exports the component. Once a frame mounts, its runtime verdict replaces the build’s.

The codes are stable: missing-provider, render-threw, missing-export, compile-failed, server-only, resolve-failed, unstyled. missing-provider almost always means the preview entry needs another wrapper.

With screen, returns { library, screen, mounted, note?, diagnostics, errors }. Mounting is per component:

  • Library components the bundle can’t compile are drawn from their own server render inside the mount (fallback), so they cost their neighbours nothing. Only a screen where nothing would mount reports mounted: false.
  • Repository components each fall back alone to their proxy snippet or a labelled frame, leaving the rest real. The report also merges what the mounted frame found at runtime (render-threw, missing-provider, unstyled, missing-export) and sets runtimeChecked.

With ids, returns { library, usable, diagnostics, errors }. An id neither the library nor the repo catalog has is reported with status: "unknown" rather than as a canvas failure.

Where the server render is the whole answer (an adapter with no browser bundle, or a screen nothing on which mounts), the result also carries renderSource (bundled-library or velloo-primitives) and hostMount. hostMount.supported says whether the adapter can client-mount its own components at all; hostMount.mounted, on screen answers, whether this screen does. An adapter with a bundle answers per component instead.

Passing neither argument returns BadRequest; an unknown screen returns ScreenNotFound.

preview_status

Check the preview entry your app’s own components render inside on the canvas: the providers and global stylesheets they need. It proves the answer by mounting a real component in a headless browser rather than inspecting the file. Run it once per folder before the first screen.

Arg Type Required Description
app string no config.hostApps key; the default app when omitted
component string no Catalog id to probe. Default: the first one the app uses, preferring one with recorded props
probe boolean no Mount a component to verify. Default true

Returns { state, preview, recipes, appWrappers, appStylesheets, unloadedStylesheets?, components, probe?, suggestedPreviewEntry?, warnings?, next }.

  • state is absent (no entry and components need one), valid (a component mounted cleanly), incomplete (it mounts, but the entry never imports one of the app’s global stylesheets, listed in unloadedStylesheets, so the app’s own classes render as nothing), or failing (the probe found a problem).
  • preview describes what’s in force: kind is file (your preview.tsx, with its path), recipe (a built-in wrapper for a known library), or none.
  • appWrappers and appStylesheets are what the app’s own entry point uses — the raw material for writing an entry. codeProps flags a wrapper prop that was an expression rather than a literal.
  • probe reports the mount: component, mounted, status, code, note, remedy, plus previewError when the entry itself threw and up to five consoleErrors. An inconclusive note means the component threw without props the app normally supplies as code; the prior state is retained. Probe another component before treating the entry as verified or broken.
  • suggestedPreviewEntry is a draft to adapt, present whenever the current entry isn’t a verified file.
  • next is the concrete step to take from this state.

set_preview_entry

Write the design folder’s preview entry and verify it by mounting a real component. Returns the same report as preview_status.

Arg Type Required Description
source string no TSX/JSX module source, up to 100,000 bytes. Omitted, the suggested entry is written
app string no config.hostApps key. Writes preview.<app>.tsx; omitted writes preview.tsx

With no source, set_preview_entry {} writes the entry preview_status suggests: the app’s own entry rewritten for a canvas, with its stylesheets, its providers in their nesting order, an in-memory router in place of the real one, a query client of its own, and auth providers left out. For most apps that is the answer. When a component still needs something, such as a provider a layout adds or seeded data, read preview.tsx, extend it, and send it back as source. With no app to suggest from, omitting source returns BadRequest.

The module’s default export receives { children, colorScheme, theme, recipeTheme } and returns the providers and fixtures your components need, with the app’s global stylesheets imported at the top. Source without a default export is rejected with BadRequest, as is an unknown app.

Keep the entry free of network calls and credentials: it runs in your local browser during design, and it’s a fixture rather than a bootstrap of the real app.

check_components

Mount every component the app gives the design, at the props its call sites pass, and report which can be trusted. This is the sweep that setup runs. It covers the app’s own components and, where the library is the app’s own files (a shadcn components/ui), the library components those files back. A library file that exports only the app’s own renamed component (a badge.tsx with StatusChip and no Badge) backs no library component; its exports are checked as the app’s own. Run it once the theme is imported and preview_status says valid, and again after changing either.

Arg Type Required Description
app string no config.hostApps key; the default app when omitted
ids string[] no Component ids to check. Default: all of them, the app’s own files first
dark boolean no Render the dark theme. Default light
image boolean no Return the contact sheets. Default true

Returns { mounted, checked, ok, problems, inconclusive?, notChecked?, partsNote?, sheets, next }, plus one contact-sheet image per render: twenty components to a sheet, up to 120 components a call. Pass ids to check the rest, or to see a few at full size.

  • ok lists the ids that render from the app’s source.

  • problems is [{ id, status, code?, note?, remedy?, flags?, detail?, size? }]. An entry with a status such as unavailable, fallback, or unstyled carries the mount’s own code and remedy, as component_status reports them. An entry with flags rendered, but not as itself:

    Flag Meaning
    blank It drew nothing. A dialog or drawer that is closed until opened does that by design
    thin It drew a sliver
    variantsIdentical Two values of a variant prop drew the same thing
  • inconclusive went without props it needs: code its call site passes (rows={data}), or a required prop no call site gave as data. It says nothing about the preview entry.

  • mounted is false, with a note, when a sheet’s components never mounted. Fix the preview entry first.

  • notChecked lists the ids past the per-call limit, and partsNote counts parts of other components that were not mounted alone.

  • next is the step to take from this result.

A component with a variant prop is mounted twice, differing in that prop alone. variantsIdentical on several components at once is usually one cause: a stylesheet the preview entry never loaded, or a colour the app’s Tailwind config defines and the theme lacks. Fix the cause, then re-run with ids for the ones that were wrong.

The report judges what was mounted and measured, not whether it is legible. Look at the sheets as well. An app with no components of its own returns checked: 0 and a note saying so. An unknown app or id returns BadRequest.

get_snippet

Return the full JSON for a single snippet: id, name, params (each with the derived required flag), and the body tree.

Arg Type Required Description
snippetId string yes The kebab-case snippet id

An unknown id returns SnippetNotFound.

get_theme

Return a theme’s token tree.

Arg Type Required Description
theme string no Named theme to read. Default "default"

Returns the theme’s tokens plus two derived fields: typeScale, what the default typeset computes to per type role, and customCss, the folder’s custom CSS. When a design-system document is followed, the result also includes designSystem: { path, note }. Read that file’s current prose before composing or reviewing; it carries rules tokens cannot express. Edit through the theme operations, not per-node sizes.

list_annotations

List annotations on a screen: designer-authored guidance plus any the agent pinned.

Arg Type Required Description
screenId string no Omitted, every screen’s annotations are listed, each with its screenId

Returns { annotations }. Each carries an author, a target: { locator }, a markdown body, and a resolved path, null when the targeted node has since been removed (treat dangling annotations as low-priority; the note is stale). User-authored annotations are read-only to the agent. Agent annotations are written with the operations on the Lifecycle page.

list_notes

List the markdown notes on one board, or on every board.

Arg Type Required Description
boardId string no Omitted, every board’s notes are listed, each with its boardId

Returns { notes }. Free notes live in board coordinates (the same space as frame x/y); an attached note carries attachment: { frameId, screenId, locator } naming the node it anchors to. Writable via add_note / update_note / remove_note on the Lifecycle page.

list_comment_threads

List visual feedback threads. Every open thread is a change the user expects.

Arg Type Required Description
boardId string no Narrow to one board. Default: every board in the folder
status string no "open" (default), "resolved", or "all"
scope string no "local", "shared" (threads left on a published link), or "all" (default)

Returns { threads }. A thread’s anchor reports its attached path, or that it is stale when the original node is gone.

get_comment_thread

Read one complete thread, including its anchor and replies.

Arg Type Required Description
threadId string (UUID) yes

Returns { thread }.

update_comment_thread

Act on a thread. Pass reply, status, or both; replying with what changed and resolving in one call is the normal close-out.

Arg Type Required Description
threadId string (UUID) yes
reply string no Agent message to post, 1–4000 characters. Posted before any status change
status string no "resolved", "open", or "deleted". Deletion is permanent; only on the user’s explicit request

Returns { thread }. Passing neither reply nor status returns BadRequest; comment-store failures return CommentStore with a code. The full loop is in the velloo://guide/comments resource.

find_nodes

Query a screen tree for matching nodes. All filters AND together. Accepts snippet:<id> to search a snippet body.

Arg Type Required Description
screenId string yes
ref string no Exact component $ref, e.g. "Button"
snippetId string no Exact $snippet id for snippet instances
id string no Exact $id anchor
classContains string no Substring of props.className
text string no Case-insensitive substring of the node’s text
prop string no Prop key that must be present
propValue any no With prop: strict-equal value match
limit number no Max matches; default 50
{ "screenId": "home", "ref": "Icon", "prop": "name", "propValue": "Github" }

Returns { matches: [{ path, kind, ref?, id?, className?, textPreview?, childCount }], total }. kind is component, snippet, or param, and total counts matches before limit. Use it to locate targets for the path-accepting operations without fetching and walking the whole tree.

On the guided façade, type, component, or tag is read as ref, and the result says so.

Assets and captures

list_assets lists the files in the folder’s assets/ directory, and list_captures / get_capture read page captures recorded by start_capture_session. Both families are documented with the rest of the visual tooling on Visual, assets, and batch.