@diffui.mcp/install 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -5
- package/launcher.mjs +53 -2
- package/package.json +4 -3
- package/skill/SKILL.md +231 -0
package/README.md
CHANGED
|
@@ -14,10 +14,14 @@ Thin Node launcher for the diffui MCP server. Cursor (and other MCP hosts) run:
|
|
|
14
14
|
}
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
On first start, the launcher
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
On first start, the launcher:
|
|
18
|
+
|
|
19
|
+
1. Copies the bundled design-first skill to `~/.cursor/skills/diffui/SKILL.md` (updates when
|
|
20
|
+
the launcher npm version changes; set `DIFFUI_SKIP_SKILL_INSTALL=1` to skip).
|
|
21
|
+
2. Downloads the platform `diffui-mcp` binary from `{DIFFUI_API_BASE}/mcp/` into
|
|
22
|
+
`~/.diffui/bin/diffui-mcp`, then execs it with inherited stdio.
|
|
23
|
+
|
|
24
|
+
Credentials can also come from the MCP `authenticate` tool (`~/.diffui/credentials`).
|
|
21
25
|
|
|
22
26
|
## Publish to npm
|
|
23
27
|
|
|
@@ -28,8 +32,14 @@ Prerequisites:
|
|
|
28
32
|
2. npm account in the [@diffui.mcp](https://www.npmjs.com/org/diffui.mcp) org.
|
|
29
33
|
|
|
30
34
|
```bash
|
|
35
|
+
./scripts/publish-mcp-install.sh
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This syncs `skills/diffui/SKILL.md` into the package, then publishes. Or manually:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
./scripts/sync-mcp-skill.sh
|
|
31
42
|
cd packages/mcp-install
|
|
32
|
-
npm login
|
|
33
43
|
npm publish --access public
|
|
34
44
|
```
|
|
35
45
|
|
package/launcher.mjs
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
3
|
* diffui MCP launcher for Cursor / Claude / npx.
|
|
4
|
-
* Ensures ~/.diffui/bin/diffui-mcp exists,
|
|
4
|
+
* Ensures ~/.diffui/bin/diffui-mcp exists, installs the Cursor skill to
|
|
5
|
+
* ~/.cursor/skills/diffui on first start (or when the launcher version changes),
|
|
6
|
+
* then execs the MCP binary with inherited stdio.
|
|
5
7
|
*
|
|
6
8
|
* Env (from Cursor mcp.json or shell):
|
|
7
9
|
* DIFFUI_API_BASE — API origin (default https://diffui.ai)
|
|
8
10
|
* DIFFUI_API_KEY — optional; authenticate tool can store ~/.diffui/credentials
|
|
11
|
+
* DIFFUI_SKIP_SKILL_INSTALL — set to 1 to skip copying SKILL.md
|
|
9
12
|
*/
|
|
10
13
|
import { spawn, spawnSync } from "node:child_process";
|
|
11
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
chmodSync,
|
|
16
|
+
copyFileSync,
|
|
17
|
+
createWriteStream,
|
|
18
|
+
existsSync,
|
|
19
|
+
mkdirSync,
|
|
20
|
+
readFileSync,
|
|
21
|
+
writeFileSync,
|
|
22
|
+
} from "node:fs";
|
|
12
23
|
import { arch, homedir, platform } from "node:os";
|
|
13
24
|
import { dirname, join } from "node:path";
|
|
25
|
+
import { fileURLToPath } from "node:url";
|
|
14
26
|
import { pipeline } from "node:stream/promises";
|
|
15
27
|
import { get as httpsGet } from "node:https";
|
|
16
28
|
import { get as httpGet } from "node:http";
|
|
17
29
|
|
|
30
|
+
const launcherDir = dirname(fileURLToPath(import.meta.url));
|
|
18
31
|
const base = String(process.env.DIFFUI_API_BASE || "https://diffui.ai").replace(/\/+$/, "");
|
|
19
32
|
const dest = join(homedir(), ".diffui", "bin", "diffui-mcp");
|
|
20
33
|
|
|
@@ -31,6 +44,43 @@ function binaryName() {
|
|
|
31
44
|
throw new Error(`diffui-mcp: unsupported platform ${platform()}-${arch()}`);
|
|
32
45
|
}
|
|
33
46
|
|
|
47
|
+
function launcherVersion() {
|
|
48
|
+
try {
|
|
49
|
+
const pkg = JSON.parse(readFileSync(join(launcherDir, "package.json"), "utf8"));
|
|
50
|
+
return String(pkg.version || "0.0.0");
|
|
51
|
+
} catch {
|
|
52
|
+
return "0.0.0";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ensureCursorSkill() {
|
|
57
|
+
if (process.env.DIFFUI_SKIP_SKILL_INSTALL === "1") return;
|
|
58
|
+
|
|
59
|
+
const skillSrc = join(launcherDir, "skill", "SKILL.md");
|
|
60
|
+
if (!existsSync(skillSrc)) return;
|
|
61
|
+
|
|
62
|
+
const skillDir = join(homedir(), ".cursor", "skills", "diffui");
|
|
63
|
+
const skillDest = join(skillDir, "SKILL.md");
|
|
64
|
+
const versionStamp = join(skillDir, ".diffui-install-version");
|
|
65
|
+
const version = launcherVersion();
|
|
66
|
+
|
|
67
|
+
let installed = "";
|
|
68
|
+
if (existsSync(versionStamp)) {
|
|
69
|
+
try {
|
|
70
|
+
installed = readFileSync(versionStamp, "utf8").trim();
|
|
71
|
+
} catch {
|
|
72
|
+
installed = "";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (installed === version && existsSync(skillDest)) return;
|
|
77
|
+
|
|
78
|
+
mkdirSync(skillDir, { recursive: true });
|
|
79
|
+
copyFileSync(skillSrc, skillDest);
|
|
80
|
+
writeFileSync(versionStamp, `${version}\n`, "utf8");
|
|
81
|
+
process.stderr.write(`diffui-mcp: installed Cursor skill at ${skillDest}\n`);
|
|
82
|
+
}
|
|
83
|
+
|
|
34
84
|
async function download(url, path) {
|
|
35
85
|
const getter = url.startsWith("https:") ? httpsGet : httpGet;
|
|
36
86
|
await new Promise((resolve, reject) => {
|
|
@@ -72,6 +122,7 @@ async function ensureBinary() {
|
|
|
72
122
|
return dest;
|
|
73
123
|
}
|
|
74
124
|
|
|
125
|
+
ensureCursorSkill();
|
|
75
126
|
const bin = await ensureBinary();
|
|
76
127
|
const child = spawn(bin, [], {
|
|
77
128
|
stdio: "inherit",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diffui.mcp/install",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Launcher for the diffui MCP server (stdio).
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Launcher for the diffui MCP server (stdio). Installs the Cursor skill on first start.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"launcher.mjs",
|
|
12
|
-
"README.md"
|
|
12
|
+
"README.md",
|
|
13
|
+
"skill/SKILL.md"
|
|
13
14
|
],
|
|
14
15
|
"engines": {
|
|
15
16
|
"node": ">=18"
|
package/skill/SKILL.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: diffui-designer
|
|
3
|
+
description: >-
|
|
4
|
+
Runs the diffui MCP workflow before any HTML/CSS/JS is written: create_project,
|
|
5
|
+
generate_options (4 variants), generate_page, create_build_link, then implement
|
|
6
|
+
code from the build markdown. Auth (whoami/authenticate) only on tool errors —
|
|
7
|
+
installs usually include DIFFUI_API_KEY already. REQUIRED when the user asks to
|
|
8
|
+
create, build, or design a webpage, website, landing page, app screen, dashboard,
|
|
9
|
+
or UI — including "create a webpage for …", "design first then build/create in …",
|
|
10
|
+
themed sites (jungle, arctic, etc.), and output paths like demoBuild/. Do NOT
|
|
11
|
+
skip diffui and design manually in prose or jump straight to coding when the
|
|
12
|
+
diffui MCP server is available. Also use when the user mentions diffui.
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Diffui designer
|
|
16
|
+
|
|
17
|
+
You orchestrate diffui (an AI design-generation canvas) through its **MCP tools**.
|
|
18
|
+
The user sees the images you return inline, and can also watch the canvas live
|
|
19
|
+
in their browser. Follow this choreography.
|
|
20
|
+
|
|
21
|
+
## When this skill applies (required)
|
|
22
|
+
|
|
23
|
+
If the **diffui MCP server** is enabled and the user wants a new webpage or UI
|
|
24
|
+
look — even when they also name an output folder (`demoBuild/bongo`,
|
|
25
|
+
`demoBuild/svalbardLan`, etc.) — you **must** use diffui for the design phase.
|
|
26
|
+
Treat these as the same class of request:
|
|
27
|
+
|
|
28
|
+
- "Create a webpage for …"
|
|
29
|
+
- "Design a landing page …"
|
|
30
|
+
- "Design first, then create/build it in …"
|
|
31
|
+
- "Build a website for …" (when no existing diffui designs are referenced)
|
|
32
|
+
|
|
33
|
+
**Two phases — never merge them:**
|
|
34
|
+
|
|
35
|
+
1. **Design (diffui MCP)** — `create_project` → `open_canvas` →
|
|
36
|
+
`generate_options` → pick/iterate → (optional `generate_page` for multi-page) →
|
|
37
|
+
`create_build_link`.
|
|
38
|
+
2. **Implementation (code)** — write HTML/CSS/JS only **after** phase 1, following
|
|
39
|
+
the build markdown from `create_build_link` into the user's target path.
|
|
40
|
+
|
|
41
|
+
**Minimal path (single landing page):** `generate_options` → user picks →
|
|
42
|
+
`create_build_link` → code. Do not require brand creation or extra `generate_page`
|
|
43
|
+
calls for a one-page request.
|
|
44
|
+
|
|
45
|
+
**Forbidden before `generate_options`:**
|
|
46
|
+
|
|
47
|
+
- Creating `index.html`, stylesheets, or app UI files
|
|
48
|
+
- Choosing colors, typography, and layout entirely from scratch
|
|
49
|
+
- "Designing conceptually" in prose instead of calling `generate_options`
|
|
50
|
+
|
|
51
|
+
**After `generate_options`:**
|
|
52
|
+
|
|
53
|
+
- Present options as Option A–D with imageIds
|
|
54
|
+
- User present → ask which to build
|
|
55
|
+
- User said "surprise me" / full trust → pick the strongest fit and say why
|
|
56
|
+
- User unavailable mid-run → wait; do not proceed to code without a pick
|
|
57
|
+
|
|
58
|
+
**Auth (reactive only):** Do not call `whoami` or `authenticate` before starting — the MCP
|
|
59
|
+
install usually includes credentials. If any tool returns not-authenticated, call
|
|
60
|
+
`authenticate` once, then retry. Do not silently fall back to hand-designed UI.
|
|
61
|
+
|
|
62
|
+
**Prompt writing:** Turn the user's request into a visual design prompt (audience, tone,
|
|
63
|
+
sections, layout, color/mood). Do not substitute concept bullets for `generate_options`.
|
|
64
|
+
|
|
65
|
+
**Do not:**
|
|
66
|
+
|
|
67
|
+
- Skip diffui because you can write vanilla HTML/CSS/JS directly.
|
|
68
|
+
- "Design" in chain-of-thought or bullet lists instead of calling `generate_options`.
|
|
69
|
+
- Read `generate_options` tool docs and then decide to design manually.
|
|
70
|
+
- Start creating files in the target directory before diffui options exist.
|
|
71
|
+
|
|
72
|
+
**Only skip diffui** when the user explicitly says not to ("skip design", "no diffui",
|
|
73
|
+
"just code it"), for pure backend tasks, small non-visual edits, or they point you at
|
|
74
|
+
finished diffui designs / a build link with no new design work. If you skip, say so in
|
|
75
|
+
one sentence.
|
|
76
|
+
|
|
77
|
+
### Example triggers
|
|
78
|
+
|
|
79
|
+
| User says | You do first |
|
|
80
|
+
|---|---|
|
|
81
|
+
| Create a jungle-themed coffee shop page in `demoBuild/bongo` | diffui `generate_options`; code goes in `demoBuild/bongo` after build link |
|
|
82
|
+
| Design the page first, then create it in `demoBuild/svalbardLan` | diffui design phase — "design first" means MCP, not manual concept art |
|
|
83
|
+
| Build me a SaaS pricing page | diffui options → pages → build link → code |
|
|
84
|
+
|
|
85
|
+
## 0. Setup
|
|
86
|
+
|
|
87
|
+
1. For a new design session, call `create_project`, then `open_canvas` so the
|
|
88
|
+
user can watch generations stream in. Reuse the same `project_id` for the
|
|
89
|
+
whole session.
|
|
90
|
+
2. **Auth only on error.** Do not proactively call `whoami`. If a tool fails with
|
|
91
|
+
not-authenticated, call `authenticate` — a browser window opens for one-click
|
|
92
|
+
connect, or pass an API key from diffui → Settings → API as `api_key`. Then
|
|
93
|
+
retry the failed call.
|
|
94
|
+
|
|
95
|
+
## 1. New design → 4 options
|
|
96
|
+
|
|
97
|
+
When the user describes a design they want:
|
|
98
|
+
|
|
99
|
+
- Call `generate_options` with `count: 4` and **no** `brand_id` (a fresh
|
|
100
|
+
canvas auto-diversifies styles across the options, which is what you want).
|
|
101
|
+
- Present the four images labeled **Option 1–4** and keep the
|
|
102
|
+
`option → imageId` ledger from the tool result. You will need those
|
|
103
|
+
imageIds for every later step — always carry the ledger forward in your
|
|
104
|
+
reasoning.
|
|
105
|
+
- Offer three paths: pick one, see more options (`generate_options` again,
|
|
106
|
+
passing the same `prompt_node_id` to append), or mix elements of options
|
|
107
|
+
(see "Generating with inputs").
|
|
108
|
+
|
|
109
|
+
## 2. Generating with inputs
|
|
110
|
+
|
|
111
|
+
`generate_with_inputs` handles any request where existing images should
|
|
112
|
+
shape the result. Each input gets a short label; write the prompt
|
|
113
|
+
referencing the labels with `@` — diffui rigs the labeled images as named
|
|
114
|
+
references. Inputs can come from three sources, mixed freely:
|
|
115
|
+
|
|
116
|
+
- **Earlier options** (`image_id` from your ledger) — "combine the top of
|
|
117
|
+
the first option and the bottom of the second":
|
|
118
|
+
`inputs: [{image_id: <opt1>, label: "option-1"}, {image_id: <opt2>,
|
|
119
|
+
label: "option-2"}]`, prompt "Use the top section of @option-1 and the
|
|
120
|
+
bottom section of @option-2, merged into one cohesive page."
|
|
121
|
+
- **Local files** (`file_path`, uploaded automatically) — "use logo.png and
|
|
122
|
+
create a landing page with it in the top right":
|
|
123
|
+
`inputs: [{file_path: "/path/to/logo.png", label: "logo"}]`, prompt
|
|
124
|
+
"Landing page for ... with @logo placed in the top right of the header."
|
|
125
|
+
- **Uploaded assets** (`asset_id` from an earlier `upload_reference`) —
|
|
126
|
+
e.g. a screenshot of the user's current app: "generate a settings page
|
|
127
|
+
matching @screenshot's visual style."
|
|
128
|
+
|
|
129
|
+
Keep spatial phrases tied to their labels ("the header from @option-4",
|
|
130
|
+
"@logo in the top right") — diffui resolves positions per named reference.
|
|
131
|
+
Present the results as new numbered options and update your ledger. Use
|
|
132
|
+
`upload_reference` first when a file will be reused across several
|
|
133
|
+
generations; pass `file_path` directly for one-offs.
|
|
134
|
+
|
|
135
|
+
## 3. Chosen option → build out the rest of the site
|
|
136
|
+
|
|
137
|
+
When the user settles on an option, **do not lock anything** — the chosen
|
|
138
|
+
option's imageId (from your ledger) is the only handle you need. Immediately:
|
|
139
|
+
|
|
140
|
+
1. **Read the header to find the pages.** Look at the chosen design and read
|
|
141
|
+
the navigation links in its header — those *are* the site's page list. A nav
|
|
142
|
+
reading "Menu · Our Story · Visit · Contact" means those are the pages to
|
|
143
|
+
build. Add any obvious page the design implies but the nav omits (a cart /
|
|
144
|
+
checkout behind a cart icon, a sign-up behind a "Get started" button). List
|
|
145
|
+
the pages you inferred and confirm or adjust with the user before generating.
|
|
146
|
+
2. **Generate each page from the chosen image.** For every accepted page, call
|
|
147
|
+
`generate_page` with `input_image_id` set to the **chosen option's imageId**
|
|
148
|
+
so the header, footer, palette, and type carry over. In the prompt, mention
|
|
149
|
+
`@reference` for what must stay consistent and describe what changes (the
|
|
150
|
+
page's own content and layout).
|
|
151
|
+
3. **Track page name → imageId** in your ledger as each page returns. No lock
|
|
152
|
+
step is involved; the imageId is the durable handle for brands and the build.
|
|
153
|
+
|
|
154
|
+
If the header nav is missing or ambiguous, fall back to domain conventions:
|
|
155
|
+
|
|
156
|
+
- e-commerce: product listing, product detail, cart, checkout
|
|
157
|
+
- SaaS: pricing, features, sign-up, dashboard, settings
|
|
158
|
+
- restaurant: menu, reservations, about, contact
|
|
159
|
+
|
|
160
|
+
## 4. Auto-create a brand (≥5 screens)
|
|
161
|
+
|
|
162
|
+
Track the screens kept this session. When **five or more visually consistent
|
|
163
|
+
screens** exist and the conversation suggests more screens are coming,
|
|
164
|
+
proactively offer to create a brand:
|
|
165
|
+
|
|
166
|
+
> "You now have N screens in a consistent style. Want me to create a brand
|
|
167
|
+
> from them? Future screens will then match automatically."
|
|
168
|
+
|
|
169
|
+
On yes: `create_brand_from_images` with those imageIds and a name derived
|
|
170
|
+
from the project. After the brand exists:
|
|
171
|
+
|
|
172
|
+
- Pass `brand_id` on every `generate_options` / `generate_page` call and
|
|
173
|
+
**stop** manual input rigging — diffui auto-selects the best brand
|
|
174
|
+
references per prompt via embedding matching.
|
|
175
|
+
- After each new screen the user keeps that fits the brand, call
|
|
176
|
+
`add_image_to_brand`.
|
|
177
|
+
|
|
178
|
+
## 5. Brands by name
|
|
179
|
+
|
|
180
|
+
- If the user names a brand ("use the Acme brand"), call `list_brands`,
|
|
181
|
+
match by name, and pass its id.
|
|
182
|
+
- If a new request clearly resembles an existing brand (same product,
|
|
183
|
+
company, or style), proactively suggest using it: check `list_brands`
|
|
184
|
+
early in a session when the user's intent hints at existing work.
|
|
185
|
+
|
|
186
|
+
## 6. Convert to code
|
|
187
|
+
|
|
188
|
+
When the user named an output folder up front (e.g. `demoBuild/bongo`), you still
|
|
189
|
+
complete the diffui design phase first. Remember that path; use it when writing
|
|
190
|
+
files after `create_build_link`.
|
|
191
|
+
|
|
192
|
+
When the design flow feels complete (pages generated, user satisfied), ask:
|
|
193
|
+
**"Ready to convert these designs to code?"** On yes:
|
|
194
|
+
|
|
195
|
+
1. Call `create_build_link` with every page you kept — the imageIds in your
|
|
196
|
+
ledger — (`image_id`, `name`, `original_prompt`, and `brand_id` if one
|
|
197
|
+
exists) and `include_markdown: true`.
|
|
198
|
+
2. Follow the returned build instructions markdown exactly — it is the
|
|
199
|
+
source of truth for implementation.
|
|
200
|
+
3. For **all** asset needs during the build, use the diffui build API
|
|
201
|
+
endpoints documented in the markdown with its `authToken`:
|
|
202
|
+
- `POST /api/build/generate-image` for photos/illustrations
|
|
203
|
+
- `POST /api/build/generate-svg` for icons and vector art
|
|
204
|
+
- `POST /api/build/remove-background` for cutouts
|
|
205
|
+
- `POST /api/build/create-texture` for seamless background textures
|
|
206
|
+
Never hotlink canvas/preview URLs into the built code; download assets
|
|
207
|
+
locally as the markdown instructs.
|
|
208
|
+
4. If you cannot write code in the current environment (e.g. chat-only),
|
|
209
|
+
give the user the `handoff` string from the tool result to paste into
|
|
210
|
+
their coding agent.
|
|
211
|
+
|
|
212
|
+
## Ground rules
|
|
213
|
+
|
|
214
|
+
- **Design before code.** If you have not called `generate_options` this session,
|
|
215
|
+
you are not allowed to create webpage files yet.
|
|
216
|
+
- Always show generated images inline right after each tool call returns.
|
|
217
|
+
- Keep a running ledger of option numbers → imageIds → page names; restate
|
|
218
|
+
it briefly when the user refers to options by number.
|
|
219
|
+
- Generations block for the whole render (up to a few minutes) and stream
|
|
220
|
+
progress updates. A quiet call is working, not stuck — **never re-issue a
|
|
221
|
+
generation to "retry" a slow one.** A duplicate run desyncs the canvas and
|
|
222
|
+
leaves you holding imageIds that aren't in committed state. If you truly need
|
|
223
|
+
longer, raise `timeout_seconds` on a fresh call, don't fire a second one
|
|
224
|
+
alongside the first.
|
|
225
|
+
- Generation costs the user wallet credits per image. Use the defaults
|
|
226
|
+
(4 options for explorations, 1 for pages); confirm before unusually large
|
|
227
|
+
batches or regenerating many screens at once.
|
|
228
|
+
- If a generation fails with a billing error, tell the user their diffui
|
|
229
|
+
wallet needs funds.
|
|
230
|
+
- If a tool errors with authentication problems, call `authenticate` once and
|
|
231
|
+
retry — do not open with a proactive whoami check.
|