Install
$ agentstack add mcp-vicmaster-framesmith ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
framesmith
[](https://opensource.org/licenses/MIT) [](https://github.com/vicmaster/framesmith/releases) [](https://modelcontextprotocol.io)
An open-source MCP server that turns your AI coding agent into a capable UI designer. It gives the agent a visual canvas, a library of vetted design patterns, and a quality bar it must clear before showing you anything — so you review a real, non-slop design in the browser and agree on it before any framework code is written.
Contents: [What it does](#what-framesmith-does) · [Capabilities](#capabilities-at-a-glance) · [Viewer](#viewer) · [Installation](#installation) · [Tools](#tools) · [Usage Example](#usage-example) · [Workflow](#workflow) · [Development](#development)
> Above: the framesmith viewer, showing the built-in pattern library — 11 vetted page archetypes an agent starts from, each carrying its live quality score (all 100 here). Workspaces and projects sit in the sidebar; you browse canvases like design files.
MCP Client → stdio → framesmith server
↓
Scene Graph (in-memory JSON tree)
↓
HTML/CSS Renderer (inline styles)
↓
Puppeteer (headless Chromium → PNG)
What framesmith does
Left to itself, an AI agent tends to produce UI that looks AI-generated — generic spacing, no icons, a purple gradient by default, placeholder text that reads like placeholder text. framesmith closes that gap by moving design before code and holding it to a bar:
- Start from taste, not a blank canvas. The agent stamps one of 11 vetted page patterns (dashboard, auth, pricing, settings, onboarding, and more) — each regression-tested to score > 95 with zero cliché tells across every theme — then adapts it. A blank canvas is where slop comes from; a pattern is a non-slop starting point. (
list_structures/apply_structure) - Use the whole toolkit — like a real UI does. Real icons (Lucide + Material Symbols), fonts resolved by name, real input controls (
toggle/checkbox/radio/select), reusable components, and layered design tokens — never faked with Unicode glyphs or stray ellipses. - Evaluate, then self-correct.
canvas_evaluatescores the design across six craft categories plus a cliché-tell detector, and returns aREADY/NOT READYdirective. The agent resolves every warning and tell and only presents once it'sREADY— polishing to the bar is the agent's job, not yours. - Review in the browser, own the output. You browse canvases like design files, with a live quality inspector and design-system panel. Designs persist as open JSON checked into your repo — no proprietary format, no lock-in — and can be re-imported from shipped HTML to keep the design honest.
Capabilities at a glance
| Area | What you get | |------|--------------| | Rendering | Scene graph → HTML/CSS → Puppeteer PNG · responsive breakpoints · gradients, shadows, blur, glassmorphism · SVG paths · animations | | Pattern library | 11 vetted page archetypes + 5 component scaffolds, all scoring > 95 with zero cliché tells; taxonomy axes + a diversification signal so successive screens vary | | Quality & taste | canvas_evaluate (6 categories + cliché tells) with a READY/NOT READY directive · canvas_autofix (mechanical fixes) · optional vision-model rubric critique + canvas_revise | | Design systems | Layered $tokens (workspace ▸ project ▸ canvas) · style presets · DESIGN.md import | | Primitives | Lucide + Material Symbols icons · Google Fonts by name · real form controls · components with instance overrides | | Import from code | canvas_import_html / canvas_import_url — token-mapped, structure-reconstructed · canvas_sync_from_url drift detection | | Viewer | Browser gallery + detail view · quality inspector (score, issues, click-to-highlight) · design-system token panel · point-and-tell feedback (Comment mode + Feedback tab) | | Open by design | MIT · plain HTML/CSS · open JSON you own in your repo · works with any MCP-compatible client |
Viewer
Run npx -p framesmith framesmith-viewer to start the standalone browser viewer (default port 3001). Open any canvas to review it at multiple breakpoints, compare them side-by-side, inspect the underlying JSON, or archive / delete.
> Above: a canvas in the detail view with the Quality inspector on the right — the same canvas_evaluate score the agent sees, with per-category bars and the issue list; the tabs along the top switch to the Design-system and Feedback panels. The toolbar exposes breakpoint previews, Compare, Fit, JSON, a Comment toggle for point-and-tell feedback, and lifecycle actions.
Quality panel. The canvas detail view shows a read-only quality inspector on the right: the heuristic canvas_evaluate score (0–100), per-category bars, and the issue list — each cliché tell with its category · tell badge, severity, and suggestion. Issues that canvas_autofix can resolve carry an auto-fixable tag, and clicking any issue highlights its node in the live preview. Every gallery card also shows a color-coded score badge so weak canvases stand out at a glance. The score matches what your agent sees over MCP (same fast-mode evaluation, genre-relaxed by the canvas's preset) — it's computed for display only and never written back.
> Above: point-and-tell in action — Comment mode is on, the clicked element is outlined, the popover's breadcrumb re-scopes the anchor, and the Feedback tab tracks open comments plus the agent's replies to resolved ones.
Feedback panel. Point-and-tell: toggle Comment in the toolbar and click any element in the preview to leave a note for the agent — a breadcrumb in the popover re-scopes the anchor from the exact element up to its card, section, or the whole page. The Feedback tab lists open comments (badge = open count) with click-to-highlight, lets you resolve or delete them, and shows the agent's resolution note as a reply once it has addressed the item. Comments persist on the canvas itself (metadata.feedback — git-diffable in bound repos) and the agent reads them over MCP via get_feedback; open feedback blocks the agent from presenting, same as open quality issues.
Design-system panel. A second inspector tab shows the canvas's effective design tokens — color swatches, type scale, spacing, and radius — resolved through the full workspace ▸ project ▸ canvas inheritance chain. Each section notes its dominant source layer, and any token resolving from a different layer is tagged (canvas / project / workspace) so you can see at a glance what a given canvas customized versus inherited.
Designs are authored through MCP tool calls from your AI assistant — in the viewer you review, comment, and manage lifecycle; you don't edit nodes. Files persist to ~/.framesmith/canvases/ so the viewer keeps showing them across sessions.
Installation
No clone or build needed — register framesmith with your MCP client via npx (requires Node 20+).
Claude Code
claude mcp add framesmith -- npx -y framesmith
Codex
Add to ~/.codex/config.toml:
[mcp_servers.framesmith]
command = "npx"
args = ["-y", "framesmith"]
Cursor
Add to ~/.cursor/mcp.json (or per-project .cursor/mcp.json):
{
"mcpServers": {
"framesmith": {
"command": "npx",
"args": ["-y", "framesmith"]
}
}
}
Windsurf
Add to ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"framesmith": {
"command": "npx",
"args": ["-y", "framesmith"]
}
}
}
VS Code + MCP extension
Add to .vscode/mcp.json (project-scoped) or your global MCP settings:
{
"servers": {
"framesmith": {
"command": "npx",
"args": ["-y", "framesmith"]
}
}
}
Any other MCP-compatible client
framesmith speaks standard stdio MCP. Point your client at npx -y framesmith using whatever config shape your client expects.
> Optional: set FRAMESMITH_VIEWER_URL=http://localhost:3001 in the MCP server env to pin it to a long-lived standalone viewer process — see [Running the viewer](#running-the-viewer).
Build from source (for development)
git clone https://github.com/vicmaster/framesmith.git
cd framesmith
npm install
npm run build
# then point your client at: node /path/to/framesmith/dist/index.js
Tools
init
One-call onboarding — the recommended first call each session, and safe to run repeatedly (idempotent). Binds the current repo if it isn't already (canvases become checked-in JSON under .framesmith/), ensures the convention projects exist, and returns the live state you need to start working.
| Param | Type | Description | |-------|------|-------------| | dir | string? | Directory to bind / detect. Defaults to the nearest git repo root above the server working directory. | | workspaceName | string? | Name for the workspace when binding fresh. Defaults to the repo folder name. | | projects | string[]? | Projects to ensure exist (default: ["Foundations", "UI"]). Existing projects are never removed, so it's safe for adding feature/area projects like Onboarding. |
Returns the bound workspace + project IDs (binding re-keys IDs to repo-* — use the ones init returns), the on-disk layout, the workspace-layer token count, a workflow cheatsheet, the current gotchas, the framesmith://guidelines URI, and the viewer URL. If any canvas in the workspace has open point-and-tell comments, the result also carries openFeedback: { total, note } — a nudge to run get_feedback before doing anything else. It does not seed design tokens — set those at the workspace layer with workspace_set_design_system. The default Foundations project is just a canvas that visualizes the workspace tokens (which is where the design system actually lives).
canvas_create
Create a new canvas. If projectId is omitted, it lands in the built-in Untitled project of the Personal workspace.
| Param | Type | Description | |-------|------|-------------| | name | string? | Canvas name | | projectId | string? | Target project. Defaults to the built-in Untitled project. See project_list. |
The response also carries a diversification signal for the target project: the recently-built structures (newest first) and a hint to differ on at least one taxonomy axis, so successive canvases don't converge on the same layout. It's advisory — never blocking.
canvas_list
List canvases. Excludes archived canvases by default. A row carrying openFeedback: n has open point-and-tell comments waiting — read them with get_feedback before working on that canvas.
| Param | Type | Description | |-------|------|-------------| | projectId | string? | Scope to one project | | includeArchived | bool? | Include archived canvases (default false) |
Returns [{ id, name, createdAt, lastModified, projectId, archived, openFeedback? }] — openFeedback (a count) is present only when > 0.
canvas_move / canvas_archive / canvas_unarchive / canvas_delete
Canvas lifecycle. canvas_move reassigns a canvas to a different project. canvas_archive sets a soft-delete flag (canvas stays on disk, hidden from default canvas_list); canvas_unarchive clears it. canvas_delete removes the canvas and its file permanently — irreversible.
viewer_url
Get the URL of the live viewer plus per-canvas URLs. Share these with the user so they can open the design in their browser. No params.
{
"url": "http://localhost:3001",
"gallery": "http://localhost:3001",
"canvases": [
{ "name": "Login", "viewer": "http://localhost:3001/canvas/abc123" }
]
}
canvas_create already returns the per-canvas viewer URL in its response; reach for viewer_url when you want the gallery URL or to enumerate every existing canvas's URL in one call.
workspace_create / workspace_list / workspace_rename / workspace_delete
Top-level container CRUD. The built-in Personal workspace cannot be deleted, and workspace_delete refuses if the workspace still contains projects (move or delete them first).
project_create / project_list / project_rename / project_delete
Mid-level container CRUD inside a workspace. The built-in Untitled project cannot be deleted. project_delete refuses if the project still contains any canvases (archived ones still count — move or delete them first).
canvas_bind
Bind a workspace to the current project directory so its canvases live in the repo as open JSON — a .framesmith/ directory checked in alongside the code, instead of the global ~/.framesmith store. Run it once per repo.
| Param | Type | Description | |-------|------|-------------| | workspaceId | string? | Workspace whose projects + canvases migrate into the repo. Defaults to the built-in Personal workspace. | | dir | string? | Directory to bind. Defaults to the nearest git repo root above the server's working directory. |
It creates .framesmith/workspace.json (the binding plus the design system, so a fresh clone resolves tokens identically) and one subdirectory per project holding one slug-named file per canvas:
.framesmith/
workspace.json # workspace + projects[] + design system
design-system/
design-tokens.json
ui/
bloom-landing.json
login-form.json
It migrates the workspace's projects + canvases in and makes the repo the source of truth for the rest of the session. A canvas is either repo-bound or global, never both. Afterwards the server auto-detects .framesmith/ on startup (walking up from its working directory). Commit .framesmith/ so designs travel with the code and diff cleanly in review.
The bind also records the repo in ~/.framesmith/registry.json, so the standalone viewer shows bound repos alongside your global workspaces in one gallery (it rebuilds that read-only mirror on launch and whenever the registry changes).
batch_design
Execute operations on the scene graph. Operations are line-separated strings:
# Insert a frame into the document root
header=I("document", { type: "frame", layout: "horizontal", fill: "#1a1a2e", padding: 24, gap: 16, width: 1440, height: 80 })
# Insert text into the header
I(header, { type: "text", content: "My App", fontSize: 24, fontWeight: 700, color: "#ffffff" })
# Update a node
U("nodeId", { fill: "#e94560" })
# Delete a node
D("nodeId")
# Copy a node to a new parent
copy=C("sourceId", "parentId", { fill: "#0f3460" })
# Move a node
M("nodeId", "newParentId", 0)
# Replace a node entirely
R("nodeId", { type: "text", content: "Replaced" })
Returns { ok, nodeIds, results }. nodeIds maps each bound variable to the node ID it created — e.g. { "header": "n_a1b2" } — so you can target those nodes in later calls (bindings only live within a single call). results lists each op's outcome in order.
Node types: frame, text, rectangle, ellipse, image, icon, path, component, instance, toggle, checkbox, radio, select
Properties: fill, gradient, stroke, strokeWidth, cornerRadius, width, height, layout ("horizontal" | "vertical"), gap, padding, alignItems, justifyContent, fontSize, fontFamily, fontWeight, color, content, textAlign, lineHeight, letterSpacing (px), textDecoration, textTransform, fontVariationSettings, src, objectFit, opacity, shadow, shadows, blur, backdropBlur, backdropFilter, overflow, wrap, position, x, y, icon, iconSize, iconColor, iconStyle, checked, disabled, value, d, viewBox, strokeLinecap, strokeLinejoin, animation, transition, componentId, overrides
Use textTransform: "uppercase" for
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: vicmaster
- Source: vicmaster/framesmith
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.