@womp/openai-plugin 0.6.0 → 0.8.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 CHANGED
@@ -25,13 +25,15 @@ src/
25
25
  backend.ts WompBackend: the contract the host implements
26
26
  tools/ one file per tool group (scene.ts, commerce.ts); tool.ts: shared helpers
27
27
  cards/ one folder per card (design/, quote/, order/); bridge.ts: shared host connection
28
+ printability.ts the print checks finish_scene and checkout_design make
28
29
  text.ts what tools and cards say to the model
29
- prompts/ the workflow the model follows
30
+ prompts/ the workflow the model follows: brief.ts, experience.ts, modelling.ts, hardware.ts, printability.ts; index.ts joins them
30
31
  ```
31
32
 
32
33
  ### Dependency rules
33
34
 
34
- - `server` → `tools` → `backend`, `text`, `prompts`, `cards/<card>/data.ts`.
35
+ - `server` → `tools` → `backend`, `printability`, `text`, `prompts`, `cards/<card>/data.ts`.
36
+ - `text` and `printability` → `backend` (types only).
35
37
  - `cards/<card>` → its own `data.ts` and `cards/bridge.ts`. A card never imports `tools`, `server` or `backend`.
36
38
  - Nothing imports `server` except `index`. Nothing outside this package sees more than the public API.
37
39
 
@@ -43,6 +45,7 @@ src/
43
45
  | `WompBackend` | the contract the host implements |
44
46
  | `BackendRefusal` | an error whose message reaches the model as worded |
45
47
  | `CheckoutSession`, `CompleteCheckoutArgs` | ChatGPT's payment sheet: the session it takes, and what it hands `complete_checkout` |
48
+ | `LiveView`, `ClaimLink` | what `openScene` and `claimLink` return for the design card's live view and account link |
46
49
 
47
50
  `subject` is the caller as the host established it. `onError` receives every
48
51
  error that is not a `BackendRefusal`. `instructions` is appended to the
@@ -59,15 +62,16 @@ sign-in.
59
62
 
60
63
  | Method | Does |
61
64
  |---|---|
62
- | `openScene(subject, { designId?, title? })` | finds the caller's design, or makes one, and opens a **build** of it; returns `{ designId, buildId, title }` |
65
+ | `openScene(subject, { designId?, title? })` | finds the caller's design, or makes one, and opens a **build** of it; returns `{ designId, buildId, title, live? }`, `live` only from a host that can show the design live |
63
66
  | `getSceneApi()` | the scene skill, script limits, view names, and `notes` about these designs |
64
67
  | `runScript(subject, { designId, code, purpose })` | runs one script on the design; `purpose` becomes the current build's activity, its last screenshot or a fresh render its picture; a failed script returns `ok: false` |
65
68
  | `renderScene(subject, { designId, view?, targetId? })` | renders the design; the image also becomes the current build's picture |
66
- | `finishScene(subject, { designId, title })` | marks the current build done, with this version's title |
69
+ | `getMaterials(subject)` | the materials on offer to the caller, before any design exists: each with its label, whether it prints in one colour (`colorRequired`) and its finishes, the first the default; and the largest size checkout takes |
70
+ | `analyzePrint(subject, { designId })` | the design as its scene is now, analysed as it prints at its own size and changing nothing, leaving out Areas named `REFERENCE` (bought parts modelled only to show a fit): size, whether it fits the largest size, separate pieces, and the price per piece in each material on offer |
71
+ | `finishScene(subject, { designId, title })` | marks the current build done, with this version's title; called only for a design that passed the print checks |
67
72
  | `claimLink?(subject)` | optional: a one-time link to a page that keeps the chat's designs in an account; without it the design card offers no button |
68
73
  | `getBuild(subject, { designId, buildId })` | a build's status, activity, title and picture; a build that isn't the caller's reads as empty |
69
- | `getPrintOptions(subject, { designId, longestSideCm? })` | the design priced per piece in each material on offer, with its print size, its pieces, and checkout's size limit and order minimum |
70
- | `checkoutDesign(subject, { designId, material, color?, finish?, quantity?, longestSideCm?, shipTo? })` | freezes the design as quoted and returns the quote with its checkout link; with `shipTo`, also the order to that address as a payment-sheet session |
74
+ | `checkoutDesign(subject, { designId, material, color?, finish?, quantity?, shipTo? })` | freezes the design as quoted, at its own size and without its `REFERENCE` Areas, and returns the quote with its checkout link; with `shipTo`, also the order to that address as a payment-sheet session; called only for a design that passed the print checks |
71
75
  | `completeCheckout?(subject, payment)` | optional: charges a payment made in ChatGPT's payment sheet and places the session's order; without it `checkout_design` takes no `shipTo` and there is no `complete_checkout` |
72
76
  | `getOrderStatus(subject, { orderId? })` | the caller's paid orders, newest first, and the one to show, with no name, address, email or money |
73
77
 
@@ -77,11 +81,19 @@ changes an earlier one.
77
81
  ### Plugin → model and cards
78
82
 
79
83
  - **Identity.** Every tool acts as `subject`, the signed-in Womp account. Tool arguments and client `_meta` are never read for identity. Without a subject every tool call fails; with `wwwAuthenticate`, the failure carries that challenge in `_meta["mcp/www_authenticate"]`, so ChatGPT offers to link the account.
84
+ - **Brief and review.** `open_scene` requires a brief (a full one for a new design, an edit brief to reopen one) and `finish_scene` the critic's review. Both stay in the chat: the backend never receives them.
85
+ - **Print checks.** `finish_scene` and `checkout_design` take the material, finish, colour and pieces, analyse the design as it is now (`analyzePrint`) and check it (`printability.ts`): the material and finish are on offer, a material that prints in one colour has it as `#rrggbb`, the size fits, and it prints as the pieces given (1 by default). Only a design that passes is finished (`finishScene`) or quoted (`checkoutDesign`).
86
+ - **Prompts** are five parts, each a template literal in its own tag, after one line on the workflow.
87
+ - `<brief>` (`brief.ts`): what to build: research, size, parts, proportions, silhouette, the material and the pieces.
88
+ - `<experience>` (`experience.ts`): the user, the conversation, the card, the product shot, the order of the tools, the print checks as they reach the user.
89
+ - `<modelling>` (`modelling.ts`): building, inspecting and reviewing the scene, the Womp way and the critic pass.
90
+ - `<hardware>` (`hardware.ts`): cases for real devices: the fit model in `REFERENCE` Areas, the case around it, the build guide and the Womp price.
91
+ - `<printability>` (`printability.ts`): the physical object: one material, real colours, the review before finishing, how each material prints, what finish_scene and checkout_design check, and a guide to minimums while building (walls by size, protrusions and loops, details, moving and separate parts).
80
92
  - **Errors.** A `BackendRefusal` message reaches the model as is. Any other error is one generic failure, reported to `onError`.
81
93
  - **Tool metadata states facts.** Descriptions, input schemas and server instructions say what a tool does, never how the model should behave. Behaviour lives in `prompts/` and reaches the model in `load_scene_skill`'s result.
82
94
  - **Every tool** declares `ui.visibility`, explicit annotation hints, and `securitySchemes: oauth2` with the `mcp:generate` scope. Tools only a card calls are `ui.visibility: ["app"]`.
83
95
  - **Card data** is the tool result's `structuredContent`, typed by the card's `data.ts`. Tools map backend types to it; a card never receives backend types. What only the card may see is in the result's `_meta`, typed by the same `data.ts`: a link that is a credential, and a quote or an order, which the model would otherwise draw as a card of its own.
84
- - **Cards** are MCP Apps resources (`text/html;profile=mcp-app`): one self-contained HTML file each, loading nothing from outside, with a `ui://` URI that changes with its content.
96
+ - **Cards** are MCP Apps resources (`text/html;profile=mcp-app`): one self-contained HTML file each, loading no script, style or font from outside, with a `ui://` URI that changes with its content. Only the design card frames a page: the live view, on `liveViewOrigin`.
85
97
 
86
98
  ### Tools
87
99
 
@@ -94,7 +106,6 @@ changes an earlier one.
94
106
  | `finish_scene` | model | |
95
107
  | `get_scene_progress` | `design` card | |
96
108
  | `get_claim_link` | `design` card | |
97
- | `get_print_options` | model | |
98
109
  | `checkout_design` | model | shows `quote` |
99
110
  | `complete_checkout` | model and ChatGPT's payment sheet, only with `completeCheckout` | |
100
111
  | `get_order_status` | model | shows `order` |
@@ -116,10 +127,16 @@ implementation in the same release.
116
127
  ## Development
117
128
 
118
129
  ```bash
119
- npm run dev # card preview at http://127.0.0.1:5188/preview/, live from source
120
- npm test # build the cards, run the tests
121
- npm run check # format, build, typecheck, tests (CI)
130
+ npm run dev # card preview at http://127.0.0.1:5188/preview/, live from source
131
+ npm run dev:chatgpt # this working copy in real ChatGPT on :8787, forwarding the rest to UPSTREAM (default staging)
132
+ npm test # build the cards, run the tests
133
+ npm run check # format, build, typecheck, tests (CI)
122
134
  ```
123
135
 
136
+ `dev:chatgpt` serves the instructions, tools, cards and workflow from `src/` and forwards every other call to a
137
+ deployed swan (`UPSTREAM`, default staging), signed in as you: open `http://localhost:8787/login` once; the sign-in is
138
+ kept in `dev/.token`. Expose it with a tunnel (`cloudflared tunnel --url http://localhost:8787`) and add the https URL
139
+ in ChatGPT with no authentication. New backend methods still need a swan deploy.
140
+
124
141
  Release: bump the version and tag `vX.Y.Z`; CI publishes to npm. Then bump the
125
142
  dependency in swan-backend.
package/dist/index.d.ts CHANGED
@@ -40,19 +40,12 @@ interface Build {
40
40
  title?: string;
41
41
  picture: Picture | null;
42
42
  }
43
- /**
44
- * The design live in 3D, for the card to frame: a page on the host's own origin (the
45
- * `liveViewOrigin` option) and when its key stops working. The URL carries a credential, so it goes to
46
- * the card alone, never into text the model reads.
47
- */
43
+ /** The design live in 3D, on `liveViewOrigin`, until `expiresAt`. A credential: for the card alone. */
48
44
  interface LiveView {
49
45
  url: string;
50
46
  expiresAt: string;
51
47
  }
52
- /**
53
- * A one-time link that opens a page for keeping this chat's designs and orders in a Womp account. Like `LiveView`
54
- * it is a credential: it goes to the card alone, when the user asks for it, and never into text the model reads.
55
- */
48
+ /** A one-time link to keep the chat's designs and orders in a Womp account. A credential: for the card alone. */
56
49
  interface ClaimLink {
57
50
  url: string;
58
51
  expiresAt: string;
@@ -62,34 +55,35 @@ interface Size {
62
55
  height: number;
63
56
  depth: number;
64
57
  }
65
- /** Checkout takes an order whose items cost more than `amount`; below it, it tops the items up to it (`topUp`) or refuses the order. */
58
+ /** Below `amount` in items, checkout tops the order up to it (`topUp`) or refuses it. */
66
59
  interface MinimumOrder {
67
60
  amount: number;
68
61
  topUp: boolean;
69
62
  }
70
- /** One material a design can be printed in, priced per piece. */
71
- interface PrintOption {
72
- /** The name checkout_design takes, such as "resin-clear-tinted". */
63
+ /** One material on offer for printing. */
64
+ interface Material {
65
+ /** The name finish_scene and checkout_design take, such as "resin-clear-tinted". */
73
66
  material: string;
74
67
  /** Such as "Tinted clear". */
75
68
  label: string;
69
+ /** The whole print is one dye colour, which the order names. */
70
+ colorRequired: boolean;
71
+ /** The finishes it is made in; the first is the default. */
72
+ finishes: string[];
73
+ }
74
+ /** One material a design can be printed in, priced per piece. */
75
+ interface PrintOption extends Material {
76
76
  /** Per piece, before shipping. */
77
77
  unitPrice: number;
78
78
  /** Business days to make it, once ordered. */
79
79
  productionDays: number;
80
- /** The material is dyed, so an order names a colour. */
81
- colorRequired: boolean;
82
- /** The finishes it is made in; the first is the default. */
83
- finishes: string[];
84
80
  }
85
- /** A design priced for printing in each material on offer. */
86
- interface PrintOptions {
81
+ /** A design analysed as it prints, at its own size, and priced in each material on offer. */
82
+ interface PrintAnalysis {
87
83
  designId: string;
88
84
  title: string;
89
- /** The size it prints at, in cm. */
85
+ /** In cm. */
90
86
  sizeCm: Size;
91
- /** Scaled to the longest side asked for, rather than at its own size. */
92
- resized: boolean;
93
87
  /** How many separate pieces it prints as. */
94
88
  pieces: number;
95
89
  /** The largest size checkout takes, in cm, in any orientation. */
@@ -97,7 +91,6 @@ interface PrintOptions {
97
91
  /** Whether `sizeCm` fits within `maxSizeCm`. */
98
92
  fits: boolean;
99
93
  currency: string;
100
- minimumOrder: MinimumOrder;
101
94
  options: PrintOption[];
102
95
  }
103
96
  /** Where an order paid in ChatGPT's payment sheet ships. */
@@ -112,10 +105,7 @@ interface ShipTo {
112
105
  country: string;
113
106
  phoneNumber?: string;
114
107
  }
115
- /**
116
- * The user's own checkout on the web, where they enter an address and pay. Like `ClaimLink` it is a credential: it goes
117
- * to the card alone, never into text the model reads.
118
- */
108
+ /** The user's own checkout on the web. A credential: for the card alone. */
119
109
  interface CheckoutLink {
120
110
  url: string;
121
111
  expiresAt: string;
@@ -227,10 +217,7 @@ interface OrderSummary {
227
217
  id: number;
228
218
  statusLabel: string;
229
219
  }
230
- /**
231
- * An order as the chat may show it. A checkout link can be forwarded and its buyer's order is found too, so it carries
232
- * no name, address, email or money.
233
- */
220
+ /** An order as the chat may show it: no name, address, email or money. */
234
221
  interface Order extends OrderSummary {
235
222
  step: "paid" | "printing" | "shipped" | "delivered" | "canceled";
236
223
  /** Only the items made from this user's checkout links. */
@@ -243,7 +230,7 @@ interface Order extends OrderSummary {
243
230
  carrier: string;
244
231
  trackingNumber: string;
245
232
  }[];
246
- /** When it leaves the factory and when it arrives, as happened or expected (ISO); null when it won't arrive or can't be dated. */
233
+ /** When it leaves the factory and arrives (ISO); null when it can't be dated. */
247
234
  timeline: {
248
235
  factoryShippingStartedAt: string;
249
236
  deliveredAt: string;
@@ -273,23 +260,25 @@ interface WompBackend {
273
260
  view?: string;
274
261
  targetId?: number;
275
262
  }): Promise<Image>;
263
+ /** The materials on offer to this user, and the largest size checkout takes, before any design exists. */
264
+ getMaterials(subject: string): Promise<{
265
+ materials: Material[];
266
+ maxSizeCm: [number, number, number];
267
+ }>;
268
+ /** The design as its scene is now, analysed and priced. Changes nothing. */
269
+ analyzePrint(subject: string, input: {
270
+ designId: string;
271
+ }): Promise<PrintAnalysis>;
276
272
  finishScene(subject: string, input: {
277
273
  designId: string;
278
274
  title: string;
279
275
  }): Promise<void>;
280
- /**
281
- * Optional: a host that cannot keep a chat's designs in an account leaves it out, and the card shows no button.
282
- * Returns undefined when it has no link to give right now.
283
- */
276
+ /** Optional: without it the card offers no account button. Undefined when no link is available now. */
284
277
  claimLink?(subject: string): Promise<ClaimLink | undefined>;
285
278
  getBuild(subject: string, input: {
286
279
  designId: string;
287
280
  buildId: string;
288
281
  }): Promise<Build>;
289
- getPrintOptions(subject: string, input: {
290
- designId: string;
291
- longestSideCm?: number;
292
- }): Promise<PrintOptions>;
293
282
  /** Freezes the design as quoted, so its order prints it as it is now. `shipTo` comes only to a host with `completeCheckout`. */
294
283
  checkoutDesign(subject: string, input: {
295
284
  designId: string;
@@ -297,14 +286,9 @@ interface WompBackend {
297
286
  color?: string;
298
287
  finish?: string;
299
288
  quantity?: number;
300
- longestSideCm?: number;
301
289
  shipTo?: ShipTo;
302
290
  }): Promise<Checkout>;
303
- /**
304
- * Optional: paying in ChatGPT's payment sheet. It charges the payment the sheet hands over and places the session's
305
- * order; a refused payment returns the session with an error message. A host that leaves it out takes no address in
306
- * checkout_design, and checkout is on the web alone.
307
- */
291
+ /** Optional: charges a payment from ChatGPT's payment sheet and places the order; without it, checkout is on the web alone. */
308
292
  completeCheckout?(subject: string, payment: CompleteCheckoutArgs): Promise<CheckoutSession>;
309
293
  /** The user's paid orders, newest first, and the one to show: `orderId` when it is one of theirs, else the newest. */
310
294
  getOrderStatus(subject: string, input: {
@@ -322,23 +306,11 @@ interface ServerOptions {
322
306
  subject?: string;
323
307
  onError?: (tool: string, error: unknown) => void;
324
308
  instructions?: string;
325
- /**
326
- * The one origin the card may frame for a live view, such as "https://app.womp.com". Without it the
327
- * card allows no frames and `live` from the backend is ignored; an `http:` origin, a path or anything
328
- * that is not an origin is the same as none.
329
- */
309
+ /** The one https origin the design card may frame for a live view; without it, no live view. */
330
310
  liveViewOrigin?: string;
331
- /**
332
- * The origins of the web pages the cards open — checkout, an order's status, keeping designs in an account — such as
333
- * ["https://app.womp.com"]. ChatGPT opens a page on one of them without asking the user first; anything that is not
334
- * an https origin is left out.
335
- */
311
+ /** The https origins of the pages the cards open, which ChatGPT then opens without asking. */
336
312
  linkOrigins?: string[];
337
- /**
338
- * The host's OAuth challenge, a `WWW-Authenticate` value, for a request whose sign-in is missing or not valid. With
339
- * it and no subject, every tool call fails with the challenge in `_meta["mcp/www_authenticate"]`, which is what
340
- * makes ChatGPT offer to link the account.
341
- */
313
+ /** The host's `WWW-Authenticate` challenge for a request without a valid sign-in. */
342
314
  wwwAuthenticate?: string;
343
315
  }
344
316
  declare function createServer({ backend, subject, onError, instructions, liveViewOrigin, linkOrigins, wwwAuthenticate }: ServerOptions): McpServer;