@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 +74 -19
- package/dist/FormToolUI-Dhl3il9B.js.map +1 -1
- package/dist/jsx.d.ts +118 -29
- package/dist/styles/globals.css +1 -1
- package/dist/web-component.d.ts +90 -36
- package/dist/web-component.d.ts.map +1 -1
- package/dist/web-component.js +1104 -161
- package/dist/web-component.js.map +1 -1
- package/package.json +4 -3
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
|
-
###
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
document.body.appendChild(
|
|
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
|
-
|
|
95
|
-
agent
|
|
96
|
-
agent
|
|
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
|
|
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
|
-
|
|
|
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` -
|
|
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<
|
|
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 '@
|
|
141
|
+
* import '@mcp-b/embedded-agent/web-component'
|
|
66
142
|
* </script>
|
|
67
143
|
*
|
|
68
|
-
* <!-- Production: SSO authentication -->
|
|
69
|
-
* <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.
|
|
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
|
-
|
|
246
|
+
WebMCPAgentElement
|
|
158
247
|
>;
|
|
159
248
|
}
|
|
160
249
|
}
|