webcake-landing-mcp 1.0.13 → 1.0.15

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 CHANGED
@@ -1,4 +1,4 @@
1
- # 🍰 WebCake Landing MCP
1
+ # <img src="docs/assets/webcake-icon.svg" alt="Webcake" width="26" height="26" align="absmiddle"> WebCake Landing MCP
2
2
 
3
3
  **English** · [Tiếng Việt](./README.vi.md)
4
4
 
@@ -58,13 +58,53 @@ persists it (source-only — the page opens in the editor where re-saving render
58
58
 
59
59
  | Method | Best for | Auth |
60
60
  |--------|----------|------|
61
- | **npx (local stdio)** — add to an IDE with one command | Daily use on your machine | browser `login`, a JWT, or none (reference tools) |
62
- | **Remote HTTP (`serve`)** — run as a connector behind a URL | A hosted/shared server (e.g. Coolify) | per-request `x-webcake-jwt` header / `?jwt=` |
61
+ | **npx (local)** — runs on your machine | Personal daily use, full control | browser `login`, a JWT, or none (reference tools) |
62
+ | **Hosted URL** — use our live server, nothing to install | No Node.js, teams, the claude.ai dialog | your personal `?jwt=` link / `x-webcake-jwt` header |
63
63
 
64
64
  The **reference + generation tools** (`get_generation_guide`, `list_elements`, `validate_page`, …) work with **zero config**; only the **persistence tools** (`create_page`, `update_page`, `list_pages`, `get_page`, `list_organizations`) need a token. Credentials resolve in order: **per-request header → env var → saved `auth.json`** (`login`).
65
65
 
66
66
  > 🛠️ Prefer a shell-script installer (`install.sh`/`install.ps1`), a cloned local build, or hand-written per-IDE config? See **[docs/manual-install.md](docs/manual-install.md)**.
67
67
 
68
+ ## 🚀 Get connected — the 2 main ways
69
+
70
+ Pick **one**. Both hand your AI tool (Claude, Cursor, …) the full Webcake landing toolkit. No coding.
71
+
72
+ ### ① `npx` — runs on your machine (recommended for personal use)
73
+
74
+ Zero install, always the latest version. **One line** grabs your token *and* writes the IDE config:
75
+
76
+ ```bash
77
+ npx -y webcake-landing-mcp install
78
+ ```
79
+
80
+ Just want to run the server (configure by hand later)?
81
+
82
+ ```bash
83
+ npx -y webcake-landing-mcp
84
+ ```
85
+
86
+ ✅ Best for: daily personal use, local development, full control. Needs Node.js 18+.
87
+
88
+ ### ② Remote URL `…/mcp?jwt=` — hosted, nothing to install
89
+
90
+ Use the server we already host. Grab **your personal link** (your token is baked in) and paste it into your client's *Add custom connector* / config:
91
+
92
+ ```
93
+ https://mcp.toolvn.io.vn/mcp?jwt=<YOUR_TOKEN>
94
+ ```
95
+
96
+ Two ways to get the link:
97
+ - **Easiest** — open **<https://webcake.io/mcp-remote>** in your Webcake dashboard → it builds & copies the link for you.
98
+ - **By hand** — see the step-by-step guide below.
99
+
100
+ You can also add extra params: `&env=prod`, `&org_id=…`, `&api_base=…`.
101
+
102
+ ✅ Best for: no Node.js, team/shared use, the **claude.ai** connector dialog (URL-only, no headers).
103
+ ⚠️ The link contains your personal token — treat it like a password, always use **HTTPS**.
104
+
105
+ > 📖 **Full hand-config for every IDE** (Claude Desktop, Claude Code, Cursor, Windsurf, claude.ai…) is in the
106
+ > step-by-step guide → **[docs/connect-mcp.md](docs/connect-mcp.md)** · Tiếng Việt: **[docs/ket-noi-mcp.md](docs/ket-noi-mcp.md)**.
107
+
68
108
  ## Install (npx)
69
109
 
70
110
  Once published to npm, the server runs straight from the registry — no clone, no build:
@@ -127,34 +167,25 @@ The MCP config is the same as the local one, but `command`/`args` point at `npx`
127
167
  > npx caches the package after the first run, so subsequent launches are fast. Use a pinned version
128
168
  > (`webcake-landing-mcp@1.0.0`) if you need a reproducible build.
129
169
 
130
- ## Run as a remote connector (Streamable HTTP)
170
+ ## Use the hosted server nothing to install
131
171
 
132
- The server also speaks the **remote MCP** (Streamable HTTP) transport, so it can be added through
133
- Claude's **"Add custom connector"** dialog via a URL not just as a local stdio server.
172
+ It's **already live** at **`https://mcp.toolvn.io.vn/mcp`** we run it for you. No server to set up, no
173
+ machine to keep awake. Just point your AI client at the URL and go.
134
174
 
135
- Start it in HTTP mode (default port `8787`, or set `PORT` / `--port`):
175
+ **Grab your personal link** (your token is baked in) the easy way → open **<https://webcake.io/mcp-remote>**
176
+ and hit **Copy**:
136
177
 
137
- ```bash
138
- npx -y webcake-landing-mcp serve --port 8787
139
- # → MCP endpoint at http://localhost:8787/mcp (GET / or /health returns a status JSON)
178
+ ```
179
+ https://mcp.toolvn.io.vn/mcp?jwt=<YOUR_TOKEN>
140
180
  ```
141
181
 
142
- Expose it over **HTTPS** at a public URL (a reverse proxy, a tunnel like `ngrok http 8787`, or any
143
- host), then in Claude **Add custom connector**:
144
-
145
- - **Name**: `webcake-landing`
146
- - **Remote MCP server URL**: `https://<your-host>/mcp`
147
-
148
- The dialog has no header field, so to pass a token through it, **put it in the URL**:
149
- `https://<your-host>/mcp?jwt=<ljwt>` (also accepts `&api_base=…`, `&org_id=…`, `&host=…`, `&app_base=…`).
150
- Give each person a URL with their own `jwt` → **per-user without OAuth**. An explicit `x-webcake-jwt` header
151
- still wins over the query. ⚠️ A token in a URL can land in access/proxy logs — require **HTTPS** and disable
152
- query-string logging on your reverse proxy; a header (or OAuth) is safer when the client supports it.
182
+ Optional extras: `&env=prod`, `&org_id=…`, `&api_base=…`. Hand each teammate a link with their own `jwt`
183
+ per-user, no OAuth. ⚠️ The link carries your personal token — treat it like a password, always over **HTTPS**.
153
184
 
154
- ### Auth per-request, multi-user (no shared token)
185
+ ### Sending the token as a header (safer)
155
186
 
156
- In stdio mode the JWT comes from env. In HTTP mode each request carries the caller's **own** credentials
157
- via headers, so a hosted server is multi-user and never bakes in a shared secret:
187
+ Clients that support headers should send the token as a header instead of putting it in the URL (so it never
188
+ lands in logs). Any header that's missing falls back to the matching env var:
158
189
 
159
190
  | Header | Maps to | Notes |
160
191
  |--------|---------|-------|
@@ -164,61 +195,12 @@ via headers, so a hosted server is multi-user and never bakes in a shared secret
164
195
  | `x-webcake-api-base` | `WEBCAKE_API_BASE` | overrides the env preset's API base |
165
196
  | `x-webcake-app-base` | `WEBCAKE_APP_BASE` | overrides the env preset's app base |
166
197
 
167
- Any header that is absent falls back to the corresponding env var — so you can also run it **single-user**
168
- by setting `WEBCAKE_API_BASE` + `WEBCAKE_JWT` in the host's env and keeping the URL private.
169
-
170
- > ⚠️ The reference + generation tools (`get_generation_guide`, `list_elements`, `validate_page`, …) need
171
- > no secret; only the persistence tools (`create_page`, `update_page`, …) use the JWT. If a request has no
172
- > JWT, those tools return `missing_env` instead of touching the network.
173
- >
174
- > Note: the claude.ai connector dialog has **no header field** (only OAuth, which this server does not
175
- > implement yet). Two ways around it: put the token in the URL as `?jwt=<ljwt>` (above — per-user, but the
176
- > token shows up in logs), or use a header-capable client (`mcp-remote --header …`, below). A token in the
177
- > server's env instead gives a shared **single-account** for everyone on that URL.
178
-
179
- ### Test it locally (no public URL needed)
180
-
181
- `localhost` can't be used in the claude.ai dialog (Anthropic fetches the URL from its own servers). To try the
182
- running `serve` server on your machine:
183
-
184
- - **MCP Inspector** (GUI — easiest): `npx @modelcontextprotocol/inspector` → Transport **Streamable HTTP** →
185
- URL `http://localhost:8787/mcp` → under Headers add `x-webcake-jwt` (+ `x-webcake-api-base`) → Connect → call tools.
186
- - **`mcp-remote`** (use the remote server from a stdio client like Claude Desktop, with headers):
187
- ```json
188
- { "mcpServers": { "webcake-remote": { "command": "npx",
189
- "args": ["-y", "mcp-remote", "http://localhost:8787/mcp",
190
- "--header", "x-webcake-jwt:<ljwt>",
191
- "--header", "x-webcake-api-base:https://api.webcake.io"] } } }
192
- ```
193
- - **curl**: `initialize` (read the `mcp-session-id` response header) → `tools/list` → `tools/call`, all with
194
- `Accept: application/json, text/event-stream`.
195
-
196
- ### Deploy on a VPS
197
-
198
- 1. **Build + run as a service** — `/etc/systemd/system/webcake-mcp.service`:
199
- ```ini
200
- [Service]
201
- WorkingDirectory=/opt/webcake-landing-mcp
202
- ExecStart=/usr/bin/node dist/index.js serve --port 8787
203
- Environment=WEBCAKE_API_BASE=https://api.webcake.io
204
- Environment=WEBCAKE_JWT=<ljwt> # single-account only — see auth note below
205
- Restart=always
206
- [Install]
207
- WantedBy=multi-user.target
208
- ```
209
- `sudo systemctl enable --now webcake-mcp` (build once: `npm install && npm run build`).
210
- 2. **HTTPS + domain** (claude.ai requires https) — e.g. Caddy auto-TLS, `/etc/caddy/Caddyfile`:
211
- ```
212
- mcp.yourdomain.com { reverse_proxy localhost:8787 }
213
- ```
214
- 3. **Add to claude.ai** → Remote MCP server URL = `https://mcp.yourdomain.com/mcp`.
215
-
216
- **Auth on a shared server:**
217
- - **Single-account** (works with the dialog today): `WEBCAKE_JWT` in the service env → everyone using the
218
- connector shares that one Webcake account. Keep the URL private / gated; the token expires (~90 days).
219
- - **Per-user** (each person their own account): give each person a URL with their own `?jwt=<ljwt>` (works
220
- through the dialog, but the token appears in logs), or use a header-capable client (`mcp-remote --header …`),
221
- or add **OAuth** (not implemented) for the cleanest flow.
198
+ > The reference + generation tools (`get_generation_guide`, `list_elements`, `validate_page`, …) need **no
199
+ > token** only the persistence tools (`create_page`, `update_page`, …) use it. Without a JWT, those return
200
+ > `missing_env` instead of touching the network.
201
+
202
+ > 📖 **Full step-by-step for every IDE** (Claude Desktop, Claude Code, Cursor, Windsurf, claude.ai)
203
+ > **[docs/connect-mcp.md](docs/connect-mcp.md)** · Tiếng Việt: **[docs/ket-noi-mcp.md](docs/ket-noi-mcp.md)**.
222
204
 
223
205
  ## Connect once — grab your token automatically (`login`)
224
206
 
@@ -243,8 +225,8 @@ It opens your browser → (log into Webcake if needed) → the token is saved to
243
225
 
244
226
  You're already logged in to Webcake in your browser, so `login` just opens a Webcake "connect"
245
227
  page that reads your **`ljwt`** (landing) cookie and hands the token back to a localhost callback —
246
- no copy-paste. The saved token is used by **both** the stdio server and a single-user `serve`
247
- deployment (env vars still take precedence). The landing JWT lasts ~90 days, so you rarely reconnect.
228
+ no copy-paste. The saved token is then read automatically (env vars still take precedence).
229
+ The landing JWT lasts ~90 days, so you rarely reconnect.
248
230
 
249
231
  Two URLs, don't mix them up:
250
232
 
@@ -270,7 +252,7 @@ For safety, only honor `redirect_uri` values on `http://127.0.0.1:*` / `http://l
270
252
  flow can also be done entirely in the SPA, no backend route needed.)
271
253
 
272
254
  > Multi-user remote (the claude.ai connector dialog) can't do this browser loopback — there each
273
- > user sends their own token via the `x-webcake-jwt` header (see the remote-connector section above).
255
+ > user sends their own token via the `x-webcake-jwt` header (see the hosted-server section above).
274
256
 
275
257
  ## Environment Variables
276
258
 
@@ -301,15 +283,14 @@ truth for the API + app bases:
301
283
  | `prod` | `https://api.webcake.io` | `https://webcake.io` |
302
284
 
303
285
  ```bash
304
- node dist/index.js serve --env staging # remote server on the staging backend
305
- node dist/index.js login --env local # connect against your local SPA + API
306
- WEBCAKE_ENV=prod node dist/index.js # stdio, prod (env var form)
286
+ npx -y webcake-landing-mcp login --env local # connect against your local SPA + API
287
+ WEBCAKE_ENV=staging npx -y webcake-landing-mcp # run against the staging backend
288
+ WEBCAKE_ENV=prod npx -y webcake-landing-mcp # prod (env var form)
307
289
  ```
308
290
 
309
- Explicit `WEBCAKE_API_BASE` / `WEBCAKE_APP_BASE` (or `--api-base`) still override the
310
- preset, field by field. On the remote HTTP server a client can override the server's
311
- environment per request with the **`x-webcake-env`** header or **`?env=`** query
312
- (e.g. `…/mcp?jwt=<token>&env=staging`) — so one server can serve multiple environments.
291
+ Explicit `WEBCAKE_API_BASE` / `WEBCAKE_APP_BASE` (or `--api-base`) still override the preset, field
292
+ by field. On the hosted server you can override the environment per request with the
293
+ **`x-webcake-env`** header or **`?env=`** query (e.g. `…/mcp?jwt=<token>&env=staging`).
313
294
 
314
295
  ### How to get `WEBCAKE_JWT`
315
296
 
@@ -330,93 +311,8 @@ cloned-build variants, see **[docs/manual-install.md](docs/manual-install.md#con
330
311
 
331
312
  ## Usage Examples
332
313
 
333
- ### Example 1: Build a new landing page from a brief
334
-
335
- **Prompt:**
336
- ```
337
- Build me a WebCake landing page for "Acme Coffee" — a hero with a CTA, a 3-feature
338
- section, and a signup form. Persist it to my default org.
339
- ```
340
-
341
- **AI agent will automatically:**
342
-
343
- **Step 1** — Call `get_generation_guide` to learn conventions (canvas, coordinate system, events, workflow)
344
-
345
- **Step 2** — Call `new_page_skeleton` for an empty top-level source, then `get_element` for each type it uses:
346
-
347
- ```
348
- get_element({ type: "section" })
349
- get_element({ type: "text-block" })
350
- get_element({ type: "button" })
351
- get_element({ type: "form" })
352
- ```
353
-
354
- **Step 3** — Assemble the full `{ page, popup, settings, options, cartConfigs }` JSON, then validate:
355
-
356
- ```
357
- validate_page({ source })
358
- → { ok: false, errors: ["BUTTON-2: event target 'POPUP-9' not found"] } # fix every error, re-validate
359
- validate_page({ source })
360
- → { ok: true, errors: [] }
361
- ```
362
-
363
- **Step 4** — Persist (dry-run first, then for real):
364
-
365
- ```
366
- list_organizations({}) → pick the org
367
- create_page({ source }) → dry-run preview (JWT masked)
368
- create_page({ source, dry_run: false }) → { page_id, editor_url, preview_url }
369
- ```
370
-
371
- Open the page in the editor and re-save to render `app`/`app_css`.
372
-
373
- ---
374
-
375
- ### Example 2: Edit an existing page
376
-
377
- **Prompt:**
378
- ```
379
- On my "Acme Coffee" landing page, change the hero headline to "Freshly Roasted Daily"
380
- and make the CTA button green.
381
- ```
382
-
383
- **AI agent edits surgically — never regenerates the whole tree:**
384
-
385
- ```
386
- # Step 1: find the page
387
- list_pages({})
388
- → [{ id: "page_42", name: "Acme Coffee", organization_id: "org_1", ... }]
389
-
390
- # Step 2: fetch its decoded source tree
391
- get_page({ page_id: "page_42" })
392
-
393
- # Step 3: change ONLY the headline text + button color, keep every other id/coordinate,
394
- # then validate and write back
395
- validate_page({ source }) → ok
396
- update_page({ page_id: "page_42", source }) → dry-run preview
397
- update_page({ page_id: "page_42", source, dry_run: false })
398
- ```
399
-
400
- ---
401
-
402
- ### Example 3: Inspect an element type before using it
403
-
404
- **Prompt:**
405
- ```
406
- What specials does a form element need, and show me a valid example.
407
- ```
408
-
409
- **AI agent calls:**
410
-
411
- ```
412
- get_element({ type: "form" })
413
- → {
414
- hints: "Each input needs a unique specials.field_name…",
415
- specials: { ... },
416
- skeleton: { ... }, # structurally-valid default node
417
- example: { ... } # filled, realistic example
418
- }
419
- ```
314
+ Three end-to-end walkthroughs build a page from a brief, edit one surgically, and inspect
315
+ an element type — live in **[docs/usage-examples.md](docs/usage-examples.md)**.
420
316
 
421
317
  ---
422
318
 
package/README.vi.md CHANGED
@@ -1,4 +1,4 @@
1
- # 🍰 WebCake Landing MCP
1
+ # <img src="docs/assets/webcake-icon.svg" alt="Webcake" width="26" height="26" align="absmiddle"> WebCake Landing MCP
2
2
 
3
3
  [English](./README.md) · **Tiếng Việt**
4
4
 
@@ -59,13 +59,53 @@ lưu lại sẽ render).
59
59
 
60
60
  | Cách | Hợp cho | Auth |
61
61
  |------|---------|------|
62
- | **npx (local stdio)** — gắn vào IDE bằng một lệnh | Dùng hằng ngày trên máy | browser `login`, JWT, hoặc không cần (tool tham chiếu) |
63
- | **Remote HTTP (`serve`)** — chạy như connector sau một URL | Server hosted/chia sẻ (vd Coolify) | header `x-webcake-jwt` mỗi request / `?jwt=` |
62
+ | **npx (local)** — chạy trên máy bạn | Dùng cá nhân hằng ngày, toàn quyền | browser `login`, JWT, hoặc không cần (tool tham chiếu) |
63
+ | **URL đã host sẵn** — dùng server tụi mình, không cài | Máy không Node.js, dùng nhóm, dialog claude.ai | link `?jwt=` cá nhân / header `x-webcake-jwt` |
64
64
 
65
65
  Các **tool tham chiếu + generation** (`get_generation_guide`, `list_elements`, `validate_page`, …) chạy **zero config**; chỉ **tool lưu trữ** (`create_page`, `update_page`, `list_pages`, `get_page`, `list_organizations`) mới cần token. Token ưu tiên theo thứ tự: **header mỗi request → biến env → file `auth.json`** (`login`).
66
66
 
67
67
  > 🛠️ Cần script cài (install.sh/.ps1), build từ clone, hay cấu hình IDE thủ công? Xem **[docs/manual-install.vi.md](docs/manual-install.vi.md)**.
68
68
 
69
+ ## 🚀 Kết nối — 2 cách chính
70
+
71
+ Chọn **một** trong hai. Cả hai đều đưa toàn bộ bộ công cụ dựng landing Webcake vào AI của bạn (Claude, Cursor, …). Không cần code.
72
+
73
+ ### ① `npx` — chạy ngay trên máy bạn (nên dùng cho cá nhân)
74
+
75
+ Không cần cài đặt, luôn bản mới nhất. **Một dòng** vừa lấy token vừa ghi cấu hình IDE:
76
+
77
+ ```bash
78
+ npx -y webcake-landing-mcp install
79
+ ```
80
+
81
+ Chỉ muốn chạy server (tự cấu hình sau)?
82
+
83
+ ```bash
84
+ npx -y webcake-landing-mcp
85
+ ```
86
+
87
+ ✅ Hợp cho: dùng cá nhân hằng ngày, dev local, toàn quyền kiểm soát. Cần Node.js 18+.
88
+
89
+ ### ② URL remote `…/mcp?jwt=` — đã host sẵn, không cần cài gì
90
+
91
+ Dùng server tụi mình đã host. Lấy **link cá nhân của bạn** (token gắn sẵn trong link) rồi dán vào ô *Add custom connector* / file cấu hình của client:
92
+
93
+ ```
94
+ https://mcp.toolvn.io.vn/mcp?jwt=<TOKEN_CỦA_BẠN>
95
+ ```
96
+
97
+ Hai cách lấy link:
98
+ - **Dễ nhất** — mở **<https://webcake.io/mcp-remote>** trong dashboard Webcake → nó tự tạo & copy link cho bạn.
99
+ - **Thủ công** — xem hướng dẫn từng bước bên dưới.
100
+
101
+ Có thể thêm tham số: `&env=prod`, `&org_id=…`, `&api_base=…`.
102
+
103
+ ✅ Hợp cho: máy không có Node.js, dùng nhóm/chia sẻ, và **dialog connector của claude.ai** (chỉ nhập URL, không có header).
104
+ ⚠️ Link chứa token cá nhân — coi như mật khẩu, luôn dùng **HTTPS**.
105
+
106
+ > 📖 **Hướng dẫn cấu hình thủ công đầy đủ cho mọi IDE** (Claude Desktop, Claude Code, Cursor, Windsurf, claude.ai…)
107
+ > ở file riêng → **[docs/ket-noi-mcp.md](docs/ket-noi-mcp.md)** · English: **[docs/connect-mcp.md](docs/connect-mcp.md)**.
108
+
69
109
  ## Cài đặt (npx)
70
110
 
71
111
  Sau khi đã publish lên npm, server chạy thẳng từ registry — không clone, không build:
@@ -128,34 +168,24 @@ Cấu hình MCP giống bản local, chỉ khác `command`/`args` trỏ tới `n
128
168
  > npx cache lại package sau lần chạy đầu, nên các lần sau khởi động nhanh. Dùng phiên bản ghim
129
169
  > (`webcake-landing-mcp@1.0.0`) nếu cần build tái lập được.
130
170
 
131
- ## Chạy như remote connector (Streamable HTTP)
171
+ ## Dùng server đã host sẵn — không cần cài gì
132
172
 
133
- Server còn nói được transport **remote MCP** (Streamable HTTP), nên thể thêm qua dialog
134
- **"Add custom connector"** của Claude bằng một URL không chỉ stdio local.
173
+ Server **đã chạy sẵn** tại **`https://mcp.toolvn.io.vn/mcp`** tụi mình lo hết. Không phải dựng server,
174
+ không phải giữ máy luôn bật. Chỉ cần trỏ AI của bạn vào URL là xong.
135
175
 
136
- Chạy chế độ HTTP (port mặc định `8787`, hoặc đặt `PORT` / `--port`):
176
+ **Lấy link nhân** (token gắn sẵn) cách dễ nhất mở **<https://webcake.io/mcp-remote>** rồi bấm **Copy**:
137
177
 
138
- ```bash
139
- npx -y webcake-landing-mcp serve --port 8787
140
- # → endpoint MCP tại http://localhost:8787/mcp (GET / hoặc /health trả JSON trạng thái)
178
+ ```
179
+ https://mcp.toolvn.io.vn/mcp?jwt=<TOKEN_CỦA_BẠN>
141
180
  ```
142
181
 
143
- Đưa ra **HTTPS** URL public (reverse proxy, tunnel như `ngrok http 8787`, hoặc host bất kỳ), rồi vào
144
- Claude **Add custom connector**:
145
-
146
- - **Name**: `webcake-landing`
147
- - **Remote MCP server URL**: `https://<host-của-bạn>/mcp`
148
-
149
- Dialog không có ô header, nên muốn truyền token qua đó thì **để vào URL**:
150
- `https://<host-của-bạn>/mcp?jwt=<ljwt>` (nhận thêm `&api_base=…`, `&org_id=…`, `&host=…`, `&app_base=…`).
151
- Mỗi người một URL với `jwt` riêng → **per-user mà không cần OAuth**. Header `x-webcake-jwt` thật vẫn ưu tiên
152
- hơn query. ⚠️ Token nằm trong URL có thể lọt vào access/proxy log — **bắt buộc HTTPS** và tắt log query ở
153
- reverse proxy; dùng header (hoặc OAuth) an toàn hơn nếu client hỗ trợ.
182
+ Tham số thêm: `&env=prod`, `&org_id=…`, `&api_base=…`. Mỗi đồng đội một link với `jwt` riêng mỗi người một
183
+ tài khoản, không cần OAuth. ⚠️ Link chứa token cá nhân — coi như mật khẩu, luôn dùng **HTTPS**.
154
184
 
155
- ### Auth mỗi request, đa người dùng (không token chung)
185
+ ### Gửi token qua header (an toàn hơn)
156
186
 
157
- stdio JWT lấy từ env. chế độ HTTP, mỗi request mang credential **riêng** của người gọi qua header,
158
- nên server hosted đa người dùng không nhúng secret chung:
187
+ Client nào set được header thì nên gửi token qua header thay nhét vào URL (token không lọt vào log).
188
+ Header nào thiếu sẽ fallback về biến env tương ứng:
159
189
 
160
190
  | Header | Tương ứng | Ghi chú |
161
191
  |--------|-----------|---------|
@@ -165,60 +195,12 @@ nên server hosted là đa người dùng và không nhúng secret chung:
165
195
  | `x-webcake-api-base` | `WEBCAKE_API_BASE` | ghi đè API base của preset |
166
196
  | `x-webcake-app-base` | `WEBCAKE_APP_BASE` | ghi đè app base của preset |
167
197
 
168
- Header nào thiếu thì fallback về biến env tương ứng — nên cũng chạy **một người dùng** được bằng cách đặt
169
- `WEBCAKE_API_BASE` + `WEBCAKE_JWT` trong env của host giữ URL riêng tư.
170
-
171
- > ⚠️ Tool tham chiếu + generation (`get_generation_guide`, `list_elements`, `validate_page`, …) không cần
172
- > secret; chỉ tool lưu trữ (`create_page`, `update_page`, …) dùng JWT. Request không JWT thì các tool đó
173
- > trả `missing_env` chứ không gọi mạng.
174
- >
175
- > Lưu ý: dialog claude.ai **không có ô header** (chỉ có OAuth, mà server này **chưa làm**). Hai cách lách:
176
- > để token vào URL dạng `?jwt=<ljwt>` (như trên — per-user, nhưng token lộ trong log), hoặc dùng client hỗ trợ
177
- > header (`mcp-remote --header …`, bên dưới). Đặt token ở env server thì thành **single-account** chung cho mọi người trên URL đó.
178
-
179
- ### Test ở local (không cần URL public)
180
-
181
- `localhost` không dùng được trong dialog claude.ai (Anthropic gọi URL từ server của họ). Để thử server `serve`
182
- chạy trên máy:
183
-
184
- - **MCP Inspector** (GUI — dễ nhất): `npx @modelcontextprotocol/inspector` → Transport **Streamable HTTP** →
185
- URL `http://localhost:8787/mcp` → mục Headers thêm `x-webcake-jwt` (+ `x-webcake-api-base`) → Connect → bấm gọi tool.
186
- - **`mcp-remote`** (dùng server remote từ client stdio như Claude Desktop, kèm header):
187
- ```json
188
- { "mcpServers": { "webcake-remote": { "command": "npx",
189
- "args": ["-y", "mcp-remote", "http://localhost:8787/mcp",
190
- "--header", "x-webcake-jwt:<ljwt>",
191
- "--header", "x-webcake-api-base:https://api.webcake.io"] } } }
192
- ```
193
- - **curl**: `initialize` (đọc header `mcp-session-id` trả về) → `tools/list` → `tools/call`, tất cả kèm
194
- `Accept: application/json, text/event-stream`.
195
-
196
- ### Deploy lên VPS
197
-
198
- 1. **Build + chạy như service** — `/etc/systemd/system/webcake-mcp.service`:
199
- ```ini
200
- [Service]
201
- WorkingDirectory=/opt/webcake-landing-mcp
202
- ExecStart=/usr/bin/node dist/index.js serve --port 8787
203
- Environment=WEBCAKE_API_BASE=https://api.webcake.io
204
- Environment=WEBCAKE_JWT=<ljwt> # chỉ cho single-account — xem ghi chú auth dưới
205
- Restart=always
206
- [Install]
207
- WantedBy=multi-user.target
208
- ```
209
- `sudo systemctl enable --now webcake-mcp` (build 1 lần: `npm install && npm run build`).
210
- 2. **HTTPS + domain** (claude.ai bắt buộc https) — vd Caddy tự cấp TLS, `/etc/caddy/Caddyfile`:
211
- ```
212
- mcp.yourdomain.com { reverse_proxy localhost:8787 }
213
- ```
214
- 3. **Thêm vào claude.ai** → Remote MCP server URL = `https://mcp.yourdomain.com/mcp`.
215
-
216
- **Auth trên server chia sẻ:**
217
- - **Single-account** (dùng được với dialog ngay): đặt `WEBCAKE_JWT` ở env service → mọi người dùng connector
218
- chung 1 tài khoản Webcake. Giữ URL riêng tư / có cổng chặn; token hết hạn (~90 ngày).
219
- - **Per-user** (mỗi người 1 account): cho mỗi người một URL với `?jwt=<ljwt>` riêng (chạy được qua dialog,
220
- nhưng token lộ trong log), hoặc dùng client hỗ trợ header (`mcp-remote --header …`), hoặc thêm **OAuth**
221
- (chưa làm) cho gọn nhất.
198
+ > Tool tham chiếu + generation (`get_generation_guide`, `list_elements`, `validate_page`, …) **không cần
199
+ > token** — chỉ tool lưu trữ (`create_page`, `update_page`, …) mới dùng. Không JWT thì các tool đó trả
200
+ > `missing_env` chứ không gọi mạng.
201
+
202
+ > 📖 **Hướng dẫn từng bước cho mọi IDE** (Claude Desktop, Claude Code, Cursor, Windsurf, claude.ai)
203
+ > **[docs/ket-noi-mcp.md](docs/ket-noi-mcp.md)** · English: **[docs/connect-mcp.md](docs/connect-mcp.md)**.
222
204
 
223
205
  ## Kết nối một lần — tự lấy token (`login`)
224
206
 
@@ -239,8 +221,7 @@ Nó mở browser → (đăng nhập Webcake nếu cần) → token được lưu
239
221
 
240
222
  Bạn đang đăng nhập Webcake sẵn trong browser, nên `login` chỉ mở trang "connect" của Webcake — trang này
241
223
  đọc cookie **`ljwt`** (landing) và trả token về một callback loopback nội bộ — khỏi copy-paste. Token đã lưu
242
- được dùng bởi **cả** server stdio lẫn deploy `serve` một-người-dùng (env vẫn ưu tiên). Landing JWT sống ~90
243
- ngày nên hiếm khi phải kết nối lại.
224
+ sẽ được tự đọc (env vẫn ưu tiên). Landing JWT sống ~90 ngày nên hiếm khi phải kết nối lại.
244
225
 
245
226
  Hai URL, đừng nhầm:
246
227
 
@@ -266,7 +247,7 @@ GET /mcp-connect?redirect_uri=<loopback>&state=<s>
266
247
  SPA cũng được, khỏi cần route backend.)
267
248
 
268
249
  > Remote đa người dùng (dialog claude.ai) không làm được browser loopback này — ở đó mỗi user gửi token riêng
269
- > qua header `x-webcake-jwt` (xem mục remote-connector ở trên).
250
+ > qua header `x-webcake-jwt` (xem mục server đã host sẵn ở trên).
270
251
 
271
252
  ## Biến môi trường
272
253
 
@@ -297,15 +278,14 @@ cho API + app base (mặc định là `prod`):
297
278
  | `prod` *(mặc định)* | `https://api.webcake.io` | `https://webcake.io` |
298
279
 
299
280
  ```bash
300
- node dist/index.js serve --env staging # server remote trỏ backend staging
301
- node dist/index.js login --env local # đăng nhập vào SPA + API local
302
- WEBCAKE_ENV=prod node dist/index.js # stdio, prod (dạng biến môi trường)
281
+ npx -y webcake-landing-mcp login --env local # đăng nhập vào SPA + API local
282
+ WEBCAKE_ENV=staging npx -y webcake-landing-mcp # chạy trỏ backend staging
283
+ WEBCAKE_ENV=prod npx -y webcake-landing-mcp # prod (dạng biến môi trường)
303
284
  ```
304
285
 
305
286
  `WEBCAKE_API_BASE` / `WEBCAKE_APP_BASE` (hoặc `--api-base`) tường minh vẫn ghi đè preset theo từng
306
- trường. Trên server HTTP remote, client có thể ghi đè môi trường của server theo từng request bằng
307
- header **`x-webcake-env`** hoặc query **`?env=`** (ví dụ `…/mcp?jwt=<token>&env=staging`) — nên một
308
- server phục vụ được nhiều môi trường.
287
+ trường. Trên server đã host sẵn, bạn có thể ghi đè môi trường theo từng request bằng header
288
+ **`x-webcake-env`** hoặc query **`?env=`** (ví dụ `…/mcp?jwt=<token>&env=staging`).
309
289
 
310
290
  ### Cách lấy `WEBCAKE_JWT`
311
291
 
@@ -324,93 +304,8 @@ Lệnh con `install` của npx (xem trên) tự ghi cấu hình đúng cho từn
324
304
 
325
305
  ## Ví dụ sử dụng
326
306
 
327
- ### dụ 1: Dựng landing page mới từ một brief
328
-
329
- **Prompt:**
330
- ```
331
- Dựng cho tôi một landing page WebCake cho "Acme Coffee" — một hero có CTA, một mục 3 tính năng,
332
- và một form đăng ký. Lưu vào org mặc định của tôi.
333
- ```
334
-
335
- **AI sẽ tự động:**
336
-
337
- **Bước 1** — Gọi `get_generation_guide` để học quy ước (canvas, hệ toạ độ, sự kiện, workflow)
338
-
339
- **Bước 2** — Gọi `new_page_skeleton` để có source top-level rỗng, rồi `get_element` cho từng loại element nó dùng:
340
-
341
- ```
342
- get_element({ type: "section" })
343
- get_element({ type: "text-block" })
344
- get_element({ type: "button" })
345
- get_element({ type: "form" })
346
- ```
347
-
348
- **Bước 3** — Lắp trọn JSON `{ page, popup, settings, options, cartConfigs }`, rồi kiểm tra:
349
-
350
- ```
351
- validate_page({ source })
352
- → { ok: false, errors: ["BUTTON-2: event target 'POPUP-9' not found"] } # sửa hết lỗi, validate lại
353
- validate_page({ source })
354
- → { ok: true, errors: [] }
355
- ```
356
-
357
- **Bước 4** — Lưu (dry-run trước, rồi mới thật):
358
-
359
- ```
360
- list_organizations({}) → chọn org
361
- create_page({ source }) → xem trước dry-run (JWT được che)
362
- create_page({ source, dry_run: false }) → { page_id, editor_url, preview_url }
363
- ```
364
-
365
- Mở trang trong editor và lưu lại để render `app`/`app_css`.
366
-
367
- ---
368
-
369
- ### Ví dụ 2: Sửa một trang có sẵn
370
-
371
- **Prompt:**
372
- ```
373
- Trên landing page "Acme Coffee" của tôi, đổi headline hero thành "Freshly Roasted Daily"
374
- và làm nút CTA màu xanh lá.
375
- ```
376
-
377
- **AI sửa đúng chỗ — không bao giờ dựng lại cả cây:**
378
-
379
- ```
380
- # Bước 1: tìm trang
381
- list_pages({})
382
- → [{ id: "page_42", name: "Acme Coffee", organization_id: "org_1", ... }]
383
-
384
- # Bước 2: lấy cây source đã decode
385
- get_page({ page_id: "page_42" })
386
-
387
- # Bước 3: chỉ đổi text headline + màu nút, giữ mọi id/toạ độ khác,
388
- # rồi validate và ghi lại
389
- validate_page({ source }) → ok
390
- update_page({ page_id: "page_42", source }) → xem trước dry-run
391
- update_page({ page_id: "page_42", source, dry_run: false })
392
- ```
393
-
394
- ---
395
-
396
- ### Ví dụ 3: Xem chi tiết một loại element trước khi dùng
397
-
398
- **Prompt:**
399
- ```
400
- Một element form cần những specials gì, và cho tôi xem một ví dụ hợp lệ.
401
- ```
402
-
403
- **AI gọi:**
404
-
405
- ```
406
- get_element({ type: "form" })
407
- → {
408
- hints: "Mỗi input cần một specials.field_name duy nhất…",
409
- specials: { ... },
410
- skeleton: { ... }, # node mặc định hợp lệ về cấu trúc
411
- example: { ... } # ví dụ đã điền, thực tế
412
- }
413
- ```
307
+ Ba luồng đầy đủ dựng trang từ brief, sửa trang đúng chỗ, và xem chi tiết một loại element —
308
+ nằm ở **[docs/usage-examples.vi.md](docs/usage-examples.vi.md)**.
414
309
 
415
310
  ---
416
311
 
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Brand icon for the MCP server, shared by both transports.
3
+ *
4
+ * Why this exists: an MCP client (e.g. the claude.ai custom-connector UI) shows
5
+ * a generic globe when it can't find an icon for the server. Two mechanisms can
6
+ * supply one, so we feed BOTH from this single source:
7
+ * 1. A favicon served over HTTP (`/favicon.ico` / `/favicon.svg`) — what
8
+ * favicon-style clients fetch from the server's origin.
9
+ * 2. `serverInfo.icons` in the `initialize` result (MCP spec) — a self-contained
10
+ * data URI so it works without the server knowing its own public URL.
11
+ *
12
+ * The mark is the official Webcake logo — the green tile with a white "W"
13
+ * (source: builderx_spa/src/assets/icon/webcake-small.svg).
14
+ */
15
+ // The official Webcake icon: a Webcake-green (#1DB954) rounded tile with a white
16
+ // "W" letterform. Kept inline (dependency-free) so it can be served raw AND
17
+ // inlined as a data URI. Keep in sync with the SPA's webcake-small.svg.
18
+ export const ICON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28" fill="none">' +
19
+ '<path d="M2.8285 3.64772C2.8285 3.64772 0.40187 14.238 2.8285 25.0176C2.8285 25.0176 13.5197 27.158 25.2727 25.0176C25.2727 25.0176 27.4716 14.1507 25.2727 3.65595C25.2727 3.64772 16.0217 1.34592 2.8285 3.64772Z" fill="#1DB954"/>' +
20
+ '<path d="M18.5722 19.0697H15.3989L14.4468 14.9073C14.4023 14.7328 14.3309 14.3865 14.2327 13.8684C14.1345 13.3503 14.0632 12.9167 14.0187 12.5677C13.9827 12.8509 13.9228 13.2032 13.8474 13.6231C13.7721 14.0429 13.6762 14.4299 13.6059 14.7756C13.5357 15.1214 13.1967 16.5555 12.6178 19.0565H9.4394L6.97852 9.50684H9.56784L10.6536 14.3064C10.8979 15.3656 11.0657 16.2086 11.1571 16.8354C11.2153 16.3963 11.3203 15.7926 11.4722 15.0242C11.624 14.2559 11.7656 13.6247 11.8969 13.1308L12.7737 9.51671H15.2551L16.1114 13.1308C16.2569 13.7087 16.4042 14.3788 16.5532 15.1362C16.7022 15.8936 16.8032 16.4534 16.8529 16.8354C16.9065 16.3513 17.0664 15.5116 17.3324 14.3162L18.4284 9.51671H21.0177L18.5722 19.0697Z" fill="white"/>' +
21
+ "</svg>";
22
+ export const ICON_MIME = "image/svg+xml";
23
+ // Self-contained data URI for serverInfo.icons (no public URL required).
24
+ export const ICON_DATA_URI = `data:${ICON_MIME};base64,${Buffer.from(ICON_SVG).toString("base64")}`;
25
+ // Public-facing identity reused across the server metadata.
26
+ export const BRAND = {
27
+ title: "Webcake Landing",
28
+ websiteUrl: "https://webcake.io",
29
+ };
package/dist/http.js CHANGED
@@ -16,6 +16,7 @@ import { createServer as createHttpServer } from "node:http";
16
16
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
17
17
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
18
18
  import { createServer } from "./server.js";
19
+ import { ICON_SVG, ICON_MIME } from "./branding.js";
19
20
  const MCP_PATH = "/mcp";
20
21
  function sendJson(res, status, body) {
21
22
  res.writeHead(status, { "content-type": "application/json" });
@@ -65,8 +66,25 @@ export async function startHttpServer(port) {
65
66
  const transports = new Map();
66
67
  const httpServer = createHttpServer(async (req, res) => {
67
68
  const path = (req.url ?? "").split("?")[0];
69
+ // Brand icon — clients (e.g. the claude.ai connector) fetch a favicon from the
70
+ // server origin; without one they show a generic globe. Served raw as SVG.
71
+ if (req.method === "GET" && (path === "/favicon.ico" || path === "/favicon.svg" || path === "/icon.svg")) {
72
+ res.writeHead(200, { "content-type": ICON_MIME, "cache-control": "public, max-age=86400" });
73
+ return res.end(ICON_SVG);
74
+ }
68
75
  // Lightweight health check for hosting platforms.
69
76
  if (req.method === "GET" && (path === "/" || path === "/health")) {
77
+ // A browser/connector probing the root with `Accept: text/html` gets a tiny
78
+ // page that links the favicon (helps icon discovery); programmatic probes
79
+ // (the container healthcheck uses `Accept: */*`) still get the JSON health.
80
+ const accept = String(req.headers["accept"] ?? "");
81
+ if (path === "/" && accept.includes("text/html")) {
82
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
83
+ return res.end(`<!doctype html><meta charset="utf-8"><title>Webcake Landing MCP</title>` +
84
+ `<link rel="icon" type="${ICON_MIME}" href="/favicon.svg">` +
85
+ `<body style="font-family:system-ui;padding:40px">` +
86
+ `<h2>Webcake Landing MCP</h2><p>Streamable-HTTP MCP server. Endpoint: <code>${MCP_PATH}</code>.</p></body>`);
87
+ }
70
88
  return sendJson(res, 200, { ok: true, server: "webcake-landing", transport: "streamable-http", endpoint: MCP_PATH });
71
89
  }
72
90
  if (path !== MCP_PATH)
package/dist/server.js CHANGED
@@ -9,8 +9,17 @@
9
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  import { landingDomain } from "./domains/landing/index.js";
11
11
  import { registerTools } from "./tools/index.js";
12
+ import { ICON_DATA_URI, ICON_MIME, BRAND } from "./branding.js";
12
13
  export function createServer() {
13
- const server = new McpServer({ name: "webcake-landing", version: "1.0.0" }, { instructions: landingDomain.instructions });
14
+ const server = new McpServer({
15
+ name: "webcake-landing",
16
+ version: "1.0.0",
17
+ // Shown by MCP clients (e.g. the claude.ai connector) instead of a generic
18
+ // globe. icons is per the MCP spec; the data URI keeps it self-contained.
19
+ title: BRAND.title,
20
+ websiteUrl: BRAND.websiteUrl,
21
+ icons: [{ src: ICON_DATA_URI, mimeType: ICON_MIME, sizes: ["any"] }],
22
+ }, { instructions: landingDomain.instructions });
14
23
  registerTools(server, landingDomain);
15
24
  return server;
16
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webcake-landing-mcp",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "MCP server exposing Webcake landing-page element schemas + AI usage hints, and persisting LLM-generated page sources to a Webcake backend.",
5
5
  "type": "module",
6
6
  "bin": {