@waniwani/sdk 0.0.16 → 0.1.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.
@@ -263,15 +263,6 @@ interface UnifiedWidgetClient {
263
263
  onWidgetStateChange(callback: (state: UnknownObject | null) => void): () => void;
264
264
  }
265
265
 
266
- /**
267
- * Context passed to widget handlers
268
- */
269
- type WidgetHandlerContext = {
270
- /** Raw MCP request extra data (includes _meta for session extraction) */
271
- extra?: {
272
- _meta?: Record<string, unknown>;
273
- };
274
- };
275
266
  type WidgetCSP = {
276
267
  /** Domains permitted for fetch/XHR network requests */
277
268
  connect_domains?: string[];
@@ -282,59 +273,36 @@ type WidgetCSP = {
282
273
  /** Origins that can receive openExternal redirects without safe-link modal */
283
274
  redirect_domains?: string[];
284
275
  };
285
- type WidgetConfig<TInput extends ZodRawShapeCompat> = {
286
- /** Unique identifier for the widget/tool */
276
+ type ResourceConfig = {
277
+ /** Unique identifier for the resource */
287
278
  id: string;
288
279
  /** Display title */
289
280
  title: string;
290
- /** Action-oriented description for the tool (tells the model WHEN to use it) */
291
- description: string;
292
- /** UI component description (describes WHAT the widget displays). Falls back to description if not provided. */
293
- widgetDescription?: string;
294
- /** Base URL for fetching widget HTML */
281
+ /** UI description (describes WHAT the resource displays) */
282
+ description?: string;
283
+ /** Base URL for fetching the HTML */
295
284
  baseUrl: string;
296
- /** Path to fetch HTML from (relative to baseUrl) */
285
+ /** Path to the HTML file (relative to baseUrl) */
297
286
  htmlPath: string;
298
- /** Input schema using zod */
299
- inputSchema: TInput;
300
- /** Optional loading message (defaults to "Loading...") */
301
- invoking?: string;
302
- /** Optional loaded message (defaults to "Loaded") */
303
- invoked?: string;
304
- /** Optional widget domain for security context */
305
- widgetDomain?: string;
306
- /** Optional: whether widget prefers border (defaults to true) */
287
+ /** Domain for OpenAI security context */
288
+ widgetDomain: string;
289
+ /** Whether widget prefers border (defaults to true) */
307
290
  prefersBorder?: boolean;
308
- /** Optional: when true, the iframe height auto-adapts to its content instead of using a fixed height */
291
+ /** When true, the iframe height auto-adapts to its content */
309
292
  autoHeight?: boolean;
310
- /** Content Security Policy configuration (required for app submission) */
293
+ /** Content Security Policy configuration */
311
294
  widgetCSP?: WidgetCSP;
312
- /** Optional: Annotations describe the tool’s potential impact. ChatGPT uses these hints to classify tools and decide when to ask the user for confirmation (elicitation) before using the tool.
313
- *
314
- * Note: openWorldHint and destructiveHint are only considered for writes (i.e. when readOnlyHint=false).
315
- */
316
- annotations?: {
317
- /** Optional: Set to true for tools that do not change state (search, lookups, previews). This won't require elicitation. */
318
- readOnlyHint?: boolean;
319
- /** Optional: Set to true for tools where calling multiple times with the same args has no additional effect. */
320
- idempotentHint?: boolean;
321
- /** Optional: Set to false for tools that only affect a bounded target (for example, "update a task by id" in your own product). Leave true for tools that can write to arbitrary URLs/files/resources. */
322
- openWorldHint?: boolean;
323
- /** Optional: Set to true for tools that can delete, overwrite, or have irreversible side effects. */
324
- destructiveHint?: boolean;
325
- };
326
295
  };
327
- type WidgetHandler<TInput extends ZodRawShapeCompat> = (input: ShapeOutput<TInput>, context: WidgetHandlerContext) => Promise<{
328
- /** Text content to return */
329
- text: string;
330
- /** Structured data to pass to the widget */
331
- data: Record<string, unknown>;
332
- }>;
333
- type WidgetToolCallback<TInput extends ZodRawShapeCompat> = ToolCallback<TInput>;
334
- type RegisteredWidget = {
335
- id: string;
336
- title: string;
337
- description: string;
296
+ type RegisteredResource = {
297
+ readonly id: string;
298
+ readonly title: string;
299
+ readonly description: string | undefined;
300
+ /** OpenAI URI: ui://widgets/apps-sdk/{id}.html */
301
+ readonly openaiUri: string;
302
+ /** MCP URI: ui://widgets/ext-apps/{id}.html */
303
+ readonly mcpUri: string;
304
+ readonly autoHeight: boolean | undefined;
305
+ /** Register this resource on an McpServer */
338
306
  register: (server: McpServer) => Promise<void>;
339
307
  };
340
308
 
@@ -355,8 +323,8 @@ type InterruptSignal = {
355
323
  };
356
324
  type WidgetSignal = {
357
325
  readonly __type: typeof WIDGET;
358
- /** ID of a registered widget to display */
359
- widgetId: string;
326
+ /** The resource to display */
327
+ resource: RegisteredResource;
360
328
  /** Data to pass to the widget as structuredContent */
361
329
  data: Record<string, unknown>;
362
330
  /** Description of what the widget does (for the AI's context) */
@@ -374,12 +342,16 @@ declare function interrupt(config: {
374
342
  /**
375
343
  * Create a widget signal — pauses the flow and renders a widget UI.
376
344
  */
377
- declare function showWidget(config: {
378
- widgetId: string;
345
+ declare function showWidget(resource: RegisteredResource, config: {
379
346
  data: Record<string, unknown>;
380
347
  description?: string;
381
348
  }): WidgetSignal;
382
349
  type MaybePromise<T> = T | Promise<T>;
350
+ /** Configuration for a flow node */
351
+ type NodeConfig = {
352
+ /** Resource to display when this node returns a WidgetSignal */
353
+ resource?: RegisteredResource;
354
+ };
383
355
  /**
384
356
  * Node handler — a single function type for all node kinds.
385
357
  * The return value determines behavior:
@@ -408,15 +380,8 @@ type FlowConfig = {
408
380
  destructiveHint?: boolean;
409
381
  };
410
382
  };
411
- type CompileOptions = {
412
- /** Map of widget IDs to their RegisteredWidget, for resolving widget resource URIs */
413
- widgetRefs?: Record<string, {
414
- id: string;
415
- }>;
416
- };
417
-
418
383
  /**
419
- * A compiled flow — compatible with RegisteredWidget for registration.
384
+ * A compiled flow — can be registered on an McpServer.
420
385
  */
421
386
  type RegisteredFlow = {
422
387
  id: string;
@@ -449,14 +414,13 @@ declare class StateGraph<TState extends Record<string, unknown>> {
449
414
  private config;
450
415
  constructor(config: FlowConfig);
451
416
  /**
452
- * Add a node to the graph.
453
- *
454
- * The handler's return value determines the node type:
455
- * - Returns `Partial<TState>` → **action node**: state is merged, auto-advances to next node
456
- * - Returns `interrupt(...)` → **interrupt node**: pauses flow, asks user a question
457
- * - Returns `showWidget(...)` → **widget node**: pauses flow, renders a widget UI
417
+ * Add a node with just a handler.
458
418
  */
459
419
  addNode(name: string, handler: NodeHandler<TState>): this;
420
+ /**
421
+ * Add a node with config (e.g., resource) and a handler.
422
+ */
423
+ addNode(name: string, config: NodeConfig, handler: NodeHandler<TState>): this;
460
424
  /**
461
425
  * Add a direct edge between two nodes.
462
426
  *
@@ -475,7 +439,7 @@ declare class StateGraph<TState extends Record<string, unknown>> {
475
439
  *
476
440
  * Validates the graph structure and returns a registration-compatible object.
477
441
  */
478
- compile(options?: CompileOptions): RegisteredFlow;
442
+ compile(): RegisteredFlow;
479
443
  private validate;
480
444
  }
481
445
 
@@ -504,31 +468,103 @@ declare class StateGraph<TState extends Record<string, unknown>> {
504
468
  declare function createFlow<TState extends Record<string, unknown>>(config: FlowConfig): StateGraph<TState>;
505
469
 
506
470
  /**
507
- * Creates a widget with minimal boilerplate.
471
+ * Creates a reusable UI resource (HTML template) that can be attached
472
+ * to tools or flow nodes.
508
473
  *
509
474
  * @example
510
475
  * ```ts
511
- * const weatherWidget = createWidget({
512
- * id: "show_weather",
513
- * title: "Show Weather",
514
- * description: "Displays weather information for a city",
476
+ * const pricingUI = createResource({
477
+ * id: "pricing_table",
478
+ * title: "Pricing Table",
515
479
  * baseUrl: "https://my-app.com",
516
- * htmlPath: "/weather",
517
- * inputSchema: {
518
- * city: z.string().describe("The city name"),
519
- * },
520
- * }, async ({ city }) => ({
521
- * text: `Weather for ${city}`,
522
- * data: { city, temperature: 72 },
480
+ * htmlPath: "/widgets/pricing",
481
+ * widgetDomain: "my-app.com",
482
+ * });
483
+ *
484
+ * await pricingUI.register(server);
485
+ * ```
486
+ */
487
+ declare function createResource(config: ResourceConfig): RegisteredResource;
488
+
489
+ type ToolHandlerContext = {
490
+ /** Raw MCP request extra data (includes _meta for session extraction) */
491
+ extra?: {
492
+ _meta?: Record<string, unknown>;
493
+ };
494
+ };
495
+ type ToolConfig<TInput extends ZodRawShapeCompat> = {
496
+ /** The resource (HTML template) this tool renders. When present, tool returns structuredContent + widget _meta. */
497
+ resource?: RegisteredResource;
498
+ /** Tool identifier. Defaults to resource.id when resource is present, required otherwise. */
499
+ id?: string;
500
+ /** Display title. Defaults to resource.title when resource is present, required otherwise. */
501
+ title?: string;
502
+ /** Action-oriented description for the tool (tells the model WHEN to use it) */
503
+ description: string;
504
+ /** UI component description (describes WHAT the widget displays). Falls back to description. Only relevant when resource is present. */
505
+ widgetDescription?: string;
506
+ /** Input schema using zod */
507
+ inputSchema: TInput;
508
+ /** Optional loading message (defaults to "Loading..."). Only relevant when resource is present. */
509
+ invoking?: string;
510
+ /** Optional loaded message (defaults to "Loaded"). Only relevant when resource is present. */
511
+ invoked?: string;
512
+ /** Annotations describe the tool's potential impact. */
513
+ annotations?: {
514
+ readOnlyHint?: boolean;
515
+ idempotentHint?: boolean;
516
+ openWorldHint?: boolean;
517
+ destructiveHint?: boolean;
518
+ };
519
+ };
520
+ type ToolHandler<TInput extends ZodRawShapeCompat> = (input: ShapeOutput<TInput>, context: ToolHandlerContext) => Promise<{
521
+ /** Text content to return */
522
+ text: string;
523
+ /** Structured data to pass to the widget. Only meaningful when resource is present. */
524
+ data?: Record<string, unknown>;
525
+ }>;
526
+ type ToolToolCallback<TInput extends ZodRawShapeCompat> = ToolCallback<TInput>;
527
+ type RegisteredTool = {
528
+ id: string;
529
+ title: string;
530
+ description: string;
531
+ /** Register the tool on the server */
532
+ register: (server: McpServer) => Promise<void>;
533
+ };
534
+
535
+ /**
536
+ * Creates an MCP tool with minimal boilerplate.
537
+ *
538
+ * When `config.resource` is provided, the tool returns `structuredContent` + widget metadata.
539
+ * Without a resource, the tool returns plain text content.
540
+ *
541
+ * @example
542
+ * ```ts
543
+ * // Widget tool (with resource)
544
+ * const pricingTool = createTool({
545
+ * resource: pricingUI,
546
+ * description: "Show pricing comparison",
547
+ * inputSchema: { postalCode: z.string() },
548
+ * }, async ({ postalCode }) => ({
549
+ * text: "Pricing loaded",
550
+ * data: { postalCode, prices: [] },
551
+ * }));
552
+ *
553
+ * // Plain tool (no resource)
554
+ * const searchTool = createTool({
555
+ * id: "search",
556
+ * title: "Search",
557
+ * description: "Search the knowledge base",
558
+ * inputSchema: { query: z.string() },
559
+ * }, async ({ query }) => ({
560
+ * text: `Results for "${query}"`,
523
561
  * }));
524
562
  * ```
525
563
  */
526
- declare function createWidget<TInput extends z.ZodRawShape>(config: WidgetConfig<TInput> & {
527
- widgetDomain: string;
528
- }, handler: WidgetHandler<TInput>): RegisteredWidget;
564
+ declare function createTool<TInput extends z.ZodRawShape>(config: ToolConfig<TInput>, handler: ToolHandler<TInput>): RegisteredTool;
529
565
  /**
530
- * Registers multiple widgets on the server
566
+ * Registers multiple tools on the server
531
567
  */
532
- declare function registerWidgets(server: McpServer, widgets: RegisteredWidget[]): Promise<void>;
568
+ declare function registerTools(server: McpServer, tools: RegisteredTool[]): Promise<void>;
533
569
 
534
- export { type CompileOptions, type ConditionFn, END, type FlowConfig, type HostContext, type InterruptSignal, type NodeHandler, type RegisteredFlow, type RegisteredWidget, START, StateGraph, type ToolCallResult, type ToolResult, type UnifiedWidgetClient, type WidgetCSP, type WidgetConfig, type WidgetHandler, type WidgetHandlerContext, type WidgetPlatform, type WidgetSignal, type WidgetToolCallback, createFlow, createWidget, detectPlatform, interrupt, isMCPApps, isOpenAI, registerWidgets, showWidget };
570
+ export { type ConditionFn, END, type FlowConfig, type HostContext, type InterruptSignal, type NodeConfig, type NodeHandler, type RegisteredFlow, type RegisteredResource, type RegisteredTool, type ResourceConfig, START, StateGraph, type ToolCallResult, type ToolConfig, type ToolHandler, type ToolHandlerContext, type ToolResult, type ToolToolCallback, type UnifiedWidgetClient, type WidgetCSP, type WidgetPlatform, type WidgetSignal, createFlow, createResource, createTool, detectPlatform, interrupt, isMCPApps, isOpenAI, registerTools, showWidget };
package/dist/mcp/index.js CHANGED
@@ -1,4 +1,4 @@
1
- function _(){return typeof window<"u"&&"openai"in window?"openai":"mcp-apps"}function z(){return _()==="openai"}function G(){return _()==="mcp-apps"}var c="__start__",l="__end__",F=Symbol.for("waniwani.flow.interrupt"),O=Symbol.for("waniwani.flow.widget");function D(t){return{__type:F,...t}}function A(t){return{__type:O,...t}}function P(t){return typeof t=="object"&&t!==null&&"__type"in t&&t.__type===F}function H(t){return typeof t=="object"&&t!==null&&"__type"in t&&t.__type===O}import{z as u}from"zod";function L(t){return["","## FLOW EXECUTION PROTOCOL","","This tool implements a multi-step conversational flow. Follow this protocol exactly:","",'1. Call with `action: "start"` to begin.',"2. The response JSON `status` field tells you what to do next:",' - `"interrupt"`: Ask the user the `question`. If a `context` field is present,'," use it as hidden instructions to enrich your response (do NOT show it verbatim)."," Then call again with:",' `action: "continue"`, `step` = the returned `step`, `state` = the returned `state`,'," `answer` = the user's answer.",' - `"widget"`: A widget UI is being shown. Do NOT call this tool again \u2014 the widget handles the callback.',' - `"complete"`: The flow is done. Present the result to the user.',' - `"error"`: Something went wrong. Show the `error` message.',"","3. ALWAYS pass back the `state` object exactly as received.","4. Do NOT skip steps or invent state values."].join(`
2
- `)}async function C(t,e){return t.type==="direct"?t.to:t.condition(e)}function Y(t){let e=`ui://widgets/apps-sdk/${t}.html`,r=`ui://widgets/ext-apps/${t}.html`;return{"openai/outputTemplate":e,"openai/widgetAccessible":!0,"openai/resultCanProduceWidget":!0,ui:{resourceUri:r}}}async function E(t,e,r,i,w,S){let d=t,o={...e},p=50,g=0;for(;g++<p;){if(d===l)return{text:JSON.stringify({status:"complete",state:o}),data:{status:"complete",state:o}};let a=r.get(d);if(!a)return{text:JSON.stringify({status:"error",error:`Unknown node: "${d}"`}),data:{status:"error"}};try{let n=await a(o,S);if(P(n))return{text:JSON.stringify({status:"interrupt",step:d,question:n.question,field:n.field,suggestions:n.suggestions,...n.context?{context:n.context}:{},state:o}),data:{status:"interrupt",step:d,state:o}};if(H(n))return{text:JSON.stringify({status:"widget",step:d,widgetId:n.widgetId,description:n.description,state:o}),data:{...n.data,__flow:{flowId:w,step:d,state:o}},widgetMeta:Y(n.widgetId)};o={...o,...n};let s=i.get(d);if(!s)return{text:JSON.stringify({status:"error",error:`No outgoing edge from node "${d}"`}),data:{status:"error"}};d=await C(s,o)}catch(n){let s=n instanceof Error?n.message:String(n);return{text:JSON.stringify({status:"error",step:d,error:s,state:o}),data:{status:"error",error:s}}}}return{text:JSON.stringify({status:"error",error:"Flow exceeded maximum iterations (possible infinite loop)"}),data:{status:"error"}}}var X={action:u.enum(["start","continue","widget_result"]).describe('"start" to begin the flow, "continue" after the user answers a question, "widget_result" when a widget returns data'),step:u.string().optional().describe("Current step name (from the previous response)"),state:u.record(u.string(),u.unknown()).optional().describe("Flow state \u2014 pass back exactly as received"),answer:u.string().optional().describe("The user's answer (for interrupt steps)"),widgetResult:u.record(u.string(),u.unknown()).optional().describe("Data returned by a widget callback")};function $(t){let{config:e,nodes:r,edges:i}=t,w=L(e),S=`${e.description}
3
- ${w}`;async function d(o,p){let g=o.state??{};if(o.action==="start"){let a=i.get(c);if(!a)return{text:JSON.stringify({status:"error",error:"No start edge"}),data:{status:"error"}};let n=await C(a,g);return E(n,g,r,i,e.id,p)}if(o.action==="continue"){if(!o.step)return{text:JSON.stringify({status:"error",error:'Missing "step" for continue action'}),data:{status:"error"}};let a={...g};if(o.answer){let R=r.get(o.step);if(R)try{let h=await R(a,p);P(h)&&h.field&&(a={...a,[h.field]:o.answer})}catch{}}let n=i.get(o.step);if(!n)return{text:JSON.stringify({status:"error",error:`No edge from step "${o.step}"`}),data:{status:"error"}};let s=await C(n,a);return E(s,a,r,i,e.id,p)}if(o.action==="widget_result"){if(!o.step)return{text:JSON.stringify({status:"error",error:'Missing "step" for widget_result action'}),data:{status:"error"}};let a={...g,...o.widgetResult??{}},n=i.get(o.step);if(!n)return{text:JSON.stringify({status:"error",error:`No edge from step "${o.step}"`}),data:{status:"error"}};let s=await C(n,a);return E(s,a,r,i,e.id,p)}return{text:JSON.stringify({status:"error",error:`Unknown action: "${o.action}"`}),data:{status:"error"}}}return{id:e.id,title:e.title,description:S,async register(o){o.registerTool(e.id,{title:e.title,description:S,inputSchema:X,annotations:e.annotations},(async(p,g)=>{let n=g._meta??{},s=await d(p,n);return{content:[{type:"text",text:s.text}],structuredContent:s.data,_meta:{...s.widgetMeta??{},...n}}}))}}}var y=class{nodes=new Map;edges=new Map;config;constructor(e){this.config=e}addNode(e,r){if(e===c||e===l)throw new Error(`"${e}" is a reserved name and cannot be used as a node name`);if(this.nodes.has(e))throw new Error(`Node "${e}" already exists`);return this.nodes.set(e,r),this}addEdge(e,r){if(this.edges.has(e))throw new Error(`Node "${e}" already has an outgoing edge. Use addConditionalEdge for branching.`);return this.edges.set(e,{type:"direct",to:r}),this}addConditionalEdge(e,r){if(this.edges.has(e))throw new Error(`Node "${e}" already has an outgoing edge.`);return this.edges.set(e,{type:"conditional",condition:r}),this}compile(e){return this.validate(),$({config:this.config,nodes:new Map(this.nodes),edges:new Map(this.edges),widgetRefs:e?.widgetRefs})}validate(){if(!this.edges.has(c))throw new Error('Flow must have an entry point. Add an edge from START: .addEdge(START, "first_node")');let e=this.edges.get(c);if(e?.type==="direct"&&e.to!==l&&!this.nodes.has(e.to))throw new Error(`START edge references non-existent node: "${e.to}"`);for(let[r,i]of this.edges){if(r!==c&&!this.nodes.has(r))throw new Error(`Edge from non-existent node: "${r}"`);if(i.type==="direct"&&i.to!==l&&!this.nodes.has(i.to))throw new Error(`Edge from "${r}" references non-existent node: "${i.to}"`)}for(let[r]of this.nodes)if(!this.edges.has(r))throw new Error(`Node "${r}" has no outgoing edge. Add one with .addEdge("${r}", ...) or .addConditionalEdge("${r}", ...)`)}};function q(t){return new y(t)}var U="text/html+skybridge",J="text/html;profile=mcp-app",Z=async(t,e)=>{let r=t.endsWith("/")?t.slice(0,-1):t;return await(await fetch(`${r}${e}`)).text()};function K(t){return{"openai/widgetDescription":t.description,"openai/widgetPrefersBorder":t.prefersBorder,"openai/widgetDomain":t.widgetDomain,...t.widgetCSP&&{"openai/widgetCSP":t.widgetCSP}}}function Q(t){let e=t.widgetCSP?{connectDomains:t.widgetCSP.connect_domains,resourceDomains:t.widgetCSP.resource_domains,frameDomains:t.widgetCSP.frame_domains,redirectDomains:t.widgetCSP.redirect_domains}:void 0;return{ui:{...e&&{csp:e},...t.prefersBorder!==void 0&&{prefersBorder:t.prefersBorder}}}}function V(t){return{"openai/outputTemplate":t.openaiTemplateUri,"openai/toolInvocation/invoking":t.invoking,"openai/toolInvocation/invoked":t.invoked,"openai/widgetAccessible":!0,"openai/resultCanProduceWidget":!0,ui:{resourceUri:t.mcpTemplateUri,...t.autoHeight&&{autoHeight:!0}}}}function tt(t,e){let{id:r,title:i,description:w,widgetDescription:S,baseUrl:d,htmlPath:o,inputSchema:p,invoking:g="Loading...",invoked:a="Loaded",widgetDomain:n,prefersBorder:s=!0,autoHeight:R,widgetCSP:h,annotations:B}=t,x=S??w,N=`ui://widgets/apps-sdk/${r}.html`,v=`ui://widgets/ext-apps/${r}.html`;return{id:r,title:i,description:w,async register(k){let b=await Z(d,o),W=V({openaiTemplateUri:N,mcpTemplateUri:v,invoking:g,invoked:a,autoHeight:R});k.registerResource(`${r}-openai-widget`,N,{title:i,description:x,mimeType:U,_meta:{"openai/widgetDescription":x,"openai/widgetPrefersBorder":s}},async T=>({contents:[{uri:T.href,mimeType:U,text:b,_meta:K({description:x,prefersBorder:s,widgetDomain:n,widgetCSP:h})}]})),k.registerResource(`${r}-mcp-widget`,v,{title:i,description:x,mimeType:J,_meta:{ui:{prefersBorder:s}}},async T=>({contents:[{uri:T.href,mimeType:J,text:b,_meta:Q({description:x,prefersBorder:s,widgetCSP:h})}]})),k.registerTool(r,{title:i,description:w,inputSchema:p,annotations:B,_meta:W},(async(T,j)=>{let M=j._meta??{},I=await e(T,{extra:{_meta:M}});return{content:[{type:"text",text:I.text}],structuredContent:I.data,_meta:{...W,...M}}}))}}}async function et(t,e){await Promise.all(e.map(r=>r.register(t)))}export{l as END,c as START,y as StateGraph,q as createFlow,tt as createWidget,_ as detectPlatform,D as interrupt,G as isMCPApps,z as isOpenAI,et as registerWidgets,A as showWidget};
1
+ function E(){return typeof window<"u"&&"openai"in window?"openai":"mcp-apps"}function B(){return E()==="openai"}function j(){return E()==="mcp-apps"}var w="__start__",S="__end__",b=Symbol.for("waniwani.flow.interrupt"),I=Symbol.for("waniwani.flow.widget");function F(e){return{__type:b,...e}}function O(e,t){return{__type:I,resource:e,...t}}function P(e){return typeof e=="object"&&e!==null&&"__type"in e&&e.__type===b}function A(e){return typeof e=="object"&&e!==null&&"__type"in e&&e.__type===I}import{z as h}from"zod";function Y(e){return["","## FLOW EXECUTION PROTOCOL","","This tool implements a multi-step conversational flow. Follow this protocol exactly:","",'1. Call with `action: "start"` to begin.',"2. The response JSON `status` field tells you what to do next:",' - `"interrupt"`: Ask the user the `question`. If a `context` field is present,'," use it as hidden instructions to enrich your response (do NOT show it verbatim)."," Then call again with:",' `action: "continue"`, `step` = the returned `step`, `state` = the returned `state`,'," `answer` = the user's answer.",' - `"widget"`: A widget UI is being shown. Do NOT call this tool again \u2014 the widget handles the callback.',' - `"complete"`: The flow is done. Present the result to the user.',' - `"error"`: Something went wrong. Show the `error` message.',"","3. ALWAYS pass back the `state` object exactly as received.","4. Do NOT skip steps or invent state values."].join(`
2
+ `)}async function y(e,t){return e.type==="direct"?e.to:e.condition(t)}async function k(e,t,r,i,l,T){let a=e,o={...t},p=50,u=0;for(;u++<p;){if(a===S)return{text:JSON.stringify({status:"complete",state:o}),data:{status:"complete",state:o}};let s=r.get(a);if(!s)return{text:JSON.stringify({status:"error",error:`Unknown node: "${a}"`}),data:{status:"error"}};try{let n=await s(o,T);if(P(n))return{text:JSON.stringify({status:"interrupt",step:a,question:n.question,field:n.field,suggestions:n.suggestions,...n.context?{context:n.context}:{},state:o}),data:{status:"interrupt",step:a,state:o}};if(A(n)){let g=n.resource;return{text:JSON.stringify({status:"widget",step:a,widgetId:g.id,description:n.description,state:o}),data:{...n.data,__flow:{flowId:l,step:a,state:o}},widgetMeta:{"openai/outputTemplate":g.openaiUri,"openai/widgetAccessible":!0,"openai/resultCanProduceWidget":!0,ui:{resourceUri:g.mcpUri}}}}o={...o,...n};let d=i.get(a);if(!d)return{text:JSON.stringify({status:"error",error:`No outgoing edge from node "${a}"`}),data:{status:"error"}};a=await y(d,o)}catch(n){let d=n instanceof Error?n.message:String(n);return{text:JSON.stringify({status:"error",step:a,error:d,state:o}),data:{status:"error",error:d}}}}return{text:JSON.stringify({status:"error",error:"Flow exceeded maximum iterations (possible infinite loop)"}),data:{status:"error"}}}var z={action:h.enum(["start","continue","widget_result"]).describe('"start" to begin the flow, "continue" after the user answers a question, "widget_result" when a widget returns data'),step:h.string().optional().describe("Current step name (from the previous response)"),state:h.record(h.string(),h.unknown()).optional().describe("Flow state \u2014 pass back exactly as received"),answer:h.string().optional().describe("The user's answer (for interrupt steps)"),widgetResult:h.record(h.string(),h.unknown()).optional().describe("Data returned by a widget callback")};function H(e){let{config:t,nodes:r,edges:i}=e,l=Y(t),T=`${t.description}
3
+ ${l}`;async function a(o,p){let u=o.state??{};if(o.action==="start"){let s=i.get(w);if(!s)return{text:JSON.stringify({status:"error",error:"No start edge"}),data:{status:"error"}};let n=await y(s,u);return k(n,u,r,i,t.id,p)}if(o.action==="continue"){if(!o.step)return{text:JSON.stringify({status:"error",error:'Missing "step" for continue action'}),data:{status:"error"}};let s={...u};if(o.answer){let g=r.get(o.step);if(g)try{let c=await g(s,p);P(c)&&c.field&&(s={...s,[c.field]:o.answer})}catch{}}let n=i.get(o.step);if(!n)return{text:JSON.stringify({status:"error",error:`No edge from step "${o.step}"`}),data:{status:"error"}};let d=await y(n,s);return k(d,s,r,i,t.id,p)}if(o.action==="widget_result"){if(!o.step)return{text:JSON.stringify({status:"error",error:'Missing "step" for widget_result action'}),data:{status:"error"}};let s={...u,...o.widgetResult??{}},n=i.get(o.step);if(!n)return{text:JSON.stringify({status:"error",error:`No edge from step "${o.step}"`}),data:{status:"error"}};let d=await y(n,s);return k(d,s,r,i,t.id,p)}return{text:JSON.stringify({status:"error",error:`Unknown action: "${o.action}"`}),data:{status:"error"}}}return{id:t.id,title:t.title,description:T,async register(o){o.registerTool(t.id,{title:t.title,description:T,inputSchema:z,annotations:t.annotations},(async(p,u)=>{let n=u._meta??{},d=await a(p,n);return{content:[{type:"text",text:d.text}],structuredContent:d.data,_meta:{...d.widgetMeta??{},...n}}}))}}}var x=class{nodes=new Map;edges=new Map;config;constructor(t){this.config=t}addNode(t,r,i){if(t===w||t===S)throw new Error(`"${t}" is a reserved name and cannot be used as a node name`);if(this.nodes.has(t))throw new Error(`Node "${t}" already exists`);let l;if(typeof r=="function")l=r;else{if(!i)throw new Error(`addNode("${t}", config, handler) requires a handler as the third argument`);l=i}return this.nodes.set(t,l),this}addEdge(t,r){if(this.edges.has(t))throw new Error(`Node "${t}" already has an outgoing edge. Use addConditionalEdge for branching.`);return this.edges.set(t,{type:"direct",to:r}),this}addConditionalEdge(t,r){if(this.edges.has(t))throw new Error(`Node "${t}" already has an outgoing edge.`);return this.edges.set(t,{type:"conditional",condition:r}),this}compile(){return this.validate(),H({config:this.config,nodes:new Map(this.nodes),edges:new Map(this.edges)})}validate(){if(!this.edges.has(w))throw new Error('Flow must have an entry point. Add an edge from START: .addEdge(START, "first_node")');let t=this.edges.get(w);if(t?.type==="direct"&&t.to!==S&&!this.nodes.has(t.to))throw new Error(`START edge references non-existent node: "${t.to}"`);for(let[r,i]of this.edges){if(r!==w&&!this.nodes.has(r))throw new Error(`Edge from non-existent node: "${r}"`);if(i.type==="direct"&&i.to!==S&&!this.nodes.has(i.to))throw new Error(`Edge from "${r}" references non-existent node: "${i.to}"`)}for(let[r]of this.nodes)if(!this.edges.has(r))throw new Error(`Node "${r}" has no outgoing edge. Add one with .addEdge("${r}", ...) or .addConditionalEdge("${r}", ...)`)}};function W(e){return new x(e)}var R="text/html+skybridge",C="text/html;profile=mcp-app",D=async(e,t)=>{let r=e.endsWith("/")?e.slice(0,-1):e;return await(await fetch(`${r}${t}`)).text()};function $(e){return{"openai/widgetDescription":e.description,"openai/widgetPrefersBorder":e.prefersBorder,"openai/widgetDomain":e.widgetDomain,...e.widgetCSP&&{"openai/widgetCSP":e.widgetCSP}}}function q(e){let t=e.widgetCSP?{connectDomains:e.widgetCSP.connect_domains,resourceDomains:e.widgetCSP.resource_domains,frameDomains:e.widgetCSP.frame_domains,redirectDomains:e.widgetCSP.redirect_domains}:void 0;return{ui:{...t&&{csp:t},...e.prefersBorder!==void 0&&{prefersBorder:e.prefersBorder}}}}function _(e){return{"openai/outputTemplate":e.openaiTemplateUri,"openai/toolInvocation/invoking":e.invoking,"openai/toolInvocation/invoked":e.invoked,"openai/widgetAccessible":!0,"openai/resultCanProduceWidget":!0,ui:{resourceUri:e.mcpTemplateUri,...e.autoHeight&&{autoHeight:!0}}}}function U(e){let{id:t,title:r,description:i,baseUrl:l,htmlPath:T,widgetDomain:a,prefersBorder:o=!0,autoHeight:p,widgetCSP:u}=e,s=`ui://widgets/apps-sdk/${t}.html`,n=`ui://widgets/ext-apps/${t}.html`,d=null,g=()=>(d||(d=D(l,T)),d),c=i;async function J(M){let v=await g();M.registerResource(`${t}-openai-widget`,s,{title:r,description:c,mimeType:R,_meta:{"openai/widgetDescription":c,"openai/widgetPrefersBorder":o}},async N=>({contents:[{uri:N.href,mimeType:R,text:v,_meta:$({description:c,prefersBorder:o,widgetDomain:a,widgetCSP:u})}]})),M.registerResource(`${t}-mcp-widget`,n,{title:r,description:c,mimeType:C,_meta:{ui:{prefersBorder:o}}},async N=>({contents:[{uri:N.href,mimeType:C,text:v,_meta:q({description:c,prefersBorder:o,widgetCSP:u})}]}))}return{id:t,title:r,description:i,openaiUri:s,mcpUri:n,autoHeight:p,register:J}}function G(e,t){let{resource:r,description:i,inputSchema:l,annotations:T}=e,a=e.id??r?.id,o=e.title??r?.title;if(!a)throw new Error("createTool: `id` is required when no resource is provided");if(!o)throw new Error("createTool: `title` is required when no resource is provided");let p=r?_({openaiTemplateUri:r.openaiUri,mcpTemplateUri:r.mcpUri,invoking:e.invoking??"Loading...",invoked:e.invoked??"Loaded",autoHeight:r.autoHeight}):void 0;return{id:a,title:o,description:i,async register(u){u.registerTool(a,{title:o,description:i,inputSchema:l,annotations:T,...p&&{_meta:p}},(async(s,n)=>{let g=n._meta??{},c=await t(s,{extra:{_meta:g}});return r&&c.data?{content:[{type:"text",text:c.text}],structuredContent:c.data,_meta:{...p,...g}}:{content:[{type:"text",text:c.text}]}}))}}}async function L(e,t){await Promise.all(t.map(r=>r.register(e)))}export{S as END,w as START,x as StateGraph,W as createFlow,U as createResource,G as createTool,E as detectPlatform,F as interrupt,j as isMCPApps,B as isOpenAI,L as registerTools,O as showWidget};
4
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/mcp/react/widgets/platform.ts","../../src/mcp/server/flows/@types.ts","../../src/mcp/server/flows/compile.ts","../../src/mcp/server/flows/state-graph.ts","../../src/mcp/server/flows/create-flow.ts","../../src/mcp/server/widgets/create-widget.ts"],"sourcesContent":["/**\n * Widget platform types\n */\nexport type WidgetPlatform = \"openai\" | \"mcp-apps\";\n\n/**\n * Detects which platform the widget is running on.\n *\n * OpenAI injects a global `window.openai` object.\n * MCP Apps runs in a sandboxed iframe and uses postMessage.\n */\nexport function detectPlatform(): WidgetPlatform {\n\tif (typeof window !== \"undefined\" && \"openai\" in window) {\n\t\treturn \"openai\";\n\t}\n\treturn \"mcp-apps\";\n}\n\n/**\n * Check if running on OpenAI platform\n */\nexport function isOpenAI(): boolean {\n\treturn detectPlatform() === \"openai\";\n}\n\n/**\n * Check if running on MCP Apps platform\n */\nexport function isMCPApps(): boolean {\n\treturn detectPlatform() === \"mcp-apps\";\n}\n","// ============================================================================\n// Sentinel constants\n// ============================================================================\n\nexport const START = \"__start__\" as const;\nexport const END = \"__end__\" as const;\n\n// ============================================================================\n// Signal types — returned by node handlers to control flow behavior\n// ============================================================================\n\nconst INTERRUPT = Symbol.for(\"waniwani.flow.interrupt\");\nconst WIDGET = Symbol.for(\"waniwani.flow.widget\");\n\nexport type InterruptSignal = {\n\treadonly __type: typeof INTERRUPT;\n\t/** Question to ask the user */\n\tquestion: string;\n\t/** State key where the answer will be stored */\n\tfield: string;\n\t/** Optional suggestions to present as options */\n\tsuggestions?: string[];\n\t/** Hidden context/instructions for the assistant (not shown to user directly) */\n\tcontext?: string;\n};\n\nexport type WidgetSignal = {\n\treadonly __type: typeof WIDGET;\n\t/** ID of a registered widget to display */\n\twidgetId: string;\n\t/** Data to pass to the widget as structuredContent */\n\tdata: Record<string, unknown>;\n\t/** Description of what the widget does (for the AI's context) */\n\tdescription?: string;\n};\n\n/**\n * Create an interrupt signal — pauses the flow and asks the user a text question.\n */\nexport function interrupt(config: {\n\tquestion: string;\n\tfield: string;\n\tsuggestions?: string[];\n\tcontext?: string;\n}): InterruptSignal {\n\treturn { __type: INTERRUPT, ...config };\n}\n\n/**\n * Create a widget signal — pauses the flow and renders a widget UI.\n */\nexport function showWidget(config: {\n\twidgetId: string;\n\tdata: Record<string, unknown>;\n\tdescription?: string;\n}): WidgetSignal {\n\treturn { __type: WIDGET, ...config };\n}\n\nexport function isInterrupt(value: unknown): value is InterruptSignal {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"__type\" in value &&\n\t\t(value as InterruptSignal).__type === INTERRUPT\n\t);\n}\n\nexport function isWidget(value: unknown): value is WidgetSignal {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"__type\" in value &&\n\t\t(value as WidgetSignal).__type === WIDGET\n\t);\n}\n\n// ============================================================================\n// Node & edge definitions\n// ============================================================================\n\nexport type MaybePromise<T> = T | Promise<T>;\n\n/**\n * Node handler — a single function type for all node kinds.\n * The return value determines behavior:\n * - `Partial<TState>` → action node (state merged, auto-advance)\n * - `InterruptSignal` → interrupt (pause, ask user)\n * - `WidgetSignal` → widget step (pause, show widget)\n */\nexport type NodeHandler<TState> = (\n\tstate: Partial<TState>,\n\tmeta?: Record<string, unknown>,\n) => MaybePromise<Partial<TState> | InterruptSignal | WidgetSignal>;\n\n/**\n * Condition function for conditional edges.\n * Receives current state, returns the name of the next node.\n */\nexport type ConditionFn<TState> = (\n\tstate: Partial<TState>,\n) => string | Promise<string>;\n\nexport type Edge<TState> =\n\t| { type: \"direct\"; to: string }\n\t| { type: \"conditional\"; condition: ConditionFn<TState> };\n\n// ============================================================================\n// Flow config & compiled output\n// ============================================================================\n\nexport type FlowConfig = {\n\t/** Unique identifier for the flow (becomes the MCP tool name) */\n\tid: string;\n\t/** Display title */\n\ttitle: string;\n\t/** Description for the AI (explains when to use this flow) */\n\tdescription: string;\n\t/** Optional tool annotations */\n\tannotations?: {\n\t\treadOnlyHint?: boolean;\n\t\tidempotentHint?: boolean;\n\t\topenWorldHint?: boolean;\n\t\tdestructiveHint?: boolean;\n\t};\n};\n\nexport type CompileOptions = {\n\t/** Map of widget IDs to their RegisteredWidget, for resolving widget resource URIs */\n\twidgetRefs?: Record<string, { id: string }>;\n};\n\n// Re-export McpServer type from the widget types\nexport type {\n\tMcpServer,\n\tRegisteredWidget,\n} from \"../widgets/types\";\n\nimport type { McpServer } from \"../widgets/types\";\n\n/**\n * A compiled flow — compatible with RegisteredWidget for registration.\n */\nexport type RegisteredFlow = {\n\tid: string;\n\ttitle: string;\n\tdescription: string;\n\tregister: (server: McpServer) => Promise<void>;\n};\n","import type { ToolCallback } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n\tServerNotification,\n\tServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport type {\n\tEdge,\n\tFlowConfig,\n\tMcpServer,\n\tNodeHandler,\n\tRegisteredFlow,\n} from \"./@types\";\nimport { END, isInterrupt, isWidget, START } from \"./@types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface CompileInput<TState extends Record<string, unknown>> {\n\tconfig: FlowConfig;\n\tnodes: Map<string, NodeHandler<TState>>;\n\tedges: Map<string, Edge<TState>>;\n\twidgetRefs?: Record<string, { id: string }>;\n}\n\ntype FlowToolInput = {\n\taction: \"start\" | \"continue\" | \"widget_result\";\n\tstep?: string;\n\tstate?: Record<string, unknown>;\n\tanswer?: string;\n\twidgetResult?: Record<string, unknown>;\n};\n\ntype ExecutionResult = {\n\ttext: string;\n\tdata: Record<string, unknown>;\n\twidgetMeta?: Record<string, unknown>;\n};\n\n// ============================================================================\n// Flow protocol — embedded in tool description\n// ============================================================================\n\nfunction buildFlowProtocol(_config: FlowConfig): string {\n\treturn [\n\t\t\"\",\n\t\t\"## FLOW EXECUTION PROTOCOL\",\n\t\t\"\",\n\t\t\"This tool implements a multi-step conversational flow. Follow this protocol exactly:\",\n\t\t\"\",\n\t\t'1. Call with `action: \"start\"` to begin.',\n\t\t\"2. The response JSON `status` field tells you what to do next:\",\n\t\t' - `\"interrupt\"`: Ask the user the `question`. If a `context` field is present,',\n\t\t\" use it as hidden instructions to enrich your response (do NOT show it verbatim).\",\n\t\t\" Then call again with:\",\n\t\t' `action: \"continue\"`, `step` = the returned `step`, `state` = the returned `state`,',\n\t\t\" `answer` = the user's answer.\",\n\t\t' - `\"widget\"`: A widget UI is being shown. Do NOT call this tool again — the widget handles the callback.',\n\t\t' - `\"complete\"`: The flow is done. Present the result to the user.',\n\t\t' - `\"error\"`: Something went wrong. Show the `error` message.',\n\t\t\"\",\n\t\t\"3. ALWAYS pass back the `state` object exactly as received.\",\n\t\t\"4. Do NOT skip steps or invent state values.\",\n\t].join(\"\\n\");\n}\n\n// ============================================================================\n// Edge resolution\n// ============================================================================\n\nasync function resolveNextNode<TState extends Record<string, unknown>>(\n\tedge: Edge<TState>,\n\tstate: Partial<TState>,\n): Promise<string> {\n\tif (edge.type === \"direct\") return edge.to;\n\treturn edge.condition(state);\n}\n\n// ============================================================================\n// Widget metadata\n// ============================================================================\n\nfunction buildWidgetMeta(widgetId: string): Record<string, unknown> {\n\tconst openaiTemplateUri = `ui://widgets/apps-sdk/${widgetId}.html`;\n\tconst mcpTemplateUri = `ui://widgets/ext-apps/${widgetId}.html`;\n\n\treturn {\n\t\t\"openai/outputTemplate\": openaiTemplateUri,\n\t\t\"openai/widgetAccessible\": true,\n\t\t\"openai/resultCanProduceWidget\": true,\n\t\tui: {\n\t\t\tresourceUri: mcpTemplateUri,\n\t\t},\n\t};\n}\n\n// ============================================================================\n// Execution engine\n// ============================================================================\n\nasync function executeFrom<TState extends Record<string, unknown>>(\n\tstartNodeName: string,\n\tinitialState: TState,\n\tnodes: Map<string, NodeHandler<TState>>,\n\tedges: Map<string, Edge<TState>>,\n\tflowId: string,\n\tmeta?: Record<string, unknown>,\n): Promise<ExecutionResult> {\n\tlet currentNode = startNodeName;\n\tlet state = { ...initialState };\n\n\t// Safety limit to prevent infinite loops\n\tconst MAX_ITERATIONS = 50;\n\tlet iterations = 0;\n\n\twhile (iterations++ < MAX_ITERATIONS) {\n\t\t// Reached END\n\t\tif (currentNode === END) {\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tstate,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"complete\", state },\n\t\t\t};\n\t\t}\n\n\t\tconst handler = nodes.get(currentNode);\n\t\tif (!handler) {\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\terror: `Unknown node: \"${currentNode}\"`,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"error\" },\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await handler(state, meta);\n\n\t\t\t// Interrupt signal — pause and ask the user\n\t\t\tif (isInterrupt(result)) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"interrupt\",\n\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\tquestion: result.question,\n\t\t\t\t\t\tfield: result.field,\n\t\t\t\t\t\tsuggestions: result.suggestions,\n\t\t\t\t\t\t...(result.context ? { context: result.context } : {}),\n\t\t\t\t\t\tstate,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"interrupt\", step: currentNode, state },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Widget signal — pause and show widget\n\t\t\tif (isWidget(result)) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"widget\",\n\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\twidgetId: result.widgetId,\n\t\t\t\t\t\tdescription: result.description,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: {\n\t\t\t\t\t\t...result.data,\n\t\t\t\t\t\t__flow: {\n\t\t\t\t\t\t\tflowId,\n\t\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\twidgetMeta: buildWidgetMeta(result.widgetId),\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Action node — merge state and auto-advance\n\t\t\tstate = { ...state, ...result } as TState;\n\n\t\t\tconst edge = edges.get(currentNode);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No outgoing edge from node \"${currentNode}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tcurrentNode = await resolveNextNode(edge, state);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\tstep: currentNode,\n\t\t\t\t\terror: message,\n\t\t\t\t\tstate,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"error\", error: message },\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\ttext: JSON.stringify({\n\t\t\tstatus: \"error\",\n\t\t\terror: \"Flow exceeded maximum iterations (possible infinite loop)\",\n\t\t}),\n\t\tdata: { status: \"error\" },\n\t};\n}\n\n// ============================================================================\n// Compile\n// ============================================================================\n\nconst inputSchema = {\n\taction: z\n\t\t.enum([\"start\", \"continue\", \"widget_result\"])\n\t\t.describe(\n\t\t\t'\"start\" to begin the flow, \"continue\" after the user answers a question, \"widget_result\" when a widget returns data',\n\t\t),\n\tstep: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\"Current step name (from the previous response)\"),\n\tstate: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\"Flow state — pass back exactly as received\"),\n\tanswer: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\"The user's answer (for interrupt steps)\"),\n\twidgetResult: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\"Data returned by a widget callback\"),\n};\n\nexport function compileFlow<TState extends Record<string, unknown>>(\n\tinput: CompileInput<TState>,\n): RegisteredFlow {\n\tconst { config, nodes, edges } = input;\n\tconst protocol = buildFlowProtocol(config);\n\tconst fullDescription = `${config.description}\\n${protocol}`;\n\n\tasync function handleToolCall(\n\t\targs: FlowToolInput,\n\t\tmeta?: Record<string, unknown>,\n\t): Promise<ExecutionResult> {\n\t\tconst state = (args.state ?? {}) as TState;\n\n\t\tif (args.action === \"start\") {\n\t\t\tconst startEdge = edges.get(START);\n\t\t\tif (!startEdge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({ status: \"error\", error: \"No start edge\" }),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst firstNode = await resolveNextNode(startEdge, state);\n\t\t\treturn executeFrom(firstNode, state, nodes, edges, config.id, meta);\n\t\t}\n\n\t\tif (args.action === \"continue\") {\n\t\t\tif (!args.step) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: 'Missing \"step\" for continue action',\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Apply user's answer to state using the field from the interrupt\n\t\t\tlet updatedState = { ...state };\n\t\t\tif (args.answer) {\n\t\t\t\tconst handler = nodes.get(args.step);\n\t\t\t\tif (handler) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await handler(updatedState, meta);\n\t\t\t\t\t\tif (isInterrupt(result) && result.field) {\n\t\t\t\t\t\t\tupdatedState = {\n\t\t\t\t\t\t\t\t...updatedState,\n\t\t\t\t\t\t\t\t[result.field]: args.answer,\n\t\t\t\t\t\t\t} as TState;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// If re-running the handler fails, still proceed with the answer\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Advance to next node\n\t\t\tconst edge = edges.get(args.step);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No edge from step \"${args.step}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst nextNode = await resolveNextNode(edge, updatedState);\n\t\t\treturn executeFrom(nextNode, updatedState, nodes, edges, config.id, meta);\n\t\t}\n\n\t\tif (args.action === \"widget_result\") {\n\t\t\tif (!args.step) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: 'Missing \"step\" for widget_result action',\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Merge widget result into state\n\t\t\tconst updatedState = {\n\t\t\t\t...state,\n\t\t\t\t...(args.widgetResult ?? {}),\n\t\t\t} as TState;\n\n\t\t\t// Advance to next node\n\t\t\tconst edge = edges.get(args.step);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No edge from step \"${args.step}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst nextNode = await resolveNextNode(edge, updatedState);\n\t\t\treturn executeFrom(nextNode, updatedState, nodes, edges, config.id, meta);\n\t\t}\n\n\t\treturn {\n\t\t\ttext: JSON.stringify({\n\t\t\t\tstatus: \"error\",\n\t\t\t\terror: `Unknown action: \"${args.action}\"`,\n\t\t\t}),\n\t\t\tdata: { status: \"error\" },\n\t\t};\n\t}\n\n\treturn {\n\t\tid: config.id,\n\t\ttitle: config.title,\n\t\tdescription: fullDescription,\n\n\t\tasync register(server: McpServer): Promise<void> {\n\t\t\tserver.registerTool(\n\t\t\t\tconfig.id,\n\t\t\t\t{\n\t\t\t\t\ttitle: config.title,\n\t\t\t\t\tdescription: fullDescription,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tannotations: config.annotations,\n\t\t\t\t},\n\t\t\t\t(async (args: FlowToolInput, extra: unknown) => {\n\t\t\t\t\tconst requestExtra = extra as RequestHandlerExtra<\n\t\t\t\t\t\tServerRequest,\n\t\t\t\t\t\tServerNotification\n\t\t\t\t\t>;\n\t\t\t\t\tconst _meta: Record<string, unknown> = requestExtra._meta ?? {};\n\n\t\t\t\t\tconst result = await handleToolCall(args, _meta);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t...(result.widgetMeta ?? {}),\n\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}) as unknown as ToolCallback<typeof inputSchema>,\n\t\t\t);\n\t\t},\n\t};\n}\n","import type {\n\tCompileOptions,\n\tConditionFn,\n\tEdge,\n\tFlowConfig,\n\tNodeHandler,\n\tRegisteredFlow,\n} from \"./@types\";\nimport { END, START } from \"./@types\";\nimport { compileFlow } from \"./compile\";\n\n/**\n * A LangGraph-inspired state graph builder for MCP tools.\n *\n * @example\n * ```ts\n * const flow = new StateGraph<MyState>({\n * id: \"onboarding\",\n * title: \"User Onboarding\",\n * description: \"Guides users through onboarding\",\n * })\n * .addNode(\"ask_name\", () => interrupt({ question: \"What's your name?\", field: \"name\" }))\n * .addNode(\"greet\", (state) => ({ greeting: `Hello ${state.name}!` }))\n * .addEdge(START, \"ask_name\")\n * .addEdge(\"ask_name\", \"greet\")\n * .addEdge(\"greet\", END)\n * .compile();\n * ```\n */\nexport class StateGraph<TState extends Record<string, unknown>> {\n\tprivate nodes = new Map<string, NodeHandler<TState>>();\n\tprivate edges = new Map<string, Edge<TState>>();\n\tprivate config: FlowConfig;\n\n\tconstructor(config: FlowConfig) {\n\t\tthis.config = config;\n\t}\n\n\t/**\n\t * Add a node to the graph.\n\t *\n\t * The handler's return value determines the node type:\n\t * - Returns `Partial<TState>` → **action node**: state is merged, auto-advances to next node\n\t * - Returns `interrupt(...)` → **interrupt node**: pauses flow, asks user a question\n\t * - Returns `showWidget(...)` → **widget node**: pauses flow, renders a widget UI\n\t */\n\taddNode(name: string, handler: NodeHandler<TState>): this {\n\t\tif (name === START || name === END) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${name}\" is a reserved name and cannot be used as a node name`,\n\t\t\t);\n\t\t}\n\t\tif (this.nodes.has(name)) {\n\t\t\tthrow new Error(`Node \"${name}\" already exists`);\n\t\t}\n\t\tthis.nodes.set(name, handler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a direct edge between two nodes.\n\t *\n\t * Use `START` as `from` to set the entry point.\n\t * Use `END` as `to` to mark a terminal node.\n\t */\n\taddEdge(from: string, to: string): this {\n\t\tif (this.edges.has(from)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node \"${from}\" already has an outgoing edge. Use addConditionalEdge for branching.`,\n\t\t\t);\n\t\t}\n\t\tthis.edges.set(from, { type: \"direct\", to });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a conditional edge from a node.\n\t *\n\t * The condition function receives current state and returns the name of the next node.\n\t */\n\taddConditionalEdge(from: string, condition: ConditionFn<TState>): this {\n\t\tif (this.edges.has(from)) {\n\t\t\tthrow new Error(`Node \"${from}\" already has an outgoing edge.`);\n\t\t}\n\t\tthis.edges.set(from, { type: \"conditional\", condition });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Compile the graph into a RegisteredFlow that can be registered on an McpServer.\n\t *\n\t * Validates the graph structure and returns a registration-compatible object.\n\t */\n\tcompile(options?: CompileOptions): RegisteredFlow {\n\t\tthis.validate();\n\n\t\treturn compileFlow<TState>({\n\t\t\tconfig: this.config,\n\t\t\tnodes: new Map(this.nodes),\n\t\t\tedges: new Map(this.edges),\n\t\t\twidgetRefs: options?.widgetRefs,\n\t\t});\n\t}\n\n\tprivate validate(): void {\n\t\t// Must have a START edge\n\t\tif (!this.edges.has(START)) {\n\t\t\tthrow new Error(\n\t\t\t\t'Flow must have an entry point. Add an edge from START: .addEdge(START, \"first_node\")',\n\t\t\t);\n\t\t}\n\n\t\t// START edge target must exist\n\t\tconst startEdge = this.edges.get(START);\n\t\tif (\n\t\t\tstartEdge?.type === \"direct\" &&\n\t\t\tstartEdge.to !== END &&\n\t\t\t!this.nodes.has(startEdge.to)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`START edge references non-existent node: \"${startEdge.to}\"`,\n\t\t\t);\n\t\t}\n\n\t\t// All static edge targets must reference existing nodes (or END)\n\t\tfor (const [from, edge] of this.edges) {\n\t\t\tif (from !== START && !this.nodes.has(from)) {\n\t\t\t\tthrow new Error(`Edge from non-existent node: \"${from}\"`);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tedge.type === \"direct\" &&\n\t\t\t\tedge.to !== END &&\n\t\t\t\t!this.nodes.has(edge.to)\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Edge from \"${from}\" references non-existent node: \"${edge.to}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Every node must have an outgoing edge\n\t\tfor (const [name] of this.nodes) {\n\t\t\tif (!this.edges.has(name)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Node \"${name}\" has no outgoing edge. Add one with .addEdge(\"${name}\", ...) or .addConditionalEdge(\"${name}\", ...)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { FlowConfig } from \"./@types\";\nimport { StateGraph } from \"./state-graph\";\n\n/**\n * Create a new flow graph — convenience factory for `new StateGraph()`.\n *\n * @example\n * ```ts\n * import { createFlow, interrupt, START, END } from \"@waniwani/sdk/mcp\";\n *\n * type MyState = { name: string; email: string };\n *\n * const flow = createFlow<MyState>({\n * id: \"onboarding\",\n * title: \"User Onboarding\",\n * description: \"Guides users through onboarding. Use when a user wants to get started.\",\n * })\n * .addNode(\"ask_name\", () => interrupt({ question: \"What's your name?\", field: \"name\" }))\n * .addNode(\"ask_email\", () => interrupt({ question: \"What's your email?\", field: \"email\" }))\n * .addEdge(START, \"ask_name\")\n * .addEdge(\"ask_name\", \"ask_email\")\n * .addEdge(\"ask_email\", END)\n * .compile();\n * ```\n */\nexport function createFlow<TState extends Record<string, unknown>>(\n\tconfig: FlowConfig,\n): StateGraph<TState> {\n\treturn new StateGraph<TState>(config);\n}\n","import type { ToolCallback } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { ShapeOutput } from \"@modelcontextprotocol/sdk/server/zod-compat.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n\tServerNotification,\n\tServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { z } from \"zod\";\nimport type {\n\tMcpServer,\n\tRegisteredWidget,\n\tWidgetConfig,\n\tWidgetHandler,\n} from \"./types\";\n\n/**\n * MIME types for widget resources.\n * OpenAI Apps SDK uses \"text/html+skybridge\"\n * MCP Apps uses \"text/html;profile=mcp-app\"\n */\nconst MIME_TYPE_OPENAI = \"text/html+skybridge\";\nconst MIME_TYPE_MCP = \"text/html;profile=mcp-app\";\n\ninterface WidgetCSP {\n\tconnect_domains?: string[];\n\tresource_domains?: string[];\n\tframe_domains?: string[];\n\tredirect_domains?: string[];\n}\n\ninterface OpenAIResourceMeta {\n\t[key: string]: unknown;\n\t\"openai/widgetDescription\"?: string;\n\t\"openai/widgetPrefersBorder\"?: boolean;\n\t\"openai/widgetDomain\"?: string;\n\t\"openai/widgetCSP\"?: WidgetCSP;\n}\n\ninterface McpAppsResourceMeta {\n\t[key: string]: unknown;\n\tui?: {\n\t\tcsp?: {\n\t\t\tconnectDomains?: string[];\n\t\t\tresourceDomains?: string[];\n\t\t\tframeDomains?: string[];\n\t\t\tredirectDomains?: string[];\n\t\t};\n\t\tdomain?: string;\n\t\tprefersBorder?: boolean;\n\t};\n}\n\nconst fetchHtml = async (baseUrl: string, path: string): Promise<string> => {\n\tconst normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\tconst result = await fetch(`${normalizedBase}${path}`);\n\treturn await result.text();\n};\n\n/**\n * Build OpenAI-specific resource metadata\n */\nfunction buildOpenAIResourceMeta(config: {\n\tdescription?: string;\n\tprefersBorder?: boolean;\n\twidgetDomain: string;\n\twidgetCSP?: WidgetCSP;\n}): OpenAIResourceMeta {\n\treturn {\n\t\t\"openai/widgetDescription\": config.description,\n\t\t\"openai/widgetPrefersBorder\": config.prefersBorder,\n\t\t\"openai/widgetDomain\": config.widgetDomain,\n\t\t...(config.widgetCSP && { \"openai/widgetCSP\": config.widgetCSP }),\n\t};\n}\n\n/**\n * Build MCP Apps-specific resource metadata\n * Note: MCP Apps (Claude) doesn't use the domain field in the same way as OpenAI.\n * Claude computes it dynamically at request time in the format: {hash}.claudemcpcontent.com\n */\nfunction buildMcpAppsResourceMeta(config: {\n\tdescription?: string;\n\tprefersBorder?: boolean;\n\twidgetCSP?: WidgetCSP;\n}): McpAppsResourceMeta {\n\tconst csp = config.widgetCSP\n\t\t? {\n\t\t\t\tconnectDomains: config.widgetCSP.connect_domains,\n\t\t\t\tresourceDomains: config.widgetCSP.resource_domains,\n\t\t\t\tframeDomains: config.widgetCSP.frame_domains,\n\t\t\t\tredirectDomains: config.widgetCSP.redirect_domains,\n\t\t\t}\n\t\t: undefined;\n\n\treturn {\n\t\tui: {\n\t\t\t...(csp && { csp }),\n\t\t\t...(config.prefersBorder !== undefined && {\n\t\t\t\tprefersBorder: config.prefersBorder,\n\t\t\t}),\n\t\t},\n\t};\n}\n\n/**\n * Build tool metadata that references both OpenAI and MCP widget URIs\n */\nfunction buildToolMeta(config: {\n\topenaiTemplateUri: string;\n\tmcpTemplateUri: string;\n\tinvoking: string;\n\tinvoked: string;\n\tautoHeight?: boolean;\n}) {\n\treturn {\n\t\t// OpenAI metadata\n\t\t\"openai/outputTemplate\": config.openaiTemplateUri,\n\t\t\"openai/toolInvocation/invoking\": config.invoking,\n\t\t\"openai/toolInvocation/invoked\": config.invoked,\n\t\t\"openai/widgetAccessible\": true,\n\t\t\"openai/resultCanProduceWidget\": true,\n\t\t// MCP Apps metadata\n\t\tui: {\n\t\t\tresourceUri: config.mcpTemplateUri,\n\t\t\t...(config.autoHeight && { autoHeight: true }),\n\t\t},\n\t} as const;\n}\n\n/**\n * Creates a widget with minimal boilerplate.\n *\n * @example\n * ```ts\n * const weatherWidget = createWidget({\n * id: \"show_weather\",\n * title: \"Show Weather\",\n * description: \"Displays weather information for a city\",\n * baseUrl: \"https://my-app.com\",\n * htmlPath: \"/weather\",\n * inputSchema: {\n * city: z.string().describe(\"The city name\"),\n * },\n * }, async ({ city }) => ({\n * text: `Weather for ${city}`,\n * data: { city, temperature: 72 },\n * }));\n * ```\n */\nexport function createWidget<TInput extends z.ZodRawShape>(\n\tconfig: WidgetConfig<TInput> & { widgetDomain: string },\n\thandler: WidgetHandler<TInput>,\n): RegisteredWidget {\n\tconst {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\t\twidgetDescription,\n\t\tbaseUrl,\n\t\thtmlPath,\n\t\tinputSchema,\n\t\tinvoking = \"Loading...\",\n\t\tinvoked = \"Loaded\",\n\t\twidgetDomain,\n\t\tprefersBorder = true,\n\t\tautoHeight,\n\t\twidgetCSP,\n\t\tannotations,\n\t} = config;\n\n\t// Use widgetDescription for UI metadata, fall back to description\n\tconst uiDescription = widgetDescription ?? description;\n\n\t// Create URIs for both platforms\n\tconst openaiTemplateUri = `ui://widgets/apps-sdk/${id}.html`;\n\tconst mcpTemplateUri = `ui://widgets/ext-apps/${id}.html`;\n\n\treturn {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\n\t\tasync register(server: McpServer): Promise<void> {\n\t\t\tconst html = await fetchHtml(baseUrl, htmlPath);\n\n\t\t\t// Build tool metadata that references both widget URIs\n\t\t\tconst toolMeta = buildToolMeta({\n\t\t\t\topenaiTemplateUri,\n\t\t\t\tmcpTemplateUri,\n\t\t\t\tinvoking,\n\t\t\t\tinvoked,\n\t\t\t\tautoHeight,\n\t\t\t});\n\n\t\t\t// Register OpenAI Apps SDK resource\n\t\t\tserver.registerResource(\n\t\t\t\t`${id}-openai-widget`,\n\t\t\t\topenaiTemplateUri,\n\t\t\t\t{\n\t\t\t\t\ttitle,\n\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\tmimeType: MIME_TYPE_OPENAI,\n\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\"openai/widgetDescription\": uiDescription,\n\t\t\t\t\t\t\"openai/widgetPrefersBorder\": prefersBorder,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (uri) => ({\n\t\t\t\t\tcontents: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\t\tmimeType: MIME_TYPE_OPENAI,\n\t\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\t\t_meta: buildOpenAIResourceMeta({\n\t\t\t\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t\t\t\twidgetDomain,\n\t\t\t\t\t\t\t\twidgetCSP,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\t// Register MCP Apps resource\n\t\t\tserver.registerResource(\n\t\t\t\t`${id}-mcp-widget`,\n\t\t\t\tmcpTemplateUri,\n\t\t\t\t{\n\t\t\t\t\ttitle,\n\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\tmimeType: MIME_TYPE_MCP,\n\t\t\t\t\t_meta: {\n\t\t\t\t\t\tui: {\n\t\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (uri) => ({\n\t\t\t\t\tcontents: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\t\tmimeType: MIME_TYPE_MCP,\n\t\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\t\t_meta: buildMcpAppsResourceMeta({\n\t\t\t\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t\t\t\twidgetCSP,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\t// Register the tool\n\t\t\tserver.registerTool(\n\t\t\t\tid,\n\t\t\t\t{\n\t\t\t\t\ttitle,\n\t\t\t\t\tdescription,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tannotations,\n\t\t\t\t\t_meta: toolMeta,\n\t\t\t\t},\n\t\t\t\t(async (args: ShapeOutput<TInput>, extra: unknown) => {\n\t\t\t\t\tconst requestExtra = extra as RequestHandlerExtra<\n\t\t\t\t\t\tServerRequest,\n\t\t\t\t\t\tServerNotification\n\t\t\t\t\t>;\n\t\t\t\t\tconst _meta: Record<string, unknown> = requestExtra._meta ?? {};\n\n\t\t\t\t\tconst result = await handler(args, { extra: { _meta } });\n\n\t\t\t\t\t/**\n\t\t\t\t\t * This is a workaround to type the tool callback correctly.\n\t\t\t\t\t *\n\t\t\t\t\t * The types are correct but TS is not able to infer the type correctly.\n\t\t\t\t\t */\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\", text: result.text }],\n\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t...toolMeta,\n\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}) as unknown as ToolCallback<TInput>,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Registers multiple widgets on the server\n */\nexport async function registerWidgets(\n\tserver: McpServer,\n\twidgets: RegisteredWidget[],\n): Promise<void> {\n\tawait Promise.all(widgets.map((w) => w.register(server)));\n}\n"],"mappings":"AAWO,SAASA,GAAiC,CAChD,OAAI,OAAO,OAAW,KAAe,WAAY,OACzC,SAED,UACR,CAKO,SAASC,GAAoB,CACnC,OAAOD,EAAe,IAAM,QAC7B,CAKO,SAASE,GAAqB,CACpC,OAAOF,EAAe,IAAM,UAC7B,CC1BO,IAAMG,EAAQ,YACRC,EAAM,UAMbC,EAAY,OAAO,IAAI,yBAAyB,EAChDC,EAAS,OAAO,IAAI,sBAAsB,EA2BzC,SAASC,EAAUC,EAKN,CACnB,MAAO,CAAE,OAAQH,EAAW,GAAGG,CAAO,CACvC,CAKO,SAASC,EAAWD,EAIV,CAChB,MAAO,CAAE,OAAQF,EAAQ,GAAGE,CAAO,CACpC,CAEO,SAASE,EAAYC,EAA0C,CACrE,OACC,OAAOA,GAAU,UACjBA,IAAU,MACV,WAAYA,GACXA,EAA0B,SAAWN,CAExC,CAEO,SAASO,EAASD,EAAuC,CAC/D,OACC,OAAOA,GAAU,UACjBA,IAAU,MACV,WAAYA,GACXA,EAAuB,SAAWL,CAErC,CCrEA,OAAS,KAAAO,MAAS,MAuClB,SAASC,EAAkBC,EAA6B,CACvD,MAAO,CACN,GACA,6BACA,GACA,uFACA,GACA,2CACA,iEACA,oFACA,wFACA,6BACA,2FACA,qCACA,mHACA,uEACA,kEACA,GACA,8DACA,8CACD,EAAE,KAAK;AAAA,CAAI,CACZ,CAMA,eAAeC,EACdC,EACAC,EACkB,CAClB,OAAID,EAAK,OAAS,SAAiBA,EAAK,GACjCA,EAAK,UAAUC,CAAK,CAC5B,CAMA,SAASC,EAAgBC,EAA2C,CACnE,IAAMC,EAAoB,yBAAyBD,CAAQ,QACrDE,EAAiB,yBAAyBF,CAAQ,QAExD,MAAO,CACN,wBAAyBC,EACzB,0BAA2B,GAC3B,gCAAiC,GACjC,GAAI,CACH,YAAaC,CACd,CACD,CACD,CAMA,eAAeC,EACdC,EACAC,EACAC,EACAC,EACAC,EACAC,EAC2B,CAC3B,IAAIC,EAAcN,EACdN,EAAQ,CAAE,GAAGO,CAAa,EAGxBM,EAAiB,GACnBC,EAAa,EAEjB,KAAOA,IAAeD,GAAgB,CAErC,GAAID,IAAgBG,EACnB,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,WACR,MAAAf,CACD,CAAC,EACD,KAAM,CAAE,OAAQ,WAAY,MAAAA,CAAM,CACnC,EAGD,IAAMgB,EAAUR,EAAM,IAAII,CAAW,EACrC,GAAI,CAACI,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,kBAAkBJ,CAAW,GACrC,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAGD,GAAI,CACH,IAAMK,EAAS,MAAMD,EAAQhB,EAAOW,CAAI,EAGxC,GAAIO,EAAYD,CAAM,EACrB,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,YACR,KAAML,EACN,SAAUK,EAAO,SACjB,MAAOA,EAAO,MACd,YAAaA,EAAO,YACpB,GAAIA,EAAO,QAAU,CAAE,QAASA,EAAO,OAAQ,EAAI,CAAC,EACpD,MAAAjB,CACD,CAAC,EACD,KAAM,CAAE,OAAQ,YAAa,KAAMY,EAAa,MAAAZ,CAAM,CACvD,EAID,GAAImB,EAASF,CAAM,EAClB,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,SACR,KAAML,EACN,SAAUK,EAAO,SACjB,YAAaA,EAAO,YACpB,MAAAjB,CACD,CAAC,EACD,KAAM,CACL,GAAGiB,EAAO,KACV,OAAQ,CACP,OAAAP,EACA,KAAME,EACN,MAAAZ,CACD,CACD,EACA,WAAYC,EAAgBgB,EAAO,QAAQ,CAC5C,EAIDjB,EAAQ,CAAE,GAAGA,EAAO,GAAGiB,CAAO,EAE9B,IAAMlB,EAAOU,EAAM,IAAIG,CAAW,EAClC,GAAI,CAACb,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,+BAA+Ba,CAAW,GAClD,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAEDA,EAAc,MAAMd,EAAgBC,EAAMC,CAAK,CAChD,OAASoB,EAAO,CACf,IAAMC,EAAUD,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,EACrE,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,KAAMR,EACN,MAAOS,EACP,MAAArB,CACD,CAAC,EACD,KAAM,CAAE,OAAQ,QAAS,MAAOqB,CAAQ,CACzC,CACD,CACD,CAEA,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,2DACR,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,CACD,CAMA,IAAMC,EAAc,CACnB,OAAQC,EACN,KAAK,CAAC,QAAS,WAAY,eAAe,CAAC,EAC3C,SACA,qHACD,EACD,KAAMA,EACJ,OAAO,EACP,SAAS,EACT,SAAS,gDAAgD,EAC3D,MAAOA,EACL,OAAOA,EAAE,OAAO,EAAGA,EAAE,QAAQ,CAAC,EAC9B,SAAS,EACT,SAAS,iDAA4C,EACvD,OAAQA,EACN,OAAO,EACP,SAAS,EACT,SAAS,yCAAyC,EACpD,aAAcA,EACZ,OAAOA,EAAE,OAAO,EAAGA,EAAE,QAAQ,CAAC,EAC9B,SAAS,EACT,SAAS,oCAAoC,CAChD,EAEO,SAASC,EACfC,EACiB,CACjB,GAAM,CAAE,OAAAC,EAAQ,MAAAlB,EAAO,MAAAC,CAAM,EAAIgB,EAC3BE,EAAW/B,EAAkB8B,CAAM,EACnCE,EAAkB,GAAGF,EAAO,WAAW;AAAA,EAAKC,CAAQ,GAE1D,eAAeE,EACdC,EACAnB,EAC2B,CAC3B,IAAMX,EAAS8B,EAAK,OAAS,CAAC,EAE9B,GAAIA,EAAK,SAAW,QAAS,CAC5B,IAAMC,EAAYtB,EAAM,IAAIuB,CAAK,EACjC,GAAI,CAACD,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CAAE,OAAQ,QAAS,MAAO,eAAgB,CAAC,EAChE,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAED,IAAME,EAAY,MAAMnC,EAAgBiC,EAAW/B,CAAK,EACxD,OAAOK,EAAY4B,EAAWjC,EAAOQ,EAAOC,EAAOiB,EAAO,GAAIf,CAAI,CACnE,CAEA,GAAImB,EAAK,SAAW,WAAY,CAC/B,GAAI,CAACA,EAAK,KACT,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,oCACR,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAID,IAAII,EAAe,CAAE,GAAGlC,CAAM,EAC9B,GAAI8B,EAAK,OAAQ,CAChB,IAAMd,EAAUR,EAAM,IAAIsB,EAAK,IAAI,EACnC,GAAId,EACH,GAAI,CACH,IAAMC,EAAS,MAAMD,EAAQkB,EAAcvB,CAAI,EAC3CO,EAAYD,CAAM,GAAKA,EAAO,QACjCiB,EAAe,CACd,GAAGA,EACH,CAACjB,EAAO,KAAK,EAAGa,EAAK,MACtB,EAEF,MAAQ,CAER,CAEF,CAGA,IAAM/B,EAAOU,EAAM,IAAIqB,EAAK,IAAI,EAChC,GAAI,CAAC/B,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,sBAAsB+B,EAAK,IAAI,GACvC,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAED,IAAMK,EAAW,MAAMrC,EAAgBC,EAAMmC,CAAY,EACzD,OAAO7B,EAAY8B,EAAUD,EAAc1B,EAAOC,EAAOiB,EAAO,GAAIf,CAAI,CACzE,CAEA,GAAImB,EAAK,SAAW,gBAAiB,CACpC,GAAI,CAACA,EAAK,KACT,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,yCACR,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAID,IAAMI,EAAe,CACpB,GAAGlC,EACH,GAAI8B,EAAK,cAAgB,CAAC,CAC3B,EAGM/B,EAAOU,EAAM,IAAIqB,EAAK,IAAI,EAChC,GAAI,CAAC/B,EACJ,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,sBAAsB+B,EAAK,IAAI,GACvC,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,EAED,IAAMK,EAAW,MAAMrC,EAAgBC,EAAMmC,CAAY,EACzD,OAAO7B,EAAY8B,EAAUD,EAAc1B,EAAOC,EAAOiB,EAAO,GAAIf,CAAI,CACzE,CAEA,MAAO,CACN,KAAM,KAAK,UAAU,CACpB,OAAQ,QACR,MAAO,oBAAoBmB,EAAK,MAAM,GACvC,CAAC,EACD,KAAM,CAAE,OAAQ,OAAQ,CACzB,CACD,CAEA,MAAO,CACN,GAAIJ,EAAO,GACX,MAAOA,EAAO,MACd,YAAaE,EAEb,MAAM,SAASQ,EAAkC,CAChDA,EAAO,aACNV,EAAO,GACP,CACC,MAAOA,EAAO,MACd,YAAaE,EACb,YAAAN,EACA,YAAaI,EAAO,WACrB,GACC,MAAOI,EAAqBO,IAAmB,CAK/C,IAAMC,EAJeD,EAI+B,OAAS,CAAC,EAExDpB,EAAS,MAAMY,EAAeC,EAAMQ,CAAK,EAE/C,MAAO,CACN,QAAS,CAAC,CAAE,KAAM,OAAiB,KAAMrB,EAAO,IAAK,CAAC,EACtD,kBAAmBA,EAAO,KAC1B,MAAO,CACN,GAAIA,EAAO,YAAc,CAAC,EAC1B,GAAGqB,CACJ,CACD,CACD,EACD,CACD,CACD,CACD,CC3WO,IAAMC,EAAN,KAAyD,CACvD,MAAQ,IAAI,IACZ,MAAQ,IAAI,IACZ,OAER,YAAYC,EAAoB,CAC/B,KAAK,OAASA,CACf,CAUA,QAAQC,EAAcC,EAAoC,CACzD,GAAID,IAASE,GAASF,IAASG,EAC9B,MAAM,IAAI,MACT,IAAIH,CAAI,wDACT,EAED,GAAI,KAAK,MAAM,IAAIA,CAAI,EACtB,MAAM,IAAI,MAAM,SAASA,CAAI,kBAAkB,EAEhD,YAAK,MAAM,IAAIA,EAAMC,CAAO,EACrB,IACR,CAQA,QAAQG,EAAcC,EAAkB,CACvC,GAAI,KAAK,MAAM,IAAID,CAAI,EACtB,MAAM,IAAI,MACT,SAASA,CAAI,uEACd,EAED,YAAK,MAAM,IAAIA,EAAM,CAAE,KAAM,SAAU,GAAAC,CAAG,CAAC,EACpC,IACR,CAOA,mBAAmBD,EAAcE,EAAsC,CACtE,GAAI,KAAK,MAAM,IAAIF,CAAI,EACtB,MAAM,IAAI,MAAM,SAASA,CAAI,iCAAiC,EAE/D,YAAK,MAAM,IAAIA,EAAM,CAAE,KAAM,cAAe,UAAAE,CAAU,CAAC,EAChD,IACR,CAOA,QAAQC,EAA0C,CACjD,YAAK,SAAS,EAEPC,EAAoB,CAC1B,OAAQ,KAAK,OACb,MAAO,IAAI,IAAI,KAAK,KAAK,EACzB,MAAO,IAAI,IAAI,KAAK,KAAK,EACzB,WAAYD,GAAS,UACtB,CAAC,CACF,CAEQ,UAAiB,CAExB,GAAI,CAAC,KAAK,MAAM,IAAIL,CAAK,EACxB,MAAM,IAAI,MACT,sFACD,EAID,IAAMO,EAAY,KAAK,MAAM,IAAIP,CAAK,EACtC,GACCO,GAAW,OAAS,UACpBA,EAAU,KAAON,GACjB,CAAC,KAAK,MAAM,IAAIM,EAAU,EAAE,EAE5B,MAAM,IAAI,MACT,6CAA6CA,EAAU,EAAE,GAC1D,EAID,OAAW,CAACL,EAAMM,CAAI,IAAK,KAAK,MAAO,CACtC,GAAIN,IAASF,GAAS,CAAC,KAAK,MAAM,IAAIE,CAAI,EACzC,MAAM,IAAI,MAAM,iCAAiCA,CAAI,GAAG,EAEzD,GACCM,EAAK,OAAS,UACdA,EAAK,KAAOP,GACZ,CAAC,KAAK,MAAM,IAAIO,EAAK,EAAE,EAEvB,MAAM,IAAI,MACT,cAAcN,CAAI,oCAAoCM,EAAK,EAAE,GAC9D,CAEF,CAGA,OAAW,CAACV,CAAI,IAAK,KAAK,MACzB,GAAI,CAAC,KAAK,MAAM,IAAIA,CAAI,EACvB,MAAM,IAAI,MACT,SAASA,CAAI,kDAAkDA,CAAI,mCAAmCA,CAAI,SAC3G,CAGH,CACD,EC5HO,SAASW,EACfC,EACqB,CACrB,OAAO,IAAIC,EAAmBD,CAAM,CACrC,CCTA,IAAME,EAAmB,sBACnBC,EAAgB,4BA+BhBC,EAAY,MAAOC,EAAiBC,IAAkC,CAC3E,IAAMC,EAAiBF,EAAQ,SAAS,GAAG,EAAIA,EAAQ,MAAM,EAAG,EAAE,EAAIA,EAEtE,OAAO,MADQ,MAAM,MAAM,GAAGE,CAAc,GAAGD,CAAI,EAAE,GACjC,KAAK,CAC1B,EAKA,SAASE,EAAwBC,EAKV,CACtB,MAAO,CACN,2BAA4BA,EAAO,YACnC,6BAA8BA,EAAO,cACrC,sBAAuBA,EAAO,aAC9B,GAAIA,EAAO,WAAa,CAAE,mBAAoBA,EAAO,SAAU,CAChE,CACD,CAOA,SAASC,EAAyBD,EAIV,CACvB,IAAME,EAAMF,EAAO,UAChB,CACA,eAAgBA,EAAO,UAAU,gBACjC,gBAAiBA,EAAO,UAAU,iBAClC,aAAcA,EAAO,UAAU,cAC/B,gBAAiBA,EAAO,UAAU,gBACnC,EACC,OAEH,MAAO,CACN,GAAI,CACH,GAAIE,GAAO,CAAE,IAAAA,CAAI,EACjB,GAAIF,EAAO,gBAAkB,QAAa,CACzC,cAAeA,EAAO,aACvB,CACD,CACD,CACD,CAKA,SAASG,EAAcH,EAMpB,CACF,MAAO,CAEN,wBAAyBA,EAAO,kBAChC,iCAAkCA,EAAO,SACzC,gCAAiCA,EAAO,QACxC,0BAA2B,GAC3B,gCAAiC,GAEjC,GAAI,CACH,YAAaA,EAAO,eACpB,GAAIA,EAAO,YAAc,CAAE,WAAY,EAAK,CAC7C,CACD,CACD,CAsBO,SAASI,GACfJ,EACAK,EACmB,CACnB,GAAM,CACL,GAAAC,EACA,MAAAC,EACA,YAAAC,EACA,kBAAAC,EACA,QAAAb,EACA,SAAAc,EACA,YAAAC,EACA,SAAAC,EAAW,aACX,QAAAC,EAAU,SACV,aAAAC,EACA,cAAAC,EAAgB,GAChB,WAAAC,EACA,UAAAC,EACA,YAAAC,CACD,EAAIlB,EAGEmB,EAAgBV,GAAqBD,EAGrCY,EAAoB,yBAAyBd,CAAE,QAC/Ce,EAAiB,yBAAyBf,CAAE,QAElD,MAAO,CACN,GAAAA,EACA,MAAAC,EACA,YAAAC,EAEA,MAAM,SAASc,EAAkC,CAChD,IAAMC,EAAO,MAAM5B,EAAUC,EAASc,CAAQ,EAGxCc,EAAWrB,EAAc,CAC9B,kBAAAiB,EACA,eAAAC,EACA,SAAAT,EACA,QAAAC,EACA,WAAAG,CACD,CAAC,EAGDM,EAAO,iBACN,GAAGhB,CAAE,iBACLc,EACA,CACC,MAAAb,EACA,YAAaY,EACb,SAAU1B,EACV,MAAO,CACN,2BAA4B0B,EAC5B,6BAA8BJ,CAC/B,CACD,EACA,MAAOU,IAAS,CACf,SAAU,CACT,CACC,IAAKA,EAAI,KACT,SAAUhC,EACV,KAAM8B,EACN,MAAOxB,EAAwB,CAC9B,YAAaoB,EACb,cAAAJ,EACA,aAAAD,EACA,UAAAG,CACD,CAAC,CACF,CACD,CACD,EACD,EAGAK,EAAO,iBACN,GAAGhB,CAAE,cACLe,EACA,CACC,MAAAd,EACA,YAAaY,EACb,SAAUzB,EACV,MAAO,CACN,GAAI,CACH,cAAAqB,CACD,CACD,CACD,EACA,MAAOU,IAAS,CACf,SAAU,CACT,CACC,IAAKA,EAAI,KACT,SAAU/B,EACV,KAAM6B,EACN,MAAOtB,EAAyB,CAC/B,YAAakB,EACb,cAAAJ,EACA,UAAAE,CACD,CAAC,CACF,CACD,CACD,EACD,EAGAK,EAAO,aACNhB,EACA,CACC,MAAAC,EACA,YAAAC,EACA,YAAAG,EACA,YAAAO,EACA,MAAOM,CACR,GACC,MAAOE,EAA2BC,IAAmB,CAKrD,IAAMC,EAJeD,EAI+B,OAAS,CAAC,EAExDE,EAAS,MAAMxB,EAAQqB,EAAM,CAAE,MAAO,CAAE,MAAAE,CAAM,CAAE,CAAC,EAOvD,MAAO,CACN,QAAS,CAAC,CAAE,KAAM,OAAQ,KAAMC,EAAO,IAAK,CAAC,EAC7C,kBAAmBA,EAAO,KAC1B,MAAO,CACN,GAAGL,EACH,GAAGI,CACJ,CACD,CACD,EACD,CACD,CACD,CACD,CAKA,eAAsBE,GACrBR,EACAS,EACgB,CAChB,MAAM,QAAQ,IAAIA,EAAQ,IAAKC,GAAMA,EAAE,SAASV,CAAM,CAAC,CAAC,CACzD","names":["detectPlatform","isOpenAI","isMCPApps","START","END","INTERRUPT","WIDGET","interrupt","config","showWidget","isInterrupt","value","isWidget","z","buildFlowProtocol","_config","resolveNextNode","edge","state","buildWidgetMeta","widgetId","openaiTemplateUri","mcpTemplateUri","executeFrom","startNodeName","initialState","nodes","edges","flowId","meta","currentNode","MAX_ITERATIONS","iterations","END","handler","result","isInterrupt","isWidget","error","message","inputSchema","z","compileFlow","input","config","protocol","fullDescription","handleToolCall","args","startEdge","START","firstNode","updatedState","nextNode","server","extra","_meta","StateGraph","config","name","handler","START","END","from","to","condition","options","compileFlow","startEdge","edge","createFlow","config","StateGraph","MIME_TYPE_OPENAI","MIME_TYPE_MCP","fetchHtml","baseUrl","path","normalizedBase","buildOpenAIResourceMeta","config","buildMcpAppsResourceMeta","csp","buildToolMeta","createWidget","handler","id","title","description","widgetDescription","htmlPath","inputSchema","invoking","invoked","widgetDomain","prefersBorder","autoHeight","widgetCSP","annotations","uiDescription","openaiTemplateUri","mcpTemplateUri","server","html","toolMeta","uri","args","extra","_meta","result","registerWidgets","widgets","w"]}
1
+ {"version":3,"sources":["../../src/mcp/react/widgets/platform.ts","../../src/mcp/server/flows/@types.ts","../../src/mcp/server/flows/compile.ts","../../src/mcp/server/flows/state-graph.ts","../../src/mcp/server/flows/create-flow.ts","../../src/mcp/server/resources/meta.ts","../../src/mcp/server/resources/create-resource.ts","../../src/mcp/server/tools/create-tool.ts"],"sourcesContent":["/**\n * Widget platform types\n */\nexport type WidgetPlatform = \"openai\" | \"mcp-apps\";\n\n/**\n * Detects which platform the widget is running on.\n *\n * OpenAI injects a global `window.openai` object.\n * MCP Apps runs in a sandboxed iframe and uses postMessage.\n */\nexport function detectPlatform(): WidgetPlatform {\n\tif (typeof window !== \"undefined\" && \"openai\" in window) {\n\t\treturn \"openai\";\n\t}\n\treturn \"mcp-apps\";\n}\n\n/**\n * Check if running on OpenAI platform\n */\nexport function isOpenAI(): boolean {\n\treturn detectPlatform() === \"openai\";\n}\n\n/**\n * Check if running on MCP Apps platform\n */\nexport function isMCPApps(): boolean {\n\treturn detectPlatform() === \"mcp-apps\";\n}\n","import type { McpServer, RegisteredResource } from \"../resources/types\";\n\nexport type { McpServer };\n\n// ============================================================================\n// Sentinel constants\n// ============================================================================\n\nexport const START = \"__start__\" as const;\nexport const END = \"__end__\" as const;\n\n// ============================================================================\n// Signal types — returned by node handlers to control flow behavior\n// ============================================================================\n\nconst INTERRUPT = Symbol.for(\"waniwani.flow.interrupt\");\nconst WIDGET = Symbol.for(\"waniwani.flow.widget\");\n\nexport type InterruptSignal = {\n\treadonly __type: typeof INTERRUPT;\n\t/** Question to ask the user */\n\tquestion: string;\n\t/** State key where the answer will be stored */\n\tfield: string;\n\t/** Optional suggestions to present as options */\n\tsuggestions?: string[];\n\t/** Hidden context/instructions for the assistant (not shown to user directly) */\n\tcontext?: string;\n};\n\nexport type WidgetSignal = {\n\treadonly __type: typeof WIDGET;\n\t/** The resource to display */\n\tresource: RegisteredResource;\n\t/** Data to pass to the widget as structuredContent */\n\tdata: Record<string, unknown>;\n\t/** Description of what the widget does (for the AI's context) */\n\tdescription?: string;\n};\n\n/**\n * Create an interrupt signal — pauses the flow and asks the user a text question.\n */\nexport function interrupt(config: {\n\tquestion: string;\n\tfield: string;\n\tsuggestions?: string[];\n\tcontext?: string;\n}): InterruptSignal {\n\treturn { __type: INTERRUPT, ...config };\n}\n\n/**\n * Create a widget signal — pauses the flow and renders a widget UI.\n */\nexport function showWidget(\n\tresource: RegisteredResource,\n\tconfig: {\n\t\tdata: Record<string, unknown>;\n\t\tdescription?: string;\n\t},\n): WidgetSignal {\n\treturn { __type: WIDGET, resource, ...config };\n}\n\nexport function isInterrupt(value: unknown): value is InterruptSignal {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"__type\" in value &&\n\t\t(value as InterruptSignal).__type === INTERRUPT\n\t);\n}\n\nexport function isWidget(value: unknown): value is WidgetSignal {\n\treturn (\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"__type\" in value &&\n\t\t(value as WidgetSignal).__type === WIDGET\n\t);\n}\n\n// ============================================================================\n// Node & edge definitions\n// ============================================================================\n\nexport type MaybePromise<T> = T | Promise<T>;\n\n/** Configuration for a flow node */\nexport type NodeConfig = {\n\t/** Resource to display when this node returns a WidgetSignal */\n\tresource?: RegisteredResource;\n};\n\n/**\n * Node handler — a single function type for all node kinds.\n * The return value determines behavior:\n * - `Partial<TState>` → action node (state merged, auto-advance)\n * - `InterruptSignal` → interrupt (pause, ask user)\n * - `WidgetSignal` → widget step (pause, show widget)\n */\nexport type NodeHandler<TState> = (\n\tstate: Partial<TState>,\n\tmeta?: Record<string, unknown>,\n) => MaybePromise<Partial<TState> | InterruptSignal | WidgetSignal>;\n\n/**\n * Condition function for conditional edges.\n * Receives current state, returns the name of the next node.\n */\nexport type ConditionFn<TState> = (\n\tstate: Partial<TState>,\n) => string | Promise<string>;\n\nexport type Edge<TState> =\n\t| { type: \"direct\"; to: string }\n\t| { type: \"conditional\"; condition: ConditionFn<TState> };\n\n// ============================================================================\n// Flow config & compiled output\n// ============================================================================\n\nexport type FlowConfig = {\n\t/** Unique identifier for the flow (becomes the MCP tool name) */\n\tid: string;\n\t/** Display title */\n\ttitle: string;\n\t/** Description for the AI (explains when to use this flow) */\n\tdescription: string;\n\t/** Optional tool annotations */\n\tannotations?: {\n\t\treadOnlyHint?: boolean;\n\t\tidempotentHint?: boolean;\n\t\topenWorldHint?: boolean;\n\t\tdestructiveHint?: boolean;\n\t};\n};\n\n/**\n * A compiled flow — can be registered on an McpServer.\n */\nexport type RegisteredFlow = {\n\tid: string;\n\ttitle: string;\n\tdescription: string;\n\tregister: (server: McpServer) => Promise<void>;\n};\n","import type { ToolCallback } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n\tServerNotification,\n\tServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport type {\n\tEdge,\n\tFlowConfig,\n\tMcpServer,\n\tNodeHandler,\n\tRegisteredFlow,\n} from \"./@types\";\nimport { END, isInterrupt, isWidget, START } from \"./@types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface CompileInput<TState extends Record<string, unknown>> {\n\tconfig: FlowConfig;\n\tnodes: Map<string, NodeHandler<TState>>;\n\tedges: Map<string, Edge<TState>>;\n}\n\ntype FlowToolInput = {\n\taction: \"start\" | \"continue\" | \"widget_result\";\n\tstep?: string;\n\tstate?: Record<string, unknown>;\n\tanswer?: string;\n\twidgetResult?: Record<string, unknown>;\n};\n\ntype ExecutionResult = {\n\ttext: string;\n\tdata: Record<string, unknown>;\n\twidgetMeta?: Record<string, unknown>;\n};\n\n// ============================================================================\n// Flow protocol — embedded in tool description\n// ============================================================================\n\nfunction buildFlowProtocol(_config: FlowConfig): string {\n\treturn [\n\t\t\"\",\n\t\t\"## FLOW EXECUTION PROTOCOL\",\n\t\t\"\",\n\t\t\"This tool implements a multi-step conversational flow. Follow this protocol exactly:\",\n\t\t\"\",\n\t\t'1. Call with `action: \"start\"` to begin.',\n\t\t\"2. The response JSON `status` field tells you what to do next:\",\n\t\t' - `\"interrupt\"`: Ask the user the `question`. If a `context` field is present,',\n\t\t\" use it as hidden instructions to enrich your response (do NOT show it verbatim).\",\n\t\t\" Then call again with:\",\n\t\t' `action: \"continue\"`, `step` = the returned `step`, `state` = the returned `state`,',\n\t\t\" `answer` = the user's answer.\",\n\t\t' - `\"widget\"`: A widget UI is being shown. Do NOT call this tool again — the widget handles the callback.',\n\t\t' - `\"complete\"`: The flow is done. Present the result to the user.',\n\t\t' - `\"error\"`: Something went wrong. Show the `error` message.',\n\t\t\"\",\n\t\t\"3. ALWAYS pass back the `state` object exactly as received.\",\n\t\t\"4. Do NOT skip steps or invent state values.\",\n\t].join(\"\\n\");\n}\n\n// ============================================================================\n// Edge resolution\n// ============================================================================\n\nasync function resolveNextNode<TState extends Record<string, unknown>>(\n\tedge: Edge<TState>,\n\tstate: Partial<TState>,\n): Promise<string> {\n\tif (edge.type === \"direct\") return edge.to;\n\treturn edge.condition(state);\n}\n\n// ============================================================================\n// Execution engine\n// ============================================================================\n\nasync function executeFrom<TState extends Record<string, unknown>>(\n\tstartNodeName: string,\n\tinitialState: TState,\n\tnodes: Map<string, NodeHandler<TState>>,\n\tedges: Map<string, Edge<TState>>,\n\tflowId: string,\n\tmeta?: Record<string, unknown>,\n): Promise<ExecutionResult> {\n\tlet currentNode = startNodeName;\n\tlet state = { ...initialState };\n\n\t// Safety limit to prevent infinite loops\n\tconst MAX_ITERATIONS = 50;\n\tlet iterations = 0;\n\n\twhile (iterations++ < MAX_ITERATIONS) {\n\t\t// Reached END\n\t\tif (currentNode === END) {\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"complete\",\n\t\t\t\t\tstate,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"complete\", state },\n\t\t\t};\n\t\t}\n\n\t\tconst handler = nodes.get(currentNode);\n\t\tif (!handler) {\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\terror: `Unknown node: \"${currentNode}\"`,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"error\" },\n\t\t\t};\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await handler(state, meta);\n\n\t\t\t// Interrupt signal — pause and ask the user\n\t\t\tif (isInterrupt(result)) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"interrupt\",\n\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\tquestion: result.question,\n\t\t\t\t\t\tfield: result.field,\n\t\t\t\t\t\tsuggestions: result.suggestions,\n\t\t\t\t\t\t...(result.context ? { context: result.context } : {}),\n\t\t\t\t\t\tstate,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"interrupt\", step: currentNode, state },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Widget signal — pause and show widget\n\t\t\tif (isWidget(result)) {\n\t\t\t\tconst resource = result.resource;\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"widget\",\n\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\twidgetId: resource.id,\n\t\t\t\t\t\tdescription: result.description,\n\t\t\t\t\t\tstate,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: {\n\t\t\t\t\t\t...result.data,\n\t\t\t\t\t\t__flow: {\n\t\t\t\t\t\t\tflowId,\n\t\t\t\t\t\t\tstep: currentNode,\n\t\t\t\t\t\t\tstate,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\twidgetMeta: {\n\t\t\t\t\t\t\"openai/outputTemplate\": resource.openaiUri,\n\t\t\t\t\t\t\"openai/widgetAccessible\": true,\n\t\t\t\t\t\t\"openai/resultCanProduceWidget\": true,\n\t\t\t\t\t\tui: {\n\t\t\t\t\t\t\tresourceUri: resource.mcpUri,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Action node — merge state and auto-advance\n\t\t\tstate = { ...state, ...result } as TState;\n\n\t\t\tconst edge = edges.get(currentNode);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No outgoing edge from node \"${currentNode}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tcurrentNode = await resolveNextNode(edge, state);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\treturn {\n\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\tstep: currentNode,\n\t\t\t\t\terror: message,\n\t\t\t\t\tstate,\n\t\t\t\t}),\n\t\t\t\tdata: { status: \"error\", error: message },\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {\n\t\ttext: JSON.stringify({\n\t\t\tstatus: \"error\",\n\t\t\terror: \"Flow exceeded maximum iterations (possible infinite loop)\",\n\t\t}),\n\t\tdata: { status: \"error\" },\n\t};\n}\n\n// ============================================================================\n// Compile\n// ============================================================================\n\nconst inputSchema = {\n\taction: z\n\t\t.enum([\"start\", \"continue\", \"widget_result\"])\n\t\t.describe(\n\t\t\t'\"start\" to begin the flow, \"continue\" after the user answers a question, \"widget_result\" when a widget returns data',\n\t\t),\n\tstep: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\"Current step name (from the previous response)\"),\n\tstate: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\"Flow state — pass back exactly as received\"),\n\tanswer: z\n\t\t.string()\n\t\t.optional()\n\t\t.describe(\"The user's answer (for interrupt steps)\"),\n\twidgetResult: z\n\t\t.record(z.string(), z.unknown())\n\t\t.optional()\n\t\t.describe(\"Data returned by a widget callback\"),\n};\n\nexport function compileFlow<TState extends Record<string, unknown>>(\n\tinput: CompileInput<TState>,\n): RegisteredFlow {\n\tconst { config, nodes, edges } = input;\n\tconst protocol = buildFlowProtocol(config);\n\tconst fullDescription = `${config.description}\\n${protocol}`;\n\n\tasync function handleToolCall(\n\t\targs: FlowToolInput,\n\t\tmeta?: Record<string, unknown>,\n\t): Promise<ExecutionResult> {\n\t\tconst state = (args.state ?? {}) as TState;\n\n\t\tif (args.action === \"start\") {\n\t\t\tconst startEdge = edges.get(START);\n\t\t\tif (!startEdge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: \"No start edge\",\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst firstNode = await resolveNextNode(startEdge, state);\n\t\t\treturn executeFrom(firstNode, state, nodes, edges, config.id, meta);\n\t\t}\n\n\t\tif (args.action === \"continue\") {\n\t\t\tif (!args.step) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: 'Missing \"step\" for continue action',\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Apply user's answer to state using the field from the interrupt\n\t\t\tlet updatedState = { ...state };\n\t\t\tif (args.answer) {\n\t\t\t\tconst handler = nodes.get(args.step);\n\t\t\t\tif (handler) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst result = await handler(updatedState, meta);\n\t\t\t\t\t\tif (isInterrupt(result) && result.field) {\n\t\t\t\t\t\t\tupdatedState = {\n\t\t\t\t\t\t\t\t...updatedState,\n\t\t\t\t\t\t\t\t[result.field]: args.answer,\n\t\t\t\t\t\t\t} as TState;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// If re-running the handler fails, still proceed with the answer\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Advance to next node\n\t\t\tconst edge = edges.get(args.step);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No edge from step \"${args.step}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst nextNode = await resolveNextNode(edge, updatedState);\n\t\t\treturn executeFrom(nextNode, updatedState, nodes, edges, config.id, meta);\n\t\t}\n\n\t\tif (args.action === \"widget_result\") {\n\t\t\tif (!args.step) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: 'Missing \"step\" for widget_result action',\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Merge widget result into state\n\t\t\tconst updatedState = {\n\t\t\t\t...state,\n\t\t\t\t...(args.widgetResult ?? {}),\n\t\t\t} as TState;\n\n\t\t\t// Advance to next node\n\t\t\tconst edge = edges.get(args.step);\n\t\t\tif (!edge) {\n\t\t\t\treturn {\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\terror: `No edge from step \"${args.step}\"`,\n\t\t\t\t\t}),\n\t\t\t\t\tdata: { status: \"error\" },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst nextNode = await resolveNextNode(edge, updatedState);\n\t\t\treturn executeFrom(nextNode, updatedState, nodes, edges, config.id, meta);\n\t\t}\n\n\t\treturn {\n\t\t\ttext: JSON.stringify({\n\t\t\t\tstatus: \"error\",\n\t\t\t\terror: `Unknown action: \"${args.action}\"`,\n\t\t\t}),\n\t\t\tdata: { status: \"error\" },\n\t\t};\n\t}\n\n\treturn {\n\t\tid: config.id,\n\t\ttitle: config.title,\n\t\tdescription: fullDescription,\n\n\t\tasync register(server: McpServer): Promise<void> {\n\t\t\tserver.registerTool(\n\t\t\t\tconfig.id,\n\t\t\t\t{\n\t\t\t\t\ttitle: config.title,\n\t\t\t\t\tdescription: fullDescription,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tannotations: config.annotations,\n\t\t\t\t},\n\t\t\t\t(async (args: FlowToolInput, extra: unknown) => {\n\t\t\t\t\tconst requestExtra = extra as RequestHandlerExtra<\n\t\t\t\t\t\tServerRequest,\n\t\t\t\t\t\tServerNotification\n\t\t\t\t\t>;\n\t\t\t\t\tconst _meta: Record<string, unknown> = requestExtra._meta ?? {};\n\n\t\t\t\t\tconst result = await handleToolCall(args, _meta);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t...(result.widgetMeta ?? {}),\n\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}) as unknown as ToolCallback<typeof inputSchema>,\n\t\t\t);\n\t\t},\n\t};\n}\n","import type {\n\tConditionFn,\n\tEdge,\n\tFlowConfig,\n\tNodeConfig,\n\tNodeHandler,\n\tRegisteredFlow,\n} from \"./@types\";\nimport { END, START } from \"./@types\";\nimport { compileFlow } from \"./compile\";\n\n/**\n * A LangGraph-inspired state graph builder for MCP tools.\n *\n * @example\n * ```ts\n * const flow = new StateGraph<MyState>({\n * id: \"onboarding\",\n * title: \"User Onboarding\",\n * description: \"Guides users through onboarding\",\n * })\n * .addNode(\"ask_name\", () => interrupt({ question: \"What's your name?\", field: \"name\" }))\n * .addNode(\"greet\", (state) => ({ greeting: `Hello ${state.name}!` }))\n * .addEdge(START, \"ask_name\")\n * .addEdge(\"ask_name\", \"greet\")\n * .addEdge(\"greet\", END)\n * .compile();\n * ```\n */\nexport class StateGraph<TState extends Record<string, unknown>> {\n\tprivate nodes = new Map<string, NodeHandler<TState>>();\n\tprivate edges = new Map<string, Edge<TState>>();\n\tprivate config: FlowConfig;\n\n\tconstructor(config: FlowConfig) {\n\t\tthis.config = config;\n\t}\n\n\t/**\n\t * Add a node with just a handler.\n\t */\n\taddNode(name: string, handler: NodeHandler<TState>): this;\n\t/**\n\t * Add a node with config (e.g., resource) and a handler.\n\t */\n\taddNode(name: string, config: NodeConfig, handler: NodeHandler<TState>): this;\n\taddNode(\n\t\tname: string,\n\t\tconfigOrHandler: NodeConfig | NodeHandler<TState>,\n\t\tmaybeHandler?: NodeHandler<TState>,\n\t): this {\n\t\tif (name === START || name === END) {\n\t\t\tthrow new Error(\n\t\t\t\t`\"${name}\" is a reserved name and cannot be used as a node name`,\n\t\t\t);\n\t\t}\n\t\tif (this.nodes.has(name)) {\n\t\t\tthrow new Error(`Node \"${name}\" already exists`);\n\t\t}\n\n\t\tlet handler: NodeHandler<TState>;\n\n\t\tif (typeof configOrHandler === \"function\") {\n\t\t\thandler = configOrHandler;\n\t\t} else {\n\t\t\tif (!maybeHandler) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`addNode(\"${name}\", config, handler) requires a handler as the third argument`,\n\t\t\t\t);\n\t\t\t}\n\t\t\thandler = maybeHandler;\n\t\t}\n\n\t\tthis.nodes.set(name, handler);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a direct edge between two nodes.\n\t *\n\t * Use `START` as `from` to set the entry point.\n\t * Use `END` as `to` to mark a terminal node.\n\t */\n\taddEdge(from: string, to: string): this {\n\t\tif (this.edges.has(from)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Node \"${from}\" already has an outgoing edge. Use addConditionalEdge for branching.`,\n\t\t\t);\n\t\t}\n\t\tthis.edges.set(from, { type: \"direct\", to });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add a conditional edge from a node.\n\t *\n\t * The condition function receives current state and returns the name of the next node.\n\t */\n\taddConditionalEdge(from: string, condition: ConditionFn<TState>): this {\n\t\tif (this.edges.has(from)) {\n\t\t\tthrow new Error(`Node \"${from}\" already has an outgoing edge.`);\n\t\t}\n\t\tthis.edges.set(from, { type: \"conditional\", condition });\n\t\treturn this;\n\t}\n\n\t/**\n\t * Compile the graph into a RegisteredFlow that can be registered on an McpServer.\n\t *\n\t * Validates the graph structure and returns a registration-compatible object.\n\t */\n\tcompile(): RegisteredFlow {\n\t\tthis.validate();\n\n\t\treturn compileFlow<TState>({\n\t\t\tconfig: this.config,\n\t\t\tnodes: new Map(this.nodes),\n\t\t\tedges: new Map(this.edges),\n\t\t});\n\t}\n\n\tprivate validate(): void {\n\t\t// Must have a START edge\n\t\tif (!this.edges.has(START)) {\n\t\t\tthrow new Error(\n\t\t\t\t'Flow must have an entry point. Add an edge from START: .addEdge(START, \"first_node\")',\n\t\t\t);\n\t\t}\n\n\t\t// START edge target must exist\n\t\tconst startEdge = this.edges.get(START);\n\t\tif (\n\t\t\tstartEdge?.type === \"direct\" &&\n\t\t\tstartEdge.to !== END &&\n\t\t\t!this.nodes.has(startEdge.to)\n\t\t) {\n\t\t\tthrow new Error(\n\t\t\t\t`START edge references non-existent node: \"${startEdge.to}\"`,\n\t\t\t);\n\t\t}\n\n\t\t// All static edge targets must reference existing nodes (or END)\n\t\tfor (const [from, edge] of this.edges) {\n\t\t\tif (from !== START && !this.nodes.has(from)) {\n\t\t\t\tthrow new Error(`Edge from non-existent node: \"${from}\"`);\n\t\t\t}\n\t\t\tif (\n\t\t\t\tedge.type === \"direct\" &&\n\t\t\t\tedge.to !== END &&\n\t\t\t\t!this.nodes.has(edge.to)\n\t\t\t) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Edge from \"${from}\" references non-existent node: \"${edge.to}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Every node must have an outgoing edge\n\t\tfor (const [name] of this.nodes) {\n\t\t\tif (!this.edges.has(name)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Node \"${name}\" has no outgoing edge. Add one with .addEdge(\"${name}\", ...) or .addConditionalEdge(\"${name}\", ...)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { FlowConfig } from \"./@types\";\nimport { StateGraph } from \"./state-graph\";\n\n/**\n * Create a new flow graph — convenience factory for `new StateGraph()`.\n *\n * @example\n * ```ts\n * import { createFlow, interrupt, START, END } from \"@waniwani/sdk/mcp\";\n *\n * type MyState = { name: string; email: string };\n *\n * const flow = createFlow<MyState>({\n * id: \"onboarding\",\n * title: \"User Onboarding\",\n * description: \"Guides users through onboarding. Use when a user wants to get started.\",\n * })\n * .addNode(\"ask_name\", () => interrupt({ question: \"What's your name?\", field: \"name\" }))\n * .addNode(\"ask_email\", () => interrupt({ question: \"What's your email?\", field: \"email\" }))\n * .addEdge(START, \"ask_name\")\n * .addEdge(\"ask_name\", \"ask_email\")\n * .addEdge(\"ask_email\", END)\n * .compile();\n * ```\n */\nexport function createFlow<TState extends Record<string, unknown>>(\n\tconfig: FlowConfig,\n): StateGraph<TState> {\n\treturn new StateGraph<TState>(config);\n}\n","import type { WidgetCSP } from \"./types\";\n\n/**\n * MIME types for widget resources.\n * OpenAI Apps SDK uses \"text/html+skybridge\"\n * MCP Apps uses \"text/html;profile=mcp-app\"\n */\nexport const MIME_TYPE_OPENAI = \"text/html+skybridge\";\nexport const MIME_TYPE_MCP = \"text/html;profile=mcp-app\";\n\n// ---- HTML fetching ----\n\nexport const fetchHtml = async (\n\tbaseUrl: string,\n\tpath: string,\n): Promise<string> => {\n\tconst normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n\tconst result = await fetch(`${normalizedBase}${path}`);\n\treturn await result.text();\n};\n\n// ---- OpenAI resource metadata ----\n\ninterface OpenAIResourceMeta {\n\t[key: string]: unknown;\n\t\"openai/widgetDescription\"?: string;\n\t\"openai/widgetPrefersBorder\"?: boolean;\n\t\"openai/widgetDomain\"?: string;\n\t\"openai/widgetCSP\"?: WidgetCSP;\n}\n\nexport function buildOpenAIResourceMeta(config: {\n\tdescription?: string;\n\tprefersBorder?: boolean;\n\twidgetDomain: string;\n\twidgetCSP?: WidgetCSP;\n}): OpenAIResourceMeta {\n\treturn {\n\t\t\"openai/widgetDescription\": config.description,\n\t\t\"openai/widgetPrefersBorder\": config.prefersBorder,\n\t\t\"openai/widgetDomain\": config.widgetDomain,\n\t\t...(config.widgetCSP && { \"openai/widgetCSP\": config.widgetCSP }),\n\t};\n}\n\n// ---- MCP Apps resource metadata ----\n\ninterface McpAppsResourceMeta {\n\t[key: string]: unknown;\n\tui?: {\n\t\tcsp?: {\n\t\t\tconnectDomains?: string[];\n\t\t\tresourceDomains?: string[];\n\t\t\tframeDomains?: string[];\n\t\t\tredirectDomains?: string[];\n\t\t};\n\t\tdomain?: string;\n\t\tprefersBorder?: boolean;\n\t};\n}\n\nexport function buildMcpAppsResourceMeta(config: {\n\tdescription?: string;\n\tprefersBorder?: boolean;\n\twidgetCSP?: WidgetCSP;\n}): McpAppsResourceMeta {\n\tconst csp = config.widgetCSP\n\t\t? {\n\t\t\t\tconnectDomains: config.widgetCSP.connect_domains,\n\t\t\t\tresourceDomains: config.widgetCSP.resource_domains,\n\t\t\t\tframeDomains: config.widgetCSP.frame_domains,\n\t\t\t\tredirectDomains: config.widgetCSP.redirect_domains,\n\t\t\t}\n\t\t: undefined;\n\n\treturn {\n\t\tui: {\n\t\t\t...(csp && { csp }),\n\t\t\t...(config.prefersBorder !== undefined && {\n\t\t\t\tprefersBorder: config.prefersBorder,\n\t\t\t}),\n\t\t},\n\t};\n}\n\n// ---- Tool metadata (references resource URIs) ----\n\nexport function buildToolMeta(config: {\n\topenaiTemplateUri: string;\n\tmcpTemplateUri: string;\n\tinvoking: string;\n\tinvoked: string;\n\tautoHeight?: boolean;\n}) {\n\treturn {\n\t\t// OpenAI metadata\n\t\t\"openai/outputTemplate\": config.openaiTemplateUri,\n\t\t\"openai/toolInvocation/invoking\": config.invoking,\n\t\t\"openai/toolInvocation/invoked\": config.invoked,\n\t\t\"openai/widgetAccessible\": true,\n\t\t\"openai/resultCanProduceWidget\": true,\n\t\t// MCP Apps metadata\n\t\tui: {\n\t\t\tresourceUri: config.mcpTemplateUri,\n\t\t\t...(config.autoHeight && { autoHeight: true }),\n\t\t},\n\t} as const;\n}\n","import {\n\tbuildMcpAppsResourceMeta,\n\tbuildOpenAIResourceMeta,\n\tfetchHtml,\n\tMIME_TYPE_MCP,\n\tMIME_TYPE_OPENAI,\n} from \"./meta\";\nimport type { McpServer, RegisteredResource, ResourceConfig } from \"./types\";\n\n/**\n * Creates a reusable UI resource (HTML template) that can be attached\n * to tools or flow nodes.\n *\n * @example\n * ```ts\n * const pricingUI = createResource({\n * id: \"pricing_table\",\n * title: \"Pricing Table\",\n * baseUrl: \"https://my-app.com\",\n * htmlPath: \"/widgets/pricing\",\n * widgetDomain: \"my-app.com\",\n * });\n *\n * await pricingUI.register(server);\n * ```\n */\nexport function createResource(config: ResourceConfig): RegisteredResource {\n\tconst {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\t\tbaseUrl,\n\t\thtmlPath,\n\t\twidgetDomain,\n\t\tprefersBorder = true,\n\t\tautoHeight,\n\t\twidgetCSP,\n\t} = config;\n\n\tconst openaiUri = `ui://widgets/apps-sdk/${id}.html`;\n\tconst mcpUri = `ui://widgets/ext-apps/${id}.html`;\n\n\t// Lazy HTML — fetched once, shared across all calls\n\tlet htmlPromise: Promise<string> | null = null;\n\tconst getHtml = () => {\n\t\tif (!htmlPromise) htmlPromise = fetchHtml(baseUrl, htmlPath);\n\t\treturn htmlPromise;\n\t};\n\n\t// Use description for UI metadata\n\tconst uiDescription = description;\n\n\tasync function register(server: McpServer): Promise<void> {\n\t\tconst html = await getHtml();\n\n\t\t// Register OpenAI Apps SDK resource\n\t\tserver.registerResource(\n\t\t\t`${id}-openai-widget`,\n\t\t\topenaiUri,\n\t\t\t{\n\t\t\t\ttitle,\n\t\t\t\tdescription: uiDescription,\n\t\t\t\tmimeType: MIME_TYPE_OPENAI,\n\t\t\t\t_meta: {\n\t\t\t\t\t\"openai/widgetDescription\": uiDescription,\n\t\t\t\t\t\"openai/widgetPrefersBorder\": prefersBorder,\n\t\t\t\t},\n\t\t\t},\n\t\t\tasync (uri) => ({\n\t\t\t\tcontents: [\n\t\t\t\t\t{\n\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\tmimeType: MIME_TYPE_OPENAI,\n\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\t_meta: buildOpenAIResourceMeta({\n\t\t\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t\t\twidgetDomain,\n\t\t\t\t\t\t\twidgetCSP,\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\n\t\t// Register MCP Apps resource\n\t\tserver.registerResource(\n\t\t\t`${id}-mcp-widget`,\n\t\t\tmcpUri,\n\t\t\t{\n\t\t\t\ttitle,\n\t\t\t\tdescription: uiDescription,\n\t\t\t\tmimeType: MIME_TYPE_MCP,\n\t\t\t\t_meta: {\n\t\t\t\t\tui: {\n\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tasync (uri) => ({\n\t\t\t\tcontents: [\n\t\t\t\t\t{\n\t\t\t\t\t\turi: uri.href,\n\t\t\t\t\t\tmimeType: MIME_TYPE_MCP,\n\t\t\t\t\t\ttext: html,\n\t\t\t\t\t\t_meta: buildMcpAppsResourceMeta({\n\t\t\t\t\t\t\tdescription: uiDescription,\n\t\t\t\t\t\t\tprefersBorder,\n\t\t\t\t\t\t\twidgetCSP,\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\t}\n\n\treturn {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\t\topenaiUri,\n\t\tmcpUri,\n\t\tautoHeight,\n\t\tregister,\n\t};\n}\n","import type { ToolCallback } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { ShapeOutput } from \"@modelcontextprotocol/sdk/server/zod-compat.js\";\nimport type { RequestHandlerExtra } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport type {\n\tServerNotification,\n\tServerRequest,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport type { z } from \"zod\";\nimport { buildToolMeta } from \"../resources/meta\";\nimport type {\n\tMcpServer,\n\tRegisteredTool,\n\tToolConfig,\n\tToolHandler,\n} from \"./types\";\n\n/**\n * Creates an MCP tool with minimal boilerplate.\n *\n * When `config.resource` is provided, the tool returns `structuredContent` + widget metadata.\n * Without a resource, the tool returns plain text content.\n *\n * @example\n * ```ts\n * // Widget tool (with resource)\n * const pricingTool = createTool({\n * resource: pricingUI,\n * description: \"Show pricing comparison\",\n * inputSchema: { postalCode: z.string() },\n * }, async ({ postalCode }) => ({\n * text: \"Pricing loaded\",\n * data: { postalCode, prices: [] },\n * }));\n *\n * // Plain tool (no resource)\n * const searchTool = createTool({\n * id: \"search\",\n * title: \"Search\",\n * description: \"Search the knowledge base\",\n * inputSchema: { query: z.string() },\n * }, async ({ query }) => ({\n * text: `Results for \"${query}\"`,\n * }));\n * ```\n */\nexport function createTool<TInput extends z.ZodRawShape>(\n\tconfig: ToolConfig<TInput>,\n\thandler: ToolHandler<TInput>,\n): RegisteredTool {\n\tconst { resource, description, inputSchema, annotations } = config;\n\n\tconst id = config.id ?? resource?.id;\n\tconst title = config.title ?? resource?.title;\n\n\tif (!id) {\n\t\tthrow new Error(\n\t\t\t\"createTool: `id` is required when no resource is provided\",\n\t\t);\n\t}\n\tif (!title) {\n\t\tthrow new Error(\n\t\t\t\"createTool: `title` is required when no resource is provided\",\n\t\t);\n\t}\n\n\t// Build widget metadata only when resource is present\n\tconst toolMeta = resource\n\t\t? buildToolMeta({\n\t\t\t\topenaiTemplateUri: resource.openaiUri,\n\t\t\t\tmcpTemplateUri: resource.mcpUri,\n\t\t\t\tinvoking: config.invoking ?? \"Loading...\",\n\t\t\t\tinvoked: config.invoked ?? \"Loaded\",\n\t\t\t\tautoHeight: resource.autoHeight,\n\t\t\t})\n\t\t: undefined;\n\n\treturn {\n\t\tid,\n\t\ttitle,\n\t\tdescription,\n\n\t\tasync register(server: McpServer): Promise<void> {\n\t\t\tserver.registerTool(\n\t\t\t\tid,\n\t\t\t\t{\n\t\t\t\t\ttitle,\n\t\t\t\t\tdescription,\n\t\t\t\t\tinputSchema,\n\t\t\t\t\tannotations,\n\t\t\t\t\t...(toolMeta && { _meta: toolMeta }),\n\t\t\t\t},\n\t\t\t\t(async (args: ShapeOutput<TInput>, extra: unknown) => {\n\t\t\t\t\tconst requestExtra = extra as RequestHandlerExtra<\n\t\t\t\t\t\tServerRequest,\n\t\t\t\t\t\tServerNotification\n\t\t\t\t\t>;\n\t\t\t\t\tconst _meta: Record<string, unknown> = requestExtra._meta ?? {};\n\n\t\t\t\t\tconst result = await handler(args, { extra: { _meta } });\n\n\t\t\t\t\t// Widget tool: return structuredContent + widget metadata\n\t\t\t\t\tif (resource && result.data) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: result.text }],\n\t\t\t\t\t\t\tstructuredContent: result.data,\n\t\t\t\t\t\t\t_meta: {\n\t\t\t\t\t\t\t\t...toolMeta,\n\t\t\t\t\t\t\t\t..._meta,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Plain tool: return text content only\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: result.text }],\n\t\t\t\t\t};\n\t\t\t\t}) as unknown as ToolCallback<TInput>,\n\t\t\t);\n\t\t},\n\t};\n}\n\n/**\n * Registers multiple tools on the server\n */\nexport async function registerTools(\n\tserver: McpServer,\n\ttools: RegisteredTool[],\n): Promise<void> {\n\tawait Promise.all(tools.map((t) => t.register(server)));\n}\n"],"mappings":"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","names":["detectPlatform","isOpenAI","isMCPApps","START","END","INTERRUPT","WIDGET","interrupt","config","showWidget","resource","isInterrupt","value","isWidget","z","buildFlowProtocol","_config","resolveNextNode","edge","state","executeFrom","startNodeName","initialState","nodes","edges","flowId","meta","currentNode","MAX_ITERATIONS","iterations","END","handler","result","isInterrupt","isWidget","resource","error","message","inputSchema","z","compileFlow","input","config","protocol","fullDescription","handleToolCall","args","startEdge","START","firstNode","updatedState","nextNode","server","extra","_meta","StateGraph","config","name","configOrHandler","maybeHandler","START","END","handler","from","to","condition","compileFlow","startEdge","edge","createFlow","config","StateGraph","MIME_TYPE_OPENAI","MIME_TYPE_MCP","fetchHtml","baseUrl","path","normalizedBase","buildOpenAIResourceMeta","config","buildMcpAppsResourceMeta","csp","buildToolMeta","createResource","config","id","title","description","baseUrl","htmlPath","widgetDomain","prefersBorder","autoHeight","widgetCSP","openaiUri","mcpUri","htmlPromise","getHtml","fetchHtml","uiDescription","register","server","html","MIME_TYPE_OPENAI","uri","buildOpenAIResourceMeta","MIME_TYPE_MCP","buildMcpAppsResourceMeta","createTool","config","handler","resource","description","inputSchema","annotations","id","title","toolMeta","buildToolMeta","server","args","extra","_meta","result","registerTools","tools","t"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waniwani/sdk",
3
- "version": "0.0.16",
3
+ "version": "0.1.0",
4
4
  "description": "WaniWani SDK - MCP event tracking, widget framework, and tools",
5
5
  "type": "module",
6
6
  "exports": {