Skip to content
Agentmart
Submit a tool

cnstudio

Edit a CnStudio-designed UI (components, pages, slots, props) through the studio's MCP tools instead of hand-editing .studio/site.json.

Skill by cnstudio-io

IndexedApache-2.0
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/cnstudio-io-cnstudio

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Edit a CnStudio-designed UI (components, pages, slots, props) through the studio's MCP tools instead of hand-editing .studio/site.json. Use in any project with a .studio/ directory whenever asked to add/move/remove components, fill slots, change props, or create design pages.

Use cases: design, writing, agent-building

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/cnstudio

What's inside

Skill name
cnstudio
Description
Edit a CnStudio-designed UI (components, pages, slots, props) through the studio's MCP tools instead of hand-editing .studio/site.json. Use in any project with a .studio/ directory whenever asked to add/move/remove components, fill slots, change props, or create design pages.
License
Apache-2.0
Instructions
171 lines in SKILL.md · view on GitHub

Before installing

  • Instructions call the network

Files (1)

  • SKILL.md

SKILL.md (first lines)

# CnStudio design edits

A CnStudio project's UI is a **design document** (`.studio/site.json`)
rendered live on the studio canvas and codegen'd into real `.tsx` page modules
(the `pages` dir in the project's `studio.config.js` / vite `cnstudio()`
options). Do NOT hand-edit `.studio/site.json` or generated page files — drive
the studio's MCP tools. Their edits are live on the canvas, undoable,
autosaved, and page modules regenerate automatically.

## Connecting

This plugin registers the `cnstudio` MCP server at `http://127.0.0.1:4923/mcp`
— the port the CnStudio VS Code extension listens on (setting
`cnstudio.mcpPort`, default 4923). Prefer the registered `mcp__cnstudio__*`
tools.

If they fail to connect:

1. Read `.studio/mcp.json` — the extension writes the CURRENT URL there when a
   project opens (it falls back to an ephemeral port when 4923 is taken). If
   the URL differs from the registered server's, talk to it directly with the
   curl recipe below.
2. No `.studio/mcp.json` / connection refused ⇒ the studio isn't running. Ask
   the user to open the project in VS Code with the CnStudio extension. Only
   if they explicitly decline, fall back to careful `.studio/site.json` edits
   (the extension watches the file and reloads external changes when it
   starts) followed by the project's generate script (`npx cnstudio generate`).

### Curl fallback (non-default port)

MCP JSON-RPC over Streamable HTTP; initialize once and keep the session id:

```bash
URL=$(jq -r .url .studio/mcp.json)
SID=$(curl -si "$URL" -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"claude-code","version":"1.0"}}}' \
  | tr -d '\r' | awk -F': ' 'tolower($1)=="mcp-session-id"{print $2}')
curl -s "$URL" -H "mcp-session-id: $SID" -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","method":"notifications/initialized"}' > /dev/null
mcp() { curl -sN "$URL" -H "mcp-session-id: $SID" -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d "{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"$1\",\"arguments\":$2}}" \
  | sed -n 's/^data: //p' | jq -r '.result.content[0].text'; }
```

## The design model

- A **node** is `{ type, props, children }`. `type` names either a design
  component (bare name, may be path-like: `page/calendar`) or a registered
  code component (registry id: `@/components/ui/button#Button`).
- **Prop values are JS expression source strings**: `"'ghost'"` is the literal
  string ghost, `"$props.title"` / `"$ctx.shell.menu"` are bindings, plain
  `42` / `true` are literals. Event props (`onX`) follow the same rule — the
  expression must evaluate to a function: `"$ctx.shell.logout"` binds the
  handler itself; inline logic is an arrow,
  `"(e) => { $ctx.calendar.id = Number(e.target.value) }"`.
- **Slots**: a component declares where instance children render with Slot
  markers — `{ "type": "Slot", "props": { "name": "main" } }`. Every slot is
  NAMED. An instance's children route to a slot via the reserved `slot` prop.
  `slot` and a marker's `name` are RAW literal strings (`"main"`, NOT
  `"'main'"`) — the one exception to the expression rule.
- Slot **forwarding**: a marker that is itself a fill
  (`{ name: "outer", slot: "inner" }`) declares one slot and routes it onward.

## $ctx: the data-binding store (cnstudio ≥ 0.5)

`$ctx` is one app-wide store of **namespaces** — readable and WRITABLE from
any expression. Reads subscribe (the node re-renders when the keys it read
change); assignments mutate the store: `"$ctx.calendar.id = 5"` in a handler
updates every reader, wherever it sits in the tree. Position governs
lifecycle only, never visibility.

- **Top-level `$ctx` keys are namespaces, never data** — `$ctx.shell.isActive`,
  not `$ctx.isActive`. Reading an undefined namespace auto-vivifies an empty
  object (`$ctx.anything` is never undefined), so guard KEYS, not namespaces:
  `"$ctx.calendar.timelines?.length > 0"`, `"$ctx.calendar.id ?? ''"`.
- **State lives in the design doc, not in provider components.** UI state is
  just `$ctx` keys the page's handlers assign (`"(v) => { $ctx.calendar.zoom
  = v[0] }"`). Compound and nullish assignment work: `"$ctx.calendar.id ??=
  list[0]?.id ?? null"`.
- **The reserved `useEffect` prop** is how a page owns its data loading: a
  function-valued expression on any node, run as a React effect on mount
  (codegen hoists it to a real `useEffect(...)`; the canvas runs it per node).
  Typical page-root effect:
  `"async () => { try { $ctx.x.list = await $ctx.api.get('/api/x') } catch (e) { $ctx.x.error = String(e) } }"`
  Effects on conditionally-rendered nodes still run on page mount, and a
  `Loop`'s effect runs once — not per item.
- **Code components publish constants/actions** the design can't express by
  defining their namespace at module scope:
  `ctxSlice("zoom", { min: ZOOM_MIN, max: ZOOM_MAX })` (from
  `@cnstudio-io/cnstudio/react-web`) — seeding is idempotent and never
  clobbers written keys. A conventional root `api` slice (`$ctx.api.get/post/…`)
  is how effects and handlers reach the backend; check the project for it.
- The canvas seeds `$ctx` from `.studio/dev-context.js` (preferred) or
  `.studio/dev-context.json`, namespaced the same way — see below.

## Sample data on the canvas (dev-context)

The canvas is a real render of the page, so it needs `$ctx` filled the way the
app would fill it. Two files under `.studio/` do this (the `.js` form
supersedes the `.json` when both exist):

- **`.studio/dev-context.js`** — a module whose DEFAULT EXPORT is the example
  `$ctx` object. Because it's a module (served through Vite: `@/` aliases
  work), it can also `import` the project's slice-defining provider modules
  for their side effects, so the REAL function slices (`$ctx.api`,
  `$ctx.helpers`, …) exist on the canvas:

  ```js
  import "@/components/providers/api-provider"     // real $ctx.api on canvas
  export default {
    shell: { crumbs: ["home"], menu: { leaves: [], groups: [] } },
    notifications: { loaded: true, unreadCount: 2, all: [ /* items */ ] },
  }
  ```

- **`.studio/dev-context.json`** — the data-only fallback (JSON: no functions).

**Seed-aware effects.** A page-root `useEffect` that always fetches will
clobber the canvas's sample data (and error when the canvas isn't

From cnstudio-io/plugins, Apache-2.0.

Signals

Stars
0
Forks
0
Open issues
0
Last commit (this folder)
Jul 3, 2026 (3 months ago)

From GitHub (fetched Oct 24, 2026).

Details

License
Apache-2.0
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from cnstudio-io

Cite this page

cnstudio skill. Agent Mart. https://agentmart.dev/skills/cnstudio-io-cnstudio (last updated 2026-10-24)