@womp/mcp-ui 0.1.8 → 0.1.10

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
@@ -221,6 +221,26 @@ snapped Tinted clear back to Clear on every re-quote), `materialLabel`,
221
221
  list with `swatchUrl`, `finishes`, `colorRequired` / `colorOptional`,
222
222
  `available` and `contactUrl`.
223
223
 
224
+ **The card's knob turns must be published, or the model orders the defaults.**
225
+ A card's own tool calls go host → server → iframe and never enter the
226
+ conversation, so the model's view of the print is frozen at the arguments it
227
+ opened the card with: tune it, ask for the cart in chat, and you get a 10 cm
228
+ white one. So every change (`markDirty()`, which every control already goes
229
+ through) schedules a `syncConfig()` that does two things:
230
+
231
+ - calls **`sync_print_config`** — an app-ONLY tool (`visibility: ["app"]`, kept
232
+ out of the model's tool list) that parks the selection server-side, where
233
+ `add_to_cart` and `prepare_print` read it. This is the load-bearing half.
234
+ Colour and finish changes do not re-quote, so `estimate_print` could not have
235
+ been the sync point.
236
+ - calls **`updateModelContext`** via `pushModelContext` (shared/bridge.ts),
237
+ which is the MCP Apps channel for the same fact and lets the model *talk*
238
+ about the right print. Host-gated, and Claude web was reported to drop the
239
+ payload ([claude-ai-mcp#102]), so it is a bonus, never the mechanism. The
240
+ harness advertises the capability and logs what arrives.
241
+
242
+ [claude-ai-mcp#102]: https://github.com/anthropics/claude-ai-mcp/issues/102
243
+
224
244
  **Layout trap that cost an hour:** a grid container's implicit `auto` track
225
245
  grows to its items' min-content, and a bare `<input type="number">` is ~150px
226
246
  wide intrinsically — three of them widened every row past the card's edge while
@@ -231,6 +251,9 @@ flex basis. If a row overflows, look there first.
231
251
  The stub takes three switches for the states a default account never shows:
232
252
  `FAKE_ENTERPRISE=1` (Enterprise materials orderable), `FAKE_PRO=1` (hollow
233
253
  priced as available), `FAKE_REPAIRED=1` (the repaired / non-manifold callouts).
254
+ It also keeps the last `sync_print_config` and merges it into `add_to_cart` /
255
+ `prepare_print` the way the server does — module scope, not inside
256
+ `buildServer()`, which runs per request exactly as swan-backend's does.
234
257
 
235
258
  ### The upload card sends bytes from inside the iframe
236
259
 
@@ -243,8 +266,8 @@ own refusal text on 4xx, 409/410 terminal. It never touches the model: the
243
266
  bytes go disk → API.
244
267
 
245
268
  Two things outside this repo make that PUT possible, and both fail as a bare
246
- network error inside the card if missing (which it turns into an "open the
247
- upload page instead" button rather than a dead end):
269
+ network error inside the card if missing (the status line names that layer,
270
+ since there is no console in the sandbox):
248
271
 
249
272
  - **`connectDomains` must name the API origin**, and the origin differs per
250
273
  environment — so `bake-html.mjs` bakes `{}` for this card and swan-backend
@@ -256,7 +279,12 @@ upload page instead" button rather than a dead end):
256
279
 
257
280
  While pending the card polls the app-callable `get_upload`, so a file the agent
258
281
  pushed itself (`curl -T` from a shell that has it) flips the card to done
259
- instead of leaving a drop zone for a file that already landed. The dev stub
282
+ instead of leaving a drop zone for a file that already landed.
283
+
284
+ There is **no link out to an upload page**. swan-frontend had one at
285
+ `/upload/<token>` before this card existed; it was removed 2026-09-15 (Anuj)
286
+ so there is exactly one place a user sends a file from. The recovery for a
287
+ blocked PUT is the agent's own `curl -T`, which its tool text gives it. The dev stub
260
288
  (`dev/server.mjs`) carries `create_upload` / `get_upload` and a `PUT
261
289
  /mcp/v1/uploads/:token` sink with the GLB-magic sniff, so the refusal strip is
262
290
  reachable locally too.
@@ -276,6 +304,10 @@ yarn dev # vite; opens the harness at /dev/host/index.html
276
304
 
277
305
  Two terminals, then edit `src/` and watch the card hot-reload.
278
306
 
307
+ Someone else already on :3001? `node dev/server.mjs 3002`, then open the harness
308
+ with `?mcp=http://127.0.0.1:3002/mcp` — two sessions, one checkout, no fighting
309
+ over the port.
310
+
279
311
  ### The harness (`dev/host/`)
280
312
 
281
313
  A real MCP Apps host: `AppBridge` from `@modelcontextprotocol/ext-apps`, driving
@@ -291,6 +323,7 @@ It is deliberately faithful where being unfaithful would hide a bug:
291
323
  | host style variables sent **only** over the bridge (and a toggle to withhold them) | a `var()` shipped without a fallback |
292
324
  | the card's own `get_generation` polling, proxied to a real MCP server | a running state that never resolves |
293
325
  | **"built resource" mode** — the exact HTML `resources/read` returns, in a sandboxed opaque-origin iframe | an asset `vite-plugin-singlefile` failed to inline (blank card) |
326
+ | `updateModelContext` advertised and logged, and a stub that remembers `sync_print_config` across requests | a card whose selection never leaves the iframe — press `add_to_cart` (it sends the mesh and nothing else) and read what the stub merged |
294
327
 
295
328
  `FAKE_DURATION_MS=8000 yarn dev:mcp` lengthens a fake generation, so the
296
329
  running → polling → complete transition is worth watching. The print stubs