@mcp-b/embedded-agent 1.2.9 → 1.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -65,36 +65,71 @@ npm install @mcp-b/embedded-agent
65
65
 
66
66
  `<webmcp-agent>` renders as a full-viewport overlay.
67
67
 
68
- ### Web Component (ESM)
68
+ ### Recommended: Imperative Authentication (Secure)
69
+
70
+ The `connect()` method is the recommended way to authenticate. It keeps tokens out of DOM attributes, preventing exposure to session replay tools, error reporters, and DOM inspectors.
69
71
 
70
72
  ```tsx
73
+ import { useRef, useEffect } from "react";
71
74
  import "@mcp-b/embedded-agent/web-component";
75
+ import type { WebMCPAgentElement } from "@mcp-b/embedded-agent/web-component";
72
76
 
73
77
  function App() {
74
- return <webmcp-agent auth-token="eyJhbGciOi..." />;
78
+ const { session } = useOktaAuth(); // or Azure, Auth0, Google, etc.
79
+ const agentRef = useRef<WebMCPAgentElement>(null);
80
+
81
+ useEffect(() => {
82
+ if (agentRef.current && session?.idToken) {
83
+ agentRef.current.connect({ idToken: session.idToken });
84
+ }
85
+ }, [session?.idToken]);
86
+
87
+ return <webmcp-agent ref={agentRef} />;
75
88
  }
76
89
  ```
77
90
 
91
+ ### Vanilla JavaScript
92
+
93
+ ```html
94
+ <webmcp-agent></webmcp-agent>
95
+
96
+ <script type="module">
97
+ import "@mcp-b/embedded-agent/web-component";
98
+
99
+ const agent = document.querySelector("webmcp-agent");
100
+ // Call connect() when you have the token
101
+ agent.connect({ idToken: "eyJhbGciOi..." });
102
+ </script>
103
+ ```
104
+
78
105
  ### Monorepo Dogfooding
79
106
 
80
107
  ```tsx
108
+ import { useRef, useEffect } from "react";
81
109
  import "@mcp-b/embedded-agent/web-component";
110
+ import type { WebMCPAgentElement } from "@mcp-b/embedded-agent/web-component";
82
111
 
83
- // authToken from your existing IDP session
84
112
  const authToken = session?.idToken ?? "";
113
+ const agentRef = useRef<WebMCPAgentElement>(null);
85
114
 
86
115
  useEffect(() => {
87
- const agent =
88
- document.querySelector("webmcp-agent") ?? document.createElement("webmcp-agent");
89
-
90
- if (!agent.isConnected) {
91
- document.body.appendChild(agent);
116
+ const agent = agentRef.current ?? document.querySelector("webmcp-agent");
117
+
118
+ if (!agent) {
119
+ const newAgent = document.createElement("webmcp-agent");
120
+ document.body.appendChild(newAgent);
121
+ // Connect after element is in DOM
122
+ (newAgent as WebMCPAgentElement).connect({ idToken: authToken });
123
+ } else if (authToken) {
124
+ (agent as WebMCPAgentElement).connect({ idToken: authToken });
92
125
  }
93
126
 
94
- agent.setAttribute("auth-token", authToken ?? "");
95
- agent.setAttribute("dev-mode", JSON.stringify({ useLocalApi: true }));
96
- agent.setAttribute("enable-debug-tools", String(import.meta.env.DEV));
127
+ // Set other attributes (these don't contain sensitive data)
128
+ agent?.setAttribute("dev-mode", JSON.stringify({ useLocalApi: true }));
129
+ agent?.setAttribute("enable-debug-tools", String(import.meta.env.DEV));
97
130
  }, [authToken]);
131
+
132
+ return <webmcp-agent ref={agentRef} />;
98
133
  ```
99
134
 
100
135
  ### Script Tag (Any Website)
@@ -104,7 +139,16 @@ Use `defer` for best performance - it loads the script without blocking page ren
104
139
  ```html
105
140
  <!-- Recommended: defer loads async, executes after DOM ready -->
106
141
  <script src="https://unpkg.com/@mcp-b/embedded-agent/dist/web-component-standalone.iife.js" defer></script>
107
- <webmcp-agent auth-token="eyJhbGciOi..."></webmcp-agent>
142
+ <webmcp-agent></webmcp-agent>
143
+
144
+ <script>
145
+ // Authenticate after the page loads (tokens not in DOM attributes)
146
+ document.addEventListener('DOMContentLoaded', () => {
147
+ const agent = document.querySelector('webmcp-agent');
148
+ // Get your token from your auth provider
149
+ agent.connect({ idToken: yourIdpToken });
150
+ });
151
+ </script>
108
152
  ```
109
153
 
110
154
  Alternative CDN (jsdelivr):
@@ -122,18 +166,25 @@ Pin to a specific version for production:
122
166
  ### Web Component Attributes
123
167
 
124
168
  ```html
169
+ <!-- PREFERRED: Use connect() method instead of auth-token attribute -->
125
170
  <webmcp-agent
126
- auth-token="eyJhbGciOi..."
127
171
  dev-mode='{"anthropicApiKey": "sk-ant-..."}'
128
172
  enable-debug-tools="true"
129
173
  ></webmcp-agent>
174
+
175
+ <script>
176
+ const agent = document.querySelector('webmcp-agent');
177
+ agent.connect({ idToken: 'eyJhbGciOi...' });
178
+ </script>
130
179
  ```
131
180
 
132
181
  ## Props / Attributes
133
182
 
134
183
  | Prop | Attribute | Type | Description |
135
184
  |------|-----------|------|-------------|
136
- | `authToken` | `auth-token` | string | IDP token for stateful sessions (optional; omit for stateless) |
185
+ | - | - | method | `connect({ idToken })` - Secure authentication (token not in DOM) |
186
+ | - | - | method | `connect({ ticketAuth })` - SSR-friendly authentication (pre-fetched ticket) |
187
+ | - | - | method | `disconnect()` - Clear authentication |
137
188
  | `open` | `open` | boolean | Controlled open state (optional) |
138
189
  | `devMode` | `dev-mode` | object/JSON | Development mode config (optional) |
139
190
  | `enableDebugTools` | `enable-debug-tools` | boolean | Enable debug tools (default: false) |
@@ -262,7 +313,7 @@ Speed up loading by adding a preconnect hint in your `<head>`:
262
313
  - `siteId` - Org-level routing in SSO-first mode (no site scoping)
263
314
  - `theme` - Use CSS variables instead (see Customization section)
264
315
  - `isolateStyles` - Always enabled (Shadow DOM is always used)
265
- - `disableShadowDOM` - Deprecated (Shadow DOM is always enabled)
316
+ - `disableShadowDOM` - Removed (Shadow DOM is always enabled)
266
317
 
267
318
  **Migration examples:**
268
319
 
@@ -276,11 +327,15 @@ Speed up loading by adding a preconnect hint in your `<head>`:
276
327
  isolate-styles="false"
277
328
  ></webmcp-agent>
278
329
 
279
- <!-- NEW (SSO-first): -->
280
- <webmcp-agent auth-token="eyJhbGciOi..."></webmcp-agent>
281
-
282
- <!-- NEW (stateless): -->
330
+ <!-- NEW (SSO-first with connect method): -->
283
331
  <webmcp-agent></webmcp-agent>
332
+ <script>
333
+ const agent = document.querySelector('webmcp-agent');
334
+ agent.connect({ idToken: 'eyJhbGciOi...' });
335
+ </script>
336
+
337
+ <!-- NEW (anonymous dev mode): -->
338
+ <webmcp-agent dev-mode='{"anthropicApiKey":"sk-ant-..."}'></webmcp-agent>
284
339
 
285
340
  <!-- Customize via CSS: -->
286
341
  <style>
@@ -1 +1 @@
1
- {"version":3,"file":"FormToolUI-Dhl3il9B.js","names":["clsx","twMerge","ClassValue","cn","inputs","React","Slot","cva","VariantProps","cn","buttonVariants","variants","variant","default","destructive","outline","secondary","ghost","link","size","sm","lg","icon","defaultVariants","Button","t0","$","_c","className","props","t1","t2","t3","asChild","undefined","Comp","t4","t5","c","_c","CollapsiblePrimitive","Collapsible","t0","$","props","t1","CollapsibleTrigger","CollapsibleContent","createContext","useContext","CallToolResult","ToolWithSource","FC","ReactNode","AddToolOutputOptions","ToolExecutionContextValue","executeTool","toolName","args","Record","Promise","getToolMetadata","description","annotations","addToolOutput","options","hasPendingApprovals","ToolExecutionContext","ToolExecutionProviderProps","value","children","ToolExecutionProvider","t0","$","_c","t1","useToolExecution","context","Error","useOptionalToolExecution","c","_c","Ajv","makeAssistantToolUI","withTheme","Theme","ShadcnTheme","validator","AlertCircle","Check","ChevronDownIcon","ChevronUpIcon","FileText","useEffect","useRef","useState","JSONSchema7","Button","Collapsible","CollapsibleContent","CollapsibleTrigger","useOptionalToolExecution","Form","ajv","normalizeSchema","schema","obj","Record","type","toLowerCase","properties","Object","fromEntries","entries","map","key","value","items","Array","isArray","additionalProperties","keyword","const","not","validateSchema","isValid","errors","errorMessages","err","path","instancePath","message","join","SchemaErrorDisplay","t0","$","title","error","isOpen","setIsOpen","t1","Symbol","for","t2","t3","t4","t5","t6","t7","FormArgs","description","submitLabel","FormResult","parseFormData","data","keys","hasNonNumericKeys","some","isNaN","Number","parsed","JSON","parse","console","warn","substring","Error","String","formatValue","undefined","stringify","FormResultDisplay","T0","T1","t8","parsedData","t9","t10","t11","_temp","t12","t13","FormToolUI","toolName","render","args","result","status","addResult","toolCallId","toolExecution","errorSentRef","schemaError","setSchemaError","isWaiting","normalizedSchema","current","validationError","_schemaError","addToolOutput","output","handleSubmit","formData","outputValue","norender"],"sources":["../src/utils/cn.ts","../src/components/ui/button.tsx","../src/components/ui/collapsible.tsx","../src/providers/ToolExecutionProvider.tsx","../src/tools/FormToolUI.tsx"],"sourcesContent":["import { clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nimport type { ClassValue } from 'clsx'\n\n/**\n * Merges Tailwind CSS classes with proper precedence handling.\n * Combines clsx for conditional classes and tailwind-merge for deduplication.\n */\nexport function cn(...inputs: ClassValue[]): string {\n\treturn twMerge(clsx(inputs))\n}\n","import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/utils/cn\";\n\nconst buttonVariants = cva(\n \"inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n","\"use client\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\n\nfunction Collapsible({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />;\n}\n\nfunction CollapsibleTrigger({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {\n return (\n <CollapsiblePrimitive.CollapsibleTrigger\n data-slot=\"collapsible-trigger\"\n {...props}\n />\n );\n}\n\nfunction CollapsibleContent({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {\n return (\n <CollapsiblePrimitive.CollapsibleContent\n data-slot=\"collapsible-content\"\n {...props}\n />\n );\n}\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent };\n","import { createContext, useContext } from 'react'\n\nimport type { CallToolResult, ToolWithSource } from '@mcp-b/cloud-mirror-types'\nimport type { FC, ReactNode } from 'react'\nimport type { AddToolOutputOptions } from '../hooks/useThreadRuntime'\n\nexport interface ToolExecutionContextValue {\n\texecuteTool: (toolName: string, args: Record<string, unknown>) => Promise<CallToolResult>\n\tgetToolMetadata: (\n\t\ttoolName: string,\n\t) => { description?: string; annotations?: ToolWithSource['annotations'] } | null\n\t/**\n\t * Add tool output (v6 pattern - sends via WebSocket and updates local state).\n\t * Used by client-side UI tools (done, form) and approval flow.\n\t */\n\taddToolOutput: (options: AddToolOutputOptions) => void\n\t/**\n\t * Whether there are pending HITL tool approvals.\n\t * UI should disable send button and show approval indicator when true.\n\t * Derived from server message metadata - single source of truth.\n\t */\n\thasPendingApprovals: boolean\n}\n\nconst ToolExecutionContext = createContext<ToolExecutionContextValue | null>(null)\n\nexport interface ToolExecutionProviderProps {\n\tvalue: ToolExecutionContextValue\n\tchildren: ReactNode\n}\n\nexport const ToolExecutionProvider: FC<ToolExecutionProviderProps> = ({ value, children }) => {\n\treturn <ToolExecutionContext.Provider value={value}>{children}</ToolExecutionContext.Provider>\n}\n\nexport function useToolExecution(): ToolExecutionContextValue {\n\tconst context = useContext(ToolExecutionContext)\n\tif (!context) {\n\t\tthrow new Error('useToolExecution must be used within a ToolExecutionProvider')\n\t}\n\treturn context\n}\n\nexport function useOptionalToolExecution(): ToolExecutionContextValue | null {\n\treturn useContext(ToolExecutionContext)\n}\n","'use client'\n\nimport Ajv from 'ajv'\nimport { makeAssistantToolUI } from '@assistant-ui/react'\nimport { withTheme } from '@rjsf/core'\nimport { Theme as ShadcnTheme } from '@rjsf/shadcn'\nimport validator from '@rjsf/validator-ajv8'\nimport { AlertCircle, Check, ChevronDownIcon, ChevronUpIcon, FileText } from 'lucide-react'\nimport { useEffect, useRef, useState } from 'react'\n\nimport type { JSONSchema7 } from 'json-schema'\n\nimport { Button } from '../components/ui/button'\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../components/ui/collapsible'\nimport { useOptionalToolExecution } from '../providers/ToolExecutionProvider'\n\n// Create themed form component\nconst Form = withTheme(ShadcnTheme)\n\n// AJV instance for schema validation\nconst ajv = new Ajv()\n\n/**\n * Normalize a JSON Schema to handle common model mistakes.\n * - Converts uppercase types (Gemini format) to lowercase (JSON Schema format)\n * - Recursively processes nested schemas\n */\nfunction normalizeSchema(schema: unknown): JSONSchema7 {\n\tif (!schema || typeof schema !== 'object') {\n\t\treturn schema as JSONSchema7\n\t}\n\n\tconst obj = { ...(schema as Record<string, unknown>) }\n\n\t// Lowercase the type if it's a string (handles OBJECT -> object, STRING -> string, etc.)\n\tif (typeof obj.type === 'string') {\n\t\tobj.type = obj.type.toLowerCase()\n\t}\n\n\t// Recursively normalize properties\n\tif (obj.properties && typeof obj.properties === 'object') {\n\t\tobj.properties = Object.fromEntries(\n\t\t\tObject.entries(obj.properties as Record<string, unknown>).map(([key, value]) => [\n\t\t\t\tkey,\n\t\t\t\tnormalizeSchema(value),\n\t\t\t])\n\t\t)\n\t}\n\n\t// Handle items for arrays\n\tif (obj.items) {\n\t\tif (Array.isArray(obj.items)) {\n\t\t\tobj.items = obj.items.map(normalizeSchema)\n\t\t} else {\n\t\t\tobj.items = normalizeSchema(obj.items)\n\t\t}\n\t}\n\n\t// Handle additionalProperties\n\tif (obj.additionalProperties && typeof obj.additionalProperties === 'object') {\n\t\tobj.additionalProperties = normalizeSchema(obj.additionalProperties)\n\t}\n\n\t// Handle allOf, anyOf, oneOf\n\tfor (const keyword of ['allOf', 'anyOf', 'oneOf'] as const) {\n\t\tif (Array.isArray(obj[keyword])) {\n\t\t\tobj[keyword] = (obj[keyword] as unknown[]).map(normalizeSchema)\n\t\t}\n\t}\n\n\t// Handle not\n\tif (obj.not && typeof obj.not === 'object') {\n\t\tobj.not = normalizeSchema(obj.not)\n\t}\n\n\treturn obj as JSONSchema7\n}\n\n/**\n * Validate a JSON Schema using AJV's meta-schema validation.\n * Returns null if valid, or an error message string if invalid.\n */\nfunction validateSchema(schema: JSONSchema7): string | null {\n\tconst isValid = ajv.validateSchema(schema)\n\tif (isValid) return null\n\n\t// Format AJV errors into a readable message\n\tconst errors = ajv.errors ?? []\n\tconst errorMessages = errors.map((err) => {\n\t\tconst path = err.instancePath || 'root'\n\t\treturn `- ${path}: ${err.message}`\n\t})\n\n\treturn [\n\t\t'Invalid JSON Schema:',\n\t\t...errorMessages,\n\t\t'',\n\t\t'Please fix the schema and try again.',\n\t].join('\\n')\n}\n\n/**\n * Display component for schema validation errors\n */\nfunction SchemaErrorDisplay({ title, error }: { title: string; error: string }) {\n\tconst [isOpen, setIsOpen] = useState(true)\n\n\treturn (\n\t\t<Collapsible open={isOpen} onOpenChange={setIsOpen}>\n\t\t\t<div className=\"my-1 overflow-hidden rounded-lg bg-destructive/10 border border-destructive/20\">\n\t\t\t\t<CollapsibleTrigger asChild>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"flex w-full items-center gap-1.5 px-2 py-1.5 text-left transition-colors hover:bg-destructive/15\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"relative\">\n\t\t\t\t\t\t\t<FileText className=\"h-3.5 w-3.5 text-destructive\" />\n\t\t\t\t\t\t\t<AlertCircle className=\"absolute -bottom-0.5 -right-0.5 h-2 w-2 text-destructive\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span className=\"flex-grow text-xs font-medium text-destructive\">\n\t\t\t\t\t\t\t{title} - Invalid Schema\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"inline-flex h-5 w-5 items-center justify-center p-0\">\n\t\t\t\t\t\t\t{isOpen ? (\n\t\t\t\t\t\t\t\t<ChevronUpIcon className=\"h-3 w-3 text-destructive/70\" />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<ChevronDownIcon className=\"h-3 w-3 text-destructive/70\" />\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</button>\n\t\t\t\t</CollapsibleTrigger>\n\n\t\t\t\t<CollapsibleContent>\n\t\t\t\t\t<div className=\"border-t border-destructive/20 px-2 py-1.5\">\n\t\t\t\t\t\t<pre className=\"whitespace-pre-wrap text-[11px] text-foreground/70\">{error}</pre>\n\t\t\t\t\t</div>\n\t\t\t\t</CollapsibleContent>\n\t\t\t</div>\n\t\t</Collapsible>\n\t)\n}\n\n/**\n * Args passed from the model when calling the form tool\n */\ntype FormArgs = {\n\ttitle: string\n\tdescription?: string\n\tschema: JSONSchema7\n\tsubmitLabel?: string\n}\n\n/**\n * Result is the form data submitted by the user\n */\ntype FormResult = Record<string, unknown>\n\n/**\n * Parse form result data, handling cases where it may be stringified\n */\nfunction parseFormData(data: FormResult | string): Record<string, unknown> {\n\t// If it's already an object with non-numeric keys, return as-is\n\tif (typeof data === 'object' && data !== null) {\n\t\tconst keys = Object.keys(data)\n\t\t// Check if keys are numeric (which means it's iterating over a string)\n\t\tconst hasNonNumericKeys = keys.some((key) => isNaN(Number(key)))\n\t\tif (hasNonNumericKeys) {\n\t\t\treturn data\n\t\t}\n\t}\n\n\t// If it's a string, try to parse it\n\tif (typeof data === 'string') {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(data)\n\t\t\tif (typeof parsed === 'object' && parsed !== null) {\n\t\t\t\treturn parsed\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// If parsing fails, log and return as a single \"value\" entry\n\t\t\tconsole.warn('[FormToolUI] Failed to parse form data as JSON:', {\n\t\t\t\tdata: data.substring(0, 100),\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown parse error',\n\t\t\t})\n\t\t\treturn { value: data }\n\t\t}\n\t}\n\n\t// Fallback: return as-is or wrap in an object\n\treturn typeof data === 'object' && data !== null ? data : { value: String(data) }\n}\n\n/**\n * Format a value for display\n */\nfunction formatValue(value: unknown): string {\n\tif (value === null || value === undefined) return '—'\n\tif (typeof value === 'boolean') return value ? 'Yes' : 'No'\n\tif (typeof value === 'object') return JSON.stringify(value)\n\treturn String(value)\n}\n\n/**\n * Display component for submitted form data - matches UnifiedToolFallback pattern\n */\nfunction FormResultDisplay({ title, data }: { title: string; data: FormResult }) {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst parsedData = parseFormData(data)\n\tconst entries = Object.entries(parsedData)\n\n\treturn (\n\t\t<Collapsible open={isOpen} onOpenChange={setIsOpen}>\n\t\t\t<div className=\"my-1 overflow-hidden rounded-lg bg-muted/50\">\n\t\t\t\t<CollapsibleTrigger asChild>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"flex w-full items-center gap-1.5 px-2 py-1.5 text-left transition-colors hover:bg-muted/70\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"relative\">\n\t\t\t\t\t\t\t<FileText className=\"h-3.5 w-3.5 text-muted-foreground\" />\n\t\t\t\t\t\t\t<Check className=\"absolute -bottom-0.5 -right-0.5 h-2 w-2 text-muted-foreground\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span className=\"flex-grow text-xs font-medium text-foreground/80\">\n\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"inline-flex h-5 w-5 items-center justify-center p-0\">\n\t\t\t\t\t\t\t{isOpen ? (\n\t\t\t\t\t\t\t\t<ChevronUpIcon className=\"h-3 w-3 text-muted-foreground/70\" />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<ChevronDownIcon className=\"h-3 w-3 text-muted-foreground/70\" />\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</button>\n\t\t\t\t</CollapsibleTrigger>\n\n\t\t\t\t<CollapsibleContent>\n\t\t\t\t\t<div className=\"border-t border-border px-2 py-1.5\">\n\t\t\t\t\t\t<div className=\"mb-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70\">\n\t\t\t\t\t\t\tSubmitted data\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"rounded bg-muted/50 p-1.5 space-y-0.5\">\n\t\t\t\t\t\t\t{entries.map(([key, value]) => (\n\t\t\t\t\t\t\t\t<div key={key} className=\"flex gap-2 text-[11px]\">\n\t\t\t\t\t\t\t\t\t<span className=\"font-medium text-muted-foreground\">{key}:</span>\n\t\t\t\t\t\t\t\t\t<span className=\"text-foreground/70\">{formatValue(value)}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</CollapsibleContent>\n\t\t\t</div>\n\t\t</Collapsible>\n\t)\n}\n\n/**\n * FormToolUI - Renders a dynamic form from JSON Schema\n *\n * HITL Pattern (AI SDK v6):\n * - Model calls `form` tool with JSON Schema\n * - This UI renders the form using @rjsf/shadcn\n * - User fills out and submits form\n * - addToolOutput() sends data to backend via WebSocket + updates local UI state\n *\n * Schema Handling:\n * - Normalizes uppercase types (Gemini format) to lowercase (JSON Schema)\n * - Validates schema with AJV and sends errors back to model if invalid\n */\nexport const FormToolUI = makeAssistantToolUI<FormArgs, FormResult>({\n\ttoolName: 'form',\n\trender: ({ args, result, status, addResult, toolCallId }) => {\n\t\t// Get addToolOutput from context (v6 pattern - sends via WebSocket + updates local state)\n\t\tconst toolExecution = useOptionalToolExecution()\n\t\t// Track if we've already sent an error to prevent duplicate sends\n\t\tconst errorSentRef = useRef(false)\n\t\t// Track validation error for display\n\t\tconst [schemaError, setSchemaError] = useState<string | null>(null)\n\n\t\tconst isWaiting = status.type === 'requires-action'\n\n\t\t// Normalize and validate schema when ready\n\t\tconst normalizedSchema = args.schema && typeof args.schema === 'object'\n\t\t\t? normalizeSchema(args.schema)\n\t\t\t: null\n\n\t\t// Validate schema and send error to model if invalid\n\t\tuseEffect(() => {\n\t\t\tif (!normalizedSchema || !isWaiting || errorSentRef.current || result) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst validationError = validateSchema(normalizedSchema)\n\t\t\tif (validationError) {\n\t\t\t\terrorSentRef.current = true\n\t\t\t\tsetSchemaError(validationError)\n\n\t\t\t\t// Update local UI state with error (conforming to FormResult type)\n\t\t\t\t// The _schemaError field indicates this is a validation error, not user data\n\t\t\t\taddResult({ _schemaError: true, message: validationError })\n\n\t\t\t\t// Send via WebSocket to backend (if context available)\n\t\t\t\tif (toolExecution) {\n\t\t\t\t\ttoolExecution.addToolOutput({\n\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\toutput: `Error: ${validationError}`,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error('[FormToolUI] Cannot send schema error: ToolExecution context not available')\n\t\t\t\t}\n\t\t\t}\n\t\t}, [normalizedSchema, isWaiting, result, addResult, toolExecution, toolCallId])\n\n\t\t// Show error UI if schema validation failed\n\t\tif (schemaError) {\n\t\t\treturn <SchemaErrorDisplay title={args.title ?? 'Form'} error={schemaError} />\n\t\t}\n\n\t\t// If form was already submitted, show the result\n\t\tif (result) {\n\t\t\treturn <FormResultDisplay title={args.title ?? 'Form'} data={result} />\n\t\t}\n\n\t\t// Handle form submission\n\t\tconst handleSubmit = ({ formData }: { formData?: FormResult }) => {\n\t\t\tif (!formData) return\n\n\t\t\tif (!toolExecution) {\n\t\t\t\tconsole.error('[FormToolUI] Cannot submit form: ToolExecution context not available', {\n\t\t\t\t\ttoolCallId,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Stringify object results for the wire format\n\t\t\tconst outputValue =\n\t\t\t\ttypeof formData === 'object'\n\t\t\t\t\t? JSON.stringify(formData)\n\t\t\t\t\t: String(formData)\n\n\t\t\t// v6 pattern: addToolOutput sends to backend + updates local state\n\t\t\ttoolExecution.addToolOutput({\n\t\t\t\ttoolCallId,\n\t\t\t\toutput: outputValue,\n\t\t\t})\n\n\t\t\t// Also update local UI state\n\t\t\taddResult(formData)\n\t\t}\n\n\t\t// Guard against undefined schema (can happen during streaming when args are being parsed)\n\t\tif (!normalizedSchema) {\n\t\t\treturn (\n\t\t\t\t<div className=\"my-1 flex items-center gap-1.5 rounded-lg bg-muted/50 px-2 py-1.5 text-sm text-foreground/70\">\n\t\t\t\t\t<FileText className=\"h-3.5 w-3.5 animate-pulse text-muted-foreground\" />\n\t\t\t\t\t<span className=\"text-xs\">Loading form...</span>\n\t\t\t\t</div>\n\t\t\t)\n\t\t}\n\n\t\treturn (\n\t\t\t<div className=\"my-1 overflow-hidden rounded-lg bg-muted/50\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"flex items-center gap-1.5 px-2 py-1.5 border-b border-border\">\n\t\t\t\t\t<FileText className=\"h-3.5 w-3.5 text-foreground/70\" />\n\t\t\t\t\t<span className=\"text-xs font-medium text-foreground\">{args.title ?? 'Form'}</span>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Content */}\n\t\t\t\t<div className=\"px-2 py-2\">\n\t\t\t\t\t{/* Description */}\n\t\t\t\t\t{args.description && (\n\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground mb-3\">{args.description}</p>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* RJSF Form */}\n\t\t\t\t\t<Form\n\t\t\t\t\t\tschema={normalizedSchema}\n\t\t\t\t\t\tvalidator={validator}\n\t\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\t\tdisabled={!isWaiting}\n\t\t\t\t\t\tclassName=\"space-y-3\"\n\t\t\t\t\t\t// Hide default submit button, we'll add our own\n\t\t\t\t\t\tuiSchema={{\n\t\t\t\t\t\t\t'ui:submitButtonOptions': {\n\t\t\t\t\t\t\t\tnorender: true,\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\t\t{/* Custom submit button */}\n\t\t\t\t\t\t<div className=\"flex justify-end pt-1\">\n\t\t\t\t\t\t\t<Button type=\"submit\" disabled={!isWaiting} size=\"sm\" className=\"h-7 px-3 text-xs\">\n\t\t\t\t\t\t\t\t<Check className=\"h-3 w-3 mr-1\" />\n\t\t\t\t\t\t\t\t{args.submitLabel || 'Submit'}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</Form>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t},\n})\n\nexport default FormToolUI\n"],"mappings":"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"}
1
+ {"version":3,"file":"FormToolUI-Dhl3il9B.js","names":["clsx","twMerge","ClassValue","cn","inputs","React","Slot","cva","VariantProps","cn","buttonVariants","variants","variant","default","destructive","outline","secondary","ghost","link","size","sm","lg","icon","defaultVariants","Button","t0","$","_c","className","props","t1","t2","t3","asChild","undefined","Comp","t4","t5","c","_c","CollapsiblePrimitive","Collapsible","t0","$","props","t1","CollapsibleTrigger","CollapsibleContent","createContext","useContext","CallToolResult","ToolWithSource","FC","ReactNode","AddToolOutputOptions","ToolExecutionContextValue","executeTool","toolName","args","Record","Promise","getToolMetadata","description","annotations","addToolOutput","options","hasPendingApprovals","ToolExecutionContext","ToolExecutionProviderProps","value","children","ToolExecutionProvider","t0","$","_c","t1","useToolExecution","context","Error","useOptionalToolExecution","c","_c","Ajv","makeAssistantToolUI","withTheme","Theme","ShadcnTheme","validator","AlertCircle","Check","ChevronDownIcon","ChevronUpIcon","FileText","useEffect","useRef","useState","JSONSchema7","Button","Collapsible","CollapsibleContent","CollapsibleTrigger","useOptionalToolExecution","Form","ajv","normalizeSchema","schema","obj","Record","type","toLowerCase","properties","Object","fromEntries","entries","map","key","value","items","Array","isArray","additionalProperties","keyword","const","not","validateSchema","isValid","errors","errorMessages","err","path","instancePath","message","join","SchemaErrorDisplay","t0","$","title","error","isOpen","setIsOpen","t1","Symbol","for","t2","t3","t4","t5","t6","t7","FormArgs","description","submitLabel","FormResult","parseFormData","data","keys","hasNonNumericKeys","some","isNaN","Number","parsed","JSON","parse","console","warn","substring","Error","String","formatValue","undefined","stringify","FormResultDisplay","T0","T1","t8","parsedData","t9","t10","t11","_temp","t12","t13","FormToolUI","toolName","render","args","result","status","addResult","toolCallId","toolExecution","errorSentRef","schemaError","setSchemaError","isWaiting","normalizedSchema","current","validationError","_schemaError","addToolOutput","output","handleSubmit","formData","outputValue","norender"],"sources":["../src/utils/cn.ts","../src/components/ui/button.tsx","../src/components/ui/collapsible.tsx","../src/providers/ToolExecutionProvider.tsx","../src/tools/FormToolUI.tsx"],"sourcesContent":["import { clsx } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nimport type { ClassValue } from 'clsx'\n\n/**\n * Merges Tailwind CSS classes with proper precedence handling.\n * Combines clsx for conditional classes and tailwind-merge for deduplication.\n */\nexport function cn(...inputs: ClassValue[]): string {\n\treturn twMerge(clsx(inputs))\n}\n","import * as React from \"react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/utils/cn\";\n\nconst buttonVariants = cva(\n \"inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md font-medium text-sm outline-none transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean;\n }) {\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n","\"use client\";\n\nimport * as CollapsiblePrimitive from \"@radix-ui/react-collapsible\";\n\nfunction Collapsible({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />;\n}\n\nfunction CollapsibleTrigger({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {\n return (\n <CollapsiblePrimitive.CollapsibleTrigger\n data-slot=\"collapsible-trigger\"\n {...props}\n />\n );\n}\n\nfunction CollapsibleContent({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {\n return (\n <CollapsiblePrimitive.CollapsibleContent\n data-slot=\"collapsible-content\"\n {...props}\n />\n );\n}\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent };\n","import { createContext, useContext } from 'react'\n\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'\nimport type { ToolWithSource } from '@mcp-b/shared-types'\nimport type { FC, ReactNode } from 'react'\nimport type { AddToolOutputOptions } from '../hooks/useThreadRuntime'\n\nexport interface ToolExecutionContextValue {\n\texecuteTool: (toolName: string, args: Record<string, unknown>) => Promise<CallToolResult>\n\tgetToolMetadata: (\n\t\ttoolName: string,\n\t) => { description?: string; annotations?: ToolWithSource['annotations'] } | null\n\t/**\n\t * Add tool output (v6 pattern - sends via WebSocket and updates local state).\n\t * Used by client-side UI tools (done, form) and approval flow.\n\t */\n\taddToolOutput: (options: AddToolOutputOptions) => void\n\t/**\n\t * Whether there are pending HITL tool approvals.\n\t * UI should disable send button and show approval indicator when true.\n\t * Derived from server message metadata - single source of truth.\n\t */\n\thasPendingApprovals: boolean\n}\n\nconst ToolExecutionContext = createContext<ToolExecutionContextValue | null>(null)\n\nexport interface ToolExecutionProviderProps {\n\tvalue: ToolExecutionContextValue\n\tchildren: ReactNode\n}\n\nexport const ToolExecutionProvider: FC<ToolExecutionProviderProps> = ({ value, children }) => {\n\treturn <ToolExecutionContext.Provider value={value}>{children}</ToolExecutionContext.Provider>\n}\n\nexport function useToolExecution(): ToolExecutionContextValue {\n\tconst context = useContext(ToolExecutionContext)\n\tif (!context) {\n\t\tthrow new Error('useToolExecution must be used within a ToolExecutionProvider')\n\t}\n\treturn context\n}\n\nexport function useOptionalToolExecution(): ToolExecutionContextValue | null {\n\treturn useContext(ToolExecutionContext)\n}\n","'use client'\n\nimport Ajv from 'ajv'\nimport { makeAssistantToolUI } from '@assistant-ui/react'\nimport { withTheme } from '@rjsf/core'\nimport { Theme as ShadcnTheme } from '@rjsf/shadcn'\nimport validator from '@rjsf/validator-ajv8'\nimport { AlertCircle, Check, ChevronDownIcon, ChevronUpIcon, FileText } from 'lucide-react'\nimport { useEffect, useRef, useState } from 'react'\n\nimport type { JSONSchema7 } from 'json-schema'\n\nimport { Button } from '../components/ui/button'\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from '../components/ui/collapsible'\nimport { useOptionalToolExecution } from '../providers/ToolExecutionProvider'\n\n// Create themed form component\nconst Form = withTheme(ShadcnTheme)\n\n// AJV instance for schema validation\nconst ajv = new Ajv()\n\n/**\n * Normalize a JSON Schema to handle common model mistakes.\n * - Converts uppercase types (Gemini format) to lowercase (JSON Schema format)\n * - Recursively processes nested schemas\n */\nfunction normalizeSchema(schema: unknown): JSONSchema7 {\n\tif (!schema || typeof schema !== 'object') {\n\t\treturn schema as JSONSchema7\n\t}\n\n\tconst obj = { ...(schema as Record<string, unknown>) }\n\n\t// Lowercase the type if it's a string (handles OBJECT -> object, STRING -> string, etc.)\n\tif (typeof obj.type === 'string') {\n\t\tobj.type = obj.type.toLowerCase()\n\t}\n\n\t// Recursively normalize properties\n\tif (obj.properties && typeof obj.properties === 'object') {\n\t\tobj.properties = Object.fromEntries(\n\t\t\tObject.entries(obj.properties as Record<string, unknown>).map(([key, value]) => [\n\t\t\t\tkey,\n\t\t\t\tnormalizeSchema(value),\n\t\t\t])\n\t\t)\n\t}\n\n\t// Handle items for arrays\n\tif (obj.items) {\n\t\tif (Array.isArray(obj.items)) {\n\t\t\tobj.items = obj.items.map(normalizeSchema)\n\t\t} else {\n\t\t\tobj.items = normalizeSchema(obj.items)\n\t\t}\n\t}\n\n\t// Handle additionalProperties\n\tif (obj.additionalProperties && typeof obj.additionalProperties === 'object') {\n\t\tobj.additionalProperties = normalizeSchema(obj.additionalProperties)\n\t}\n\n\t// Handle allOf, anyOf, oneOf\n\tfor (const keyword of ['allOf', 'anyOf', 'oneOf'] as const) {\n\t\tif (Array.isArray(obj[keyword])) {\n\t\t\tobj[keyword] = (obj[keyword] as unknown[]).map(normalizeSchema)\n\t\t}\n\t}\n\n\t// Handle not\n\tif (obj.not && typeof obj.not === 'object') {\n\t\tobj.not = normalizeSchema(obj.not)\n\t}\n\n\treturn obj as JSONSchema7\n}\n\n/**\n * Validate a JSON Schema using AJV's meta-schema validation.\n * Returns null if valid, or an error message string if invalid.\n */\nfunction validateSchema(schema: JSONSchema7): string | null {\n\tconst isValid = ajv.validateSchema(schema)\n\tif (isValid) return null\n\n\t// Format AJV errors into a readable message\n\tconst errors = ajv.errors ?? []\n\tconst errorMessages = errors.map((err) => {\n\t\tconst path = err.instancePath || 'root'\n\t\treturn `- ${path}: ${err.message}`\n\t})\n\n\treturn [\n\t\t'Invalid JSON Schema:',\n\t\t...errorMessages,\n\t\t'',\n\t\t'Please fix the schema and try again.',\n\t].join('\\n')\n}\n\n/**\n * Display component for schema validation errors\n */\nfunction SchemaErrorDisplay({ title, error }: { title: string; error: string }) {\n\tconst [isOpen, setIsOpen] = useState(true)\n\n\treturn (\n\t\t<Collapsible open={isOpen} onOpenChange={setIsOpen}>\n\t\t\t<div className=\"my-1 overflow-hidden rounded-lg bg-destructive/10 border border-destructive/20\">\n\t\t\t\t<CollapsibleTrigger asChild>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"flex w-full items-center gap-1.5 px-2 py-1.5 text-left transition-colors hover:bg-destructive/15\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"relative\">\n\t\t\t\t\t\t\t<FileText className=\"h-3.5 w-3.5 text-destructive\" />\n\t\t\t\t\t\t\t<AlertCircle className=\"absolute -bottom-0.5 -right-0.5 h-2 w-2 text-destructive\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span className=\"flex-grow text-xs font-medium text-destructive\">\n\t\t\t\t\t\t\t{title} - Invalid Schema\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"inline-flex h-5 w-5 items-center justify-center p-0\">\n\t\t\t\t\t\t\t{isOpen ? (\n\t\t\t\t\t\t\t\t<ChevronUpIcon className=\"h-3 w-3 text-destructive/70\" />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<ChevronDownIcon className=\"h-3 w-3 text-destructive/70\" />\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</button>\n\t\t\t\t</CollapsibleTrigger>\n\n\t\t\t\t<CollapsibleContent>\n\t\t\t\t\t<div className=\"border-t border-destructive/20 px-2 py-1.5\">\n\t\t\t\t\t\t<pre className=\"whitespace-pre-wrap text-[11px] text-foreground/70\">{error}</pre>\n\t\t\t\t\t</div>\n\t\t\t\t</CollapsibleContent>\n\t\t\t</div>\n\t\t</Collapsible>\n\t)\n}\n\n/**\n * Args passed from the model when calling the form tool\n */\ntype FormArgs = {\n\ttitle: string\n\tdescription?: string\n\tschema: JSONSchema7\n\tsubmitLabel?: string\n}\n\n/**\n * Result is the form data submitted by the user\n */\ntype FormResult = Record<string, unknown>\n\n/**\n * Parse form result data, handling cases where it may be stringified\n */\nfunction parseFormData(data: FormResult | string): Record<string, unknown> {\n\t// If it's already an object with non-numeric keys, return as-is\n\tif (typeof data === 'object' && data !== null) {\n\t\tconst keys = Object.keys(data)\n\t\t// Check if keys are numeric (which means it's iterating over a string)\n\t\tconst hasNonNumericKeys = keys.some((key) => isNaN(Number(key)))\n\t\tif (hasNonNumericKeys) {\n\t\t\treturn data\n\t\t}\n\t}\n\n\t// If it's a string, try to parse it\n\tif (typeof data === 'string') {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(data)\n\t\t\tif (typeof parsed === 'object' && parsed !== null) {\n\t\t\t\treturn parsed\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// If parsing fails, log and return as a single \"value\" entry\n\t\t\tconsole.warn('[FormToolUI] Failed to parse form data as JSON:', {\n\t\t\t\tdata: data.substring(0, 100),\n\t\t\t\terror: error instanceof Error ? error.message : 'Unknown parse error',\n\t\t\t})\n\t\t\treturn { value: data }\n\t\t}\n\t}\n\n\t// Fallback: return as-is or wrap in an object\n\treturn typeof data === 'object' && data !== null ? data : { value: String(data) }\n}\n\n/**\n * Format a value for display\n */\nfunction formatValue(value: unknown): string {\n\tif (value === null || value === undefined) return '—'\n\tif (typeof value === 'boolean') return value ? 'Yes' : 'No'\n\tif (typeof value === 'object') return JSON.stringify(value)\n\treturn String(value)\n}\n\n/**\n * Display component for submitted form data - matches UnifiedToolFallback pattern\n */\nfunction FormResultDisplay({ title, data }: { title: string; data: FormResult }) {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst parsedData = parseFormData(data)\n\tconst entries = Object.entries(parsedData)\n\n\treturn (\n\t\t<Collapsible open={isOpen} onOpenChange={setIsOpen}>\n\t\t\t<div className=\"my-1 overflow-hidden rounded-lg bg-muted/50\">\n\t\t\t\t<CollapsibleTrigger asChild>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"flex w-full items-center gap-1.5 px-2 py-1.5 text-left transition-colors hover:bg-muted/70\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"relative\">\n\t\t\t\t\t\t\t<FileText className=\"h-3.5 w-3.5 text-muted-foreground\" />\n\t\t\t\t\t\t\t<Check className=\"absolute -bottom-0.5 -right-0.5 h-2 w-2 text-muted-foreground\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span className=\"flex-grow text-xs font-medium text-foreground/80\">\n\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"inline-flex h-5 w-5 items-center justify-center p-0\">\n\t\t\t\t\t\t\t{isOpen ? (\n\t\t\t\t\t\t\t\t<ChevronUpIcon className=\"h-3 w-3 text-muted-foreground/70\" />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<ChevronDownIcon className=\"h-3 w-3 text-muted-foreground/70\" />\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</button>\n\t\t\t\t</CollapsibleTrigger>\n\n\t\t\t\t<CollapsibleContent>\n\t\t\t\t\t<div className=\"border-t border-border px-2 py-1.5\">\n\t\t\t\t\t\t<div className=\"mb-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground/70\">\n\t\t\t\t\t\t\tSubmitted data\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"rounded bg-muted/50 p-1.5 space-y-0.5\">\n\t\t\t\t\t\t\t{entries.map(([key, value]) => (\n\t\t\t\t\t\t\t\t<div key={key} className=\"flex gap-2 text-[11px]\">\n\t\t\t\t\t\t\t\t\t<span className=\"font-medium text-muted-foreground\">{key}:</span>\n\t\t\t\t\t\t\t\t\t<span className=\"text-foreground/70\">{formatValue(value)}</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</CollapsibleContent>\n\t\t\t</div>\n\t\t</Collapsible>\n\t)\n}\n\n/**\n * FormToolUI - Renders a dynamic form from JSON Schema\n *\n * HITL Pattern (AI SDK v6):\n * - Model calls `form` tool with JSON Schema\n * - This UI renders the form using @rjsf/shadcn\n * - User fills out and submits form\n * - addToolOutput() sends data to backend via WebSocket + updates local UI state\n *\n * Schema Handling:\n * - Normalizes uppercase types (Gemini format) to lowercase (JSON Schema)\n * - Validates schema with AJV and sends errors back to model if invalid\n */\nexport const FormToolUI = makeAssistantToolUI<FormArgs, FormResult>({\n\ttoolName: 'form',\n\trender: ({ args, result, status, addResult, toolCallId }) => {\n\t\t// Get addToolOutput from context (v6 pattern - sends via WebSocket + updates local state)\n\t\tconst toolExecution = useOptionalToolExecution()\n\t\t// Track if we've already sent an error to prevent duplicate sends\n\t\tconst errorSentRef = useRef(false)\n\t\t// Track validation error for display\n\t\tconst [schemaError, setSchemaError] = useState<string | null>(null)\n\n\t\tconst isWaiting = status.type === 'requires-action'\n\n\t\t// Normalize and validate schema when ready\n\t\tconst normalizedSchema = args.schema && typeof args.schema === 'object'\n\t\t\t? normalizeSchema(args.schema)\n\t\t\t: null\n\n\t\t// Validate schema and send error to model if invalid\n\t\tuseEffect(() => {\n\t\t\tif (!normalizedSchema || !isWaiting || errorSentRef.current || result) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst validationError = validateSchema(normalizedSchema)\n\t\t\tif (validationError) {\n\t\t\t\terrorSentRef.current = true\n\t\t\t\tsetSchemaError(validationError)\n\n\t\t\t\t// Update local UI state with error (conforming to FormResult type)\n\t\t\t\t// The _schemaError field indicates this is a validation error, not user data\n\t\t\t\taddResult({ _schemaError: true, message: validationError })\n\n\t\t\t\t// Send via WebSocket to backend (if context available)\n\t\t\t\tif (toolExecution) {\n\t\t\t\t\ttoolExecution.addToolOutput({\n\t\t\t\t\t\ttoolCallId,\n\t\t\t\t\t\toutput: `Error: ${validationError}`,\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error('[FormToolUI] Cannot send schema error: ToolExecution context not available')\n\t\t\t\t}\n\t\t\t}\n\t\t}, [normalizedSchema, isWaiting, result, addResult, toolExecution, toolCallId])\n\n\t\t// Show error UI if schema validation failed\n\t\tif (schemaError) {\n\t\t\treturn <SchemaErrorDisplay title={args.title ?? 'Form'} error={schemaError} />\n\t\t}\n\n\t\t// If form was already submitted, show the result\n\t\tif (result) {\n\t\t\treturn <FormResultDisplay title={args.title ?? 'Form'} data={result} />\n\t\t}\n\n\t\t// Handle form submission\n\t\tconst handleSubmit = ({ formData }: { formData?: FormResult }) => {\n\t\t\tif (!formData) return\n\n\t\t\tif (!toolExecution) {\n\t\t\t\tconsole.error('[FormToolUI] Cannot submit form: ToolExecution context not available', {\n\t\t\t\t\ttoolCallId,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Stringify object results for the wire format\n\t\t\tconst outputValue =\n\t\t\t\ttypeof formData === 'object'\n\t\t\t\t\t? JSON.stringify(formData)\n\t\t\t\t\t: String(formData)\n\n\t\t\t// v6 pattern: addToolOutput sends to backend + updates local state\n\t\t\ttoolExecution.addToolOutput({\n\t\t\t\ttoolCallId,\n\t\t\t\toutput: outputValue,\n\t\t\t})\n\n\t\t\t// Also update local UI state\n\t\t\taddResult(formData)\n\t\t}\n\n\t\t// Guard against undefined schema (can happen during streaming when args are being parsed)\n\t\tif (!normalizedSchema) {\n\t\t\treturn (\n\t\t\t\t<div className=\"my-1 flex items-center gap-1.5 rounded-lg bg-muted/50 px-2 py-1.5 text-sm text-foreground/70\">\n\t\t\t\t\t<FileText className=\"h-3.5 w-3.5 animate-pulse text-muted-foreground\" />\n\t\t\t\t\t<span className=\"text-xs\">Loading form...</span>\n\t\t\t\t</div>\n\t\t\t)\n\t\t}\n\n\t\treturn (\n\t\t\t<div className=\"my-1 overflow-hidden rounded-lg bg-muted/50\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"flex items-center gap-1.5 px-2 py-1.5 border-b border-border\">\n\t\t\t\t\t<FileText className=\"h-3.5 w-3.5 text-foreground/70\" />\n\t\t\t\t\t<span className=\"text-xs font-medium text-foreground\">{args.title ?? 'Form'}</span>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Content */}\n\t\t\t\t<div className=\"px-2 py-2\">\n\t\t\t\t\t{/* Description */}\n\t\t\t\t\t{args.description && (\n\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground mb-3\">{args.description}</p>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* RJSF Form */}\n\t\t\t\t\t<Form\n\t\t\t\t\t\tschema={normalizedSchema}\n\t\t\t\t\t\tvalidator={validator}\n\t\t\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\t\t\tdisabled={!isWaiting}\n\t\t\t\t\t\tclassName=\"space-y-3\"\n\t\t\t\t\t\t// Hide default submit button, we'll add our own\n\t\t\t\t\t\tuiSchema={{\n\t\t\t\t\t\t\t'ui:submitButtonOptions': {\n\t\t\t\t\t\t\t\tnorender: true,\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\t\t{/* Custom submit button */}\n\t\t\t\t\t\t<div className=\"flex justify-end pt-1\">\n\t\t\t\t\t\t\t<Button type=\"submit\" disabled={!isWaiting} size=\"sm\" className=\"h-7 px-3 text-xs\">\n\t\t\t\t\t\t\t\t<Check className=\"h-3 w-3 mr-1\" />\n\t\t\t\t\t\t\t\t{args.submitLabel || 'Submit'}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</Form>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t)\n\t},\n})\n\nexport default FormToolUI\n"],"mappings":"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"}
package/dist/jsx.d.ts CHANGED
@@ -22,29 +22,105 @@ interface DevModeConfig {
22
22
  useLocalApi?: boolean;
23
23
  }
24
24
 
25
+ /**
26
+ * Client-side ticket auth data for SSR authentication.
27
+ * This is what the embedded widget uses for WebSocket authentication.
28
+ *
29
+ * NOTE: This interface is intentionally duplicated from @char-ai/auth.
30
+ * Declaration files (.d.ts) cannot import from implementation modules
31
+ * without causing circular dependency issues. The type is simple and
32
+ * stable, so duplication is acceptable here.
33
+ *
34
+ * @see packages/auth/src/security/ticket.ts for the source of truth
35
+ */
36
+ interface TicketAuth {
37
+ /** The opaque ticket string */
38
+ ticket: string;
39
+ /** User ID (from token's sub claim) - needed for DO routing */
40
+ userId: string;
41
+ /** Organization ID - needed for DO routing */
42
+ orgId: string;
43
+ }
44
+
45
+ /**
46
+ * Connection options for the imperative connect() method.
47
+ * Supports two authentication methods:
48
+ * - `idToken`: Direct IDP token authentication (SPA-friendly)
49
+ * - `ticketAuth`: Pre-fetched ticket authentication (SSR-friendly)
50
+ */
51
+ interface ConnectOptions {
52
+ /**
53
+ * IDP token (ID token) for authentication.
54
+ * Token is stored as a JavaScript property (not as a DOM attribute),
55
+ * preventing exposure to DOM inspection and session replay tools.
56
+ *
57
+ * Either `idToken` or `ticketAuth` must be provided, but not both.
58
+ */
59
+ idToken?: string;
60
+
61
+ /**
62
+ * Pre-fetched ticket for SSR-friendly authentication.
63
+ * Use this when your server has already exchanged the IDP token for a ticket.
64
+ *
65
+ * Benefits:
66
+ * - IDP token never exposed to browser (in SSR mode)
67
+ * - No JWKS fetch on every connection (validation done once)
68
+ * - Ticket is short-lived (60s default) and single-use
69
+ *
70
+ * Either `idToken` or `ticketAuth` must be provided, but not both.
71
+ */
72
+ ticketAuth?: TicketAuth;
73
+ }
74
+
75
+ /**
76
+ * WebMCP Agent custom element interface.
77
+ * Extends HTMLElement with secure authentication methods.
78
+ */
79
+ interface WebMCPAgentElement extends HTMLElement {
80
+ /**
81
+ * Connect to the Char agent with authentication.
82
+ *
83
+ * The token is stored as a JavaScript property (not as a DOM attribute),
84
+ * preventing exposure to DOM inspection and session replay tools.
85
+ * This is the recommended way to authenticate.
86
+ *
87
+ * @param options.idToken - IDP token (ID token) for authentication
88
+ * @returns true if connection was initiated, false if idToken was missing
89
+ *
90
+ * @example
91
+ * ```ts
92
+ * // Vanilla JS
93
+ * const agent = document.querySelector('webmcp-agent') as WebMCPAgentElement
94
+ * const success = agent.connect({ idToken: session.idToken })
95
+ * if (!success) {
96
+ * console.error('Failed to connect')
97
+ * }
98
+ *
99
+ * // React with ref
100
+ * const agentRef = useRef<WebMCPAgentElement>(null)
101
+ * useEffect(() => {
102
+ * agentRef.current?.connect({ idToken: session.idToken })
103
+ * }, [session.idToken])
104
+ * ```
105
+ */
106
+ connect(options: ConnectOptions): boolean;
107
+
108
+ /**
109
+ * Disconnect from the Char agent.
110
+ * Clears the authentication token.
111
+ * @returns true if disconnection succeeded, false if an error occurred
112
+ */
113
+ disconnect(): boolean;
114
+ }
115
+
116
+ // Export the element type for consumers
117
+ export type { WebMCPAgentElement, ConnectOptions, TicketAuth, DevModeConfig };
118
+
25
119
  declare module 'react' {
26
120
  namespace JSX {
27
121
  interface IntrinsicElements {
28
122
  'webmcp-agent': React.DetailedHTMLProps<
29
- React.HTMLAttributes<HTMLElement> & {
30
- /**
31
- * IDP token for SSO-first authentication.
32
- *
33
- * Pass your existing IDP token (from Okta, Azure AD, Auth0, Google, etc.)
34
- * and the backend will validate it against your organization's configured IDP.
35
- *
36
- * When provided:
37
- * - Messages persist across refreshes (Cloudflare Durable Objects)
38
- * - User ID extracted from token's sub claim
39
- * - Tools shared across all your organization's apps
40
- *
41
- * When NOT provided:
42
- * - Anonymous mode with Durable Objects
43
- * - Requires anthropicApiKey in dev-mode for AI calls
44
- * - Localhost-only for security
45
- */
46
- 'auth-token'?: string;
47
-
123
+ React.HTMLAttributes<WebMCPAgentElement> & {
48
124
  /**
49
125
  * Controlled open state.
50
126
  * When provided, the host controls whether the agent UI is open or collapsed.
@@ -62,20 +138,18 @@ declare module 'react' {
62
138
  * @example Vanilla HTML/JS
63
139
  * ```html
64
140
  * <script type="module">
65
- * import '@anthropic/char-embedded-agent/web-component'
141
+ * import '@mcp-b/embedded-agent/web-component'
66
142
  * </script>
67
143
  *
68
- * <!-- Production: SSO authentication -->
69
- * <webmcp-agent auth-token="eyJhbGciOiJSUzI1NiI..."></webmcp-agent>
70
- *
71
- * <!-- Development: Anonymous mode (localhost only) -->
72
- * <webmcp-agent dev-mode='{"anthropicApiKey":"sk-ant-..."}'></webmcp-agent>
73
- *
74
- * <!-- Or set via JavaScript -->
144
+ * <!-- Production: SSO authentication (use connect method) -->
145
+ * <webmcp-agent></webmcp-agent>
75
146
  * <script>
76
147
  * const agent = document.querySelector('webmcp-agent');
77
- * agent.devMode = { anthropicApiKey: 'sk-ant-...', useLocalApi: true };
148
+ * agent.connect({ idToken: 'eyJhbGciOiJSUzI1NiI...' });
78
149
  * </script>
150
+ *
151
+ * <!-- Development: Anonymous mode (localhost only) -->
152
+ * <webmcp-agent dev-mode='{"anthropicApiKey":"sk-ant-..."}'></webmcp-agent>
79
153
  * ```
80
154
  *
81
155
  * @example React 18 (JSON string attribute)
@@ -153,8 +227,23 @@ declare module 'react' {
153
227
  * ```
154
228
  */
155
229
  onClose?: () => void;
230
+
231
+ /**
232
+ * React ref for accessing the web component's imperative API.
233
+ * Use this to call connect() method securely.
234
+ *
235
+ * @example
236
+ * ```tsx
237
+ * const agentRef = useRef<WebMCPAgentElement>(null);
238
+ * useEffect(() => {
239
+ * agentRef.current?.connect({ idToken: session.idToken });
240
+ * }, [session.idToken]);
241
+ * return <webmcp-agent ref={agentRef} />;
242
+ * ```
243
+ */
244
+ ref?: React.Ref<WebMCPAgentElement>;
156
245
  },
157
- HTMLElement
246
+ WebMCPAgentElement
158
247
  >;
159
248
  }
160
249
  }