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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/docs/guides/mcp-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ The Subframe MCP server exposes tools across several categories. Most read tools
| Tool | Description | Key inputs | Returns |
| --- | --- | --- | --- |
| `list_pages` | List all pages with the flow each belongs to | `projectId` | Array of `id`, `name`, `url`, `lastModifiedAt`, `flowId`, `flowName` |
| `get_page_info` | Generated React/Tailwind code for a page | `id`/`name`/`url`, `projectId` | `id`, `name`, `lastModifiedAt`, `files` |
| `get_page_info` | Generated React/Tailwind code for a page. Pass `includeNodeIds: true` to get a `data-node-id` on every element for use with `edit_page` | `id`/`name`/`url`, `projectId`, `includeNodeIds?` | `id`, `name`, `lastModifiedAt`, `files` |
| `design_page` | Generates 1-4 page variations as an asynchronous background job. Variations land as pages in a flow as they finish | `description`, `variations`, `flowName`, `projectId`, `codeContext?`, `additionalReferences?` | `flowId`, `flowUrl`, `jobId` |
| `edit_page` | Apply a targeted change to an existing page; applied immediately | `id`/`name`/`url`, `description`, `projectId`, `additionalReferences?` | `pageUrl` |
| `edit_page` | Apply a targeted edit to one node of an existing page — `replace` the node and its subtree, `insert-above`/`insert-below` a new sibling, or `delete` it. Call `get_page_info` with `includeNodeIds: true` first to get each element's `data-node-id`. Applied immediately | `id`/`name`/`url`, `nodeId`, `operation`, `code`, `projectId` | `pageUrl`, `appliedCode?`, `warnings?` |
| `delete_page` | Delete a page, removing it from its flow and stripping prototype actions referencing it. Refuses by default if referenced in other pages. Use `force: true` to delete anyway | `id`/`name`/`url`, `projectId`, `force?` | `deletedId`, `deletedName`, `references` |

### Components
Expand All @@ -59,9 +59,9 @@ Snippets are small, standalone bits of UI typically embedded inside design docum
| Tool | Description | Key inputs | Returns |
| --- | --- | --- | --- |
| `list_snippets` | List all snippets | `projectId` | Array of `id`, `name`, `url`, `lastModifiedAt` |
| `get_snippet_info` | Generated code for a snippet | `id`/`name`/`url`, `projectId` | `id`, `name`, `lastModifiedAt`, `files` |
| `get_snippet_info` | Generated code for a snippet. Pass `includeNodeIds: true` to get a `data-node-id` on every element for use with `edit_snippet` | `id`/`name`/`url`, `projectId`, `includeNodeIds?` | `id`, `name`, `lastModifiedAt`, `files` |
| `design_snippet` | Design a new snippet | `description`, `name?`, `projectId`, `codeContext?`, `additionalReferences?` | `snippetId`, `snippetUrl` |
| `edit_snippet` | Edit an existing snippet | `id`/`name`/`url`, `description`, `projectId`, `additionalReferences?` | `snippetUrl` |
| `edit_snippet` | Apply a targeted edit to one node of an existing snippet (same model as `edit_page`). Call `get_snippet_info` with `includeNodeIds: true` first | `id`/`name`/`url`, `nodeId`, `operation`, `code`, `projectId` | `snippetUrl`, `appliedCode?`, `warnings?` |
| `delete_snippet` | Delete a snippet. Any design document embeds are removed automatically | `id`/`name`/`url`, `projectId` | `deletedId`, `deletedName` |

### Flows
Expand Down
2 changes: 1 addition & 1 deletion plugins/claude/subframe/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "subframe",
"description": "Design and build UIs with Subframe. Create pixel-perfect React + Tailwind pages using your design system, explore design variations, and implement with business logic.",
"version": "1.0.19",
"version": "1.0.20",
"author": {
"name": "Subframe"
},
Expand Down
25 changes: 17 additions & 8 deletions plugins/claude/subframe/skills/design/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Subframe's design AI is far more accurate when the call carries raw code than wh

Group related files in adjacent blocks (component + stories + CSS module).

**Don't paste what the AI already has.** For `edit_component`, `edit_page`, and `edit_snippet`, the current Subframe code is already on the server — don't echo it back. Read it with `get_component_info` / `get_page_info` / `get_snippet_info` so your description can target exactly what differs. Prefer the most efficient form: plain language when the change doesn't depend on any code the AI hasn't seen ("change padding from 4 to 6, add a hover state"); otherwise reference code scaled to what's needed — a targeted diff or code snippet when the Subframe code already resembles the target, a full file when handing over a wholesale target, a sibling pattern, or related types.
**Don't paste what the AI already has.** For `edit_component`, the current Subframe code is already on the server — don't echo it back. Read it with `get_component_info` so your description can target exactly what differs. Prefer the most efficient form: plain language when the change doesn't depend on any code the AI hasn't seen ("change padding from 4 to 6, add a hover state"); otherwise reference code scaled to what's needed — a targeted diff or code snippet when the Subframe code already resembles the target, a full file when handing over a wholesale target, a sibling pattern, or related types. (`edit_page` and `edit_snippet` use a different, node-targeted model — see their own sections.)

**Soft cap on very large files (~500 LOC combined).** When trimming, keep verbatim:

Expand All @@ -113,13 +113,13 @@ Include in the description:
4. **Prop-type files** if types are defined separately (e.g. a `types.ts`).
5. **Theme tokens the component references** — pull the relevant rows from the codebase theme (`tailwind.config.*`, CSS variables) and from `get_theme` so the AI keeps roles aligned.

### For snippet design (`design_snippet`, `edit_snippet`)
### For snippet design (`design_snippet`)

See the snippet tool sections below for `codeContext` / `additionalReferences` parameter use. `edit_snippet` has no `codeContext` parameterpaste outside reference code into `description`.
See the `design_snippet` section below for `codeContext` / `additionalReferences` parameter use. `edit_snippet` takes no grounding contextit's a node-targeted edit (see its section below).

### For page design (`design_page`, `edit_page`)
### For page design (`design_page`)

See [Preparing codeContext](#preparing-codecontext) below for the page-specific rule about leaving Subframe component references as-is and inlining everything else. The general grounding rules above (default to full files, paste styles verbatim, soft cap with trimming) layer on top.
See [Preparing codeContext](#preparing-codecontext) below for the page-specific rule about leaving Subframe component references as-is and inlining everything else. The general grounding rules above (default to full files, paste styles verbatim, soft cap with trimming) layer on top. (`edit_page` is a node-targeted edit, not a `codeContext`-grounded design call — see its section below.)

### For theme edits (`edit_theme`)

Expand All @@ -131,7 +131,7 @@ See [Preparing the edit_theme description](#preparing-the-edit_theme-description

**Surface job status to the user.** When you kick off a design, tell them you've started ("Designing your settings page in Subframe…") and present the URL. When the job finishes, tell them a relevant message like "✓ Variations are ready to review.". The user already sees live progress in the editor, but they should not have to go to the editor to know when the design is done.

**Present the URL verbatim** — don't strip query parameters. The URLs returned by `design_page`, `design_component`, and `edit_component` (and the inline-AI tools like `edit_page`) embed a conversation ID that opens the AI chat panel preloaded with the conversation that produced the design. That gives the user reasoning, intermediate steps, and a place to keep iterating with the AI directly — far more useful context than the bare resource URL.
**Present the URL verbatim** — don't strip query parameters. The URLs returned by `design_page`, `design_component`, and `edit_component` embed a conversation ID that opens the AI chat panel preloaded with the conversation that produced the design. That gives the user reasoning, intermediate steps, and a place to keep iterating with the AI directly — far more useful context than the bare resource URL.

**When to call `wait_for_jobs`:**

Expand Down Expand Up @@ -249,7 +249,16 @@ When designing multiple related pages (flows, CRUD, etc.):

### `edit_page` — targeted edits to an existing page

Use `edit_page` for targeted changes to a specific Subframe page. Pass `id`, `name`, or `url` (call `list_pages` first if you need to find it) plus a `description` of the change. Follow the [Grounding](#grounding-design-calls-in-real-code) rules — the AI already has the current page code, so only paste outside reference code when the change depends on something the AI can't see; pass `additionalReferences` to point it at related Subframe pages, snippets, or components. The edit applies immediately; present the returned `pageUrl` to the user.
Use `edit_page` to change one node of an existing Subframe page. It's a structured edit, not a prose description:

1. Call `get_page_info` with `includeNodeIds: true` to get the page JSX with a `data-node-id` on every element.
2. Pick the `nodeId` you want to change.
3. Choose an `operation`: `replace` (swap the node and its subtree), `insert-above` / `insert-below` (add your `code` as a new sibling), or `delete` (remove it).
4. Pass the new or replacement subtree as `code` — a JSX fragment with a single root element. Don't include `data-node-id` attributes (new nodes are assigned ids automatically); leave `code` empty for `delete`.

Identify the page with `id`, `name`, or `url` (call `list_pages` first if you need to find it). The edit applies immediately and returns `pageUrl`, `appliedCode` (the canonical code after parsing — compare it to what you sent to confirm nothing was dropped or normalized), and any parser `warnings`. Call `get_page_info` again to see the updated code and node ids. The user can undo via page version history in the Subframe editor.

`code` must be valid Subframe JSX: a single root element and static markup only — no `.map()`, hooks, state, or conditional rendering (these are rejected, not silently dropped), and no `<html>`/`<body>` wrappers. Style with Tailwind `className`s; inline `style={{…}}`, event handlers, and most `data-*` attributes are dropped. The `includeNodeIds: true` output is already in this shape — use it as your template.

#### When to use `edit_page` vs `design_page`

Expand Down Expand Up @@ -330,7 +339,7 @@ Returns `snippetId` and `snippetUrl`. Embed the snippet in a design document wit

### `edit_snippet` — change an existing snippet

Same shape as `edit_component` but for snippets. Use when the embedded example needs to evolve alongside the component it documents.
Same node-targeted model as `edit_page`, but for snippets. Call `get_snippet_info` with `includeNodeIds: true` to see each element's `data-node-id`, then pass `nodeId`, an `operation` (`replace` / `insert-above` / `insert-below` / `delete`), and a single-root `code` fragment (same JSX constraints as `edit_page`). The edit applies immediately and returns `snippetUrl`, `appliedCode`, and any `warnings`. Use when the embedded example needs to evolve alongside the component it documents.

## Design documents

Expand Down
Loading