@mcp-b/char 0.0.5 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +76 -362
  2. package/dist/custom-elements.json +2180 -0
  3. package/dist/display-mode-policy.d.ts +82 -0
  4. package/dist/display-mode-policy.d.ts.map +1 -0
  5. package/dist/display-mode-policy.js +87 -0
  6. package/dist/display-mode-policy.js.map +1 -0
  7. package/dist/index.d.ts +707 -326
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2370 -15550
  10. package/dist/index.js.map +1 -1
  11. package/dist/shell-component.d.ts +379 -0
  12. package/dist/shell-component.d.ts.map +1 -0
  13. package/dist/shell-component.js +2504 -0
  14. package/dist/shell-component.js.map +1 -0
  15. package/dist/tsdoc-metadata.json +11 -0
  16. package/dist/utils.d.ts +161 -0
  17. package/dist/utils.d.ts.map +1 -0
  18. package/dist/utils.js +393 -0
  19. package/dist/utils.js.map +1 -0
  20. package/dist/web-component-standalone.iife.js +1255 -2358
  21. package/dist/web-component-standalone.iife.js.map +1 -1
  22. package/dist/web-component.d.ts +381 -180
  23. package/dist/web-component.d.ts.map +1 -1
  24. package/dist/web-component.js +1137 -15768
  25. package/dist/web-component.js.map +1 -1
  26. package/package.json +23 -115
  27. package/THIRD_PARTY_NOTICES.md +0 -52
  28. package/dist/VoiceHandoffPanel-CIFIJSDs.js +0 -244
  29. package/dist/VoiceHandoffPanel-CIFIJSDs.js.map +0 -1
  30. package/dist/button-BLnLZvxR.js +0 -105
  31. package/dist/button-BLnLZvxR.js.map +0 -1
  32. package/dist/realtimekit.d.ts +0 -15
  33. package/dist/realtimekit.d.ts.map +0 -1
  34. package/dist/realtimekit.js +0 -89
  35. package/dist/realtimekit.js.map +0 -1
  36. package/dist/styles/globals.css +0 -2
  37. package/dist/styles.d.ts +0 -2
  38. package/dist/web-component-standalone.css +0 -37
  39. package/dist/web-component-standalone.css.map +0 -1
package/README.md CHANGED
@@ -1,405 +1,119 @@
1
1
  # @mcp-b/char
2
2
 
3
- An Intercom-like AI chat widget with MCP tool support and voice mode. Drop it into your app and you're done.
3
+ Framework-agnostic custom elements for embedding Char.
4
4
 
5
- Styles are isolated using Shadow DOM. Customize appearance by setting CSS variables on the host page.
5
+ This package is zero-dependency at runtime and ships two elements:
6
6
 
7
- ## Build Architecture
7
+ - `<char-agent>`: low-level iframe + bridge primitive
8
+ - `<char-agent-shell>`: opinionated host shell (PIP composer, desktop push panel, responsive fullscreen)
8
9
 
9
- This package provides the `<char-agent>` custom element in two formats:
10
-
11
- | Export | Format | Use Case |
12
- |--------|--------|----------|
13
- | `@mcp-b/char` / `@mcp-b/char/web-component` | ESM web component | Bundlers, monorepo dev |
14
- | `@mcp-b/char/standalone` | IIFE web component | `<script>` tag embeds |
15
-
16
- ### Bundle Sizes
17
-
18
- | Build | Size | Gzipped | Use Case |
19
- |-------|------|---------|----------|
20
- | ESM | ~560 KB | ~115 KB | Bundlers (React externalized) |
21
- | Standalone IIFE | ~2.2 MB | ~400 KB | Script tag embeds |
22
-
23
- The standalone IIFE includes React, so it's larger but works on any website without dependencies.
24
-
25
- ### Why Two Builds?
26
-
27
- **ESM Build** (root export or `/web-component`)
28
- - Web component entrypoint for bundlers
29
- - React is externalized via `peerDependencies` (install `react` and `react-dom`)
30
- - Smaller bundle; no duplicate React if the host already uses it
31
- - Uses Shadow DOM for style isolation
32
- - Use for: bundlers, monorepo consumers
33
-
34
- **Standalone IIFE Build** (`/standalone`)
35
- - Web component via `<script>` tag
36
- - React is bundled inside the package
37
- - Uses Shadow DOM for complete JS/CSS isolation
38
- - Separate React instances in separate DOM trees = no conflict
39
- - Use for: `<script>` tag embedding on any website (React or not)
40
-
41
- ### The "Two Reacts" Problem
42
-
43
- When a React app loads the standalone bundle, you get duplicate React instances which causes the infamous "hooks can only be called inside a function component" error.
44
-
45
- **Shadow DOM solves this**: Each React instance manages its own separate DOM tree inside the shadow boundary, so they never conflict.
46
-
47
- ```
48
- Host Page (React 18)
49
- ├── <div id="root"> ← Host's React
50
- │ └── ... host app ...
51
-
52
- └── <char-agent>
53
- └── #shadow-root ← Isolation boundary
54
- └── <div> ← Bundled React 19
55
- └── ... widget ...
56
- ```
57
-
58
- ## Installation
10
+ ## Install
59
11
 
60
12
  ```bash
61
- npm install @mcp-b/char
62
- ```
63
-
64
- ## Usage
65
-
66
- `<char-agent>` renders as a full-viewport overlay.
67
-
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.
71
-
72
- ```tsx
73
- import { useRef, useEffect } from "react";
74
- import "@mcp-b/char/web-component";
75
- import type { CharAgentElement } from "@mcp-b/char/web-component";
76
-
77
- function App() {
78
- const { session } = useOktaAuth(); // or Azure, Auth0, Google, etc.
79
- const agentRef = useRef<CharAgentElement>(null);
80
-
81
- useEffect(() => {
82
- if (agentRef.current && session?.idToken) {
83
- agentRef.current.connect({ idToken: session.idToken });
84
- }
85
- }, [session?.idToken]);
86
-
87
- return <char-agent ref={agentRef} />;
88
- }
89
- ```
90
-
91
- ### Vanilla JavaScript
92
-
93
- ```html
94
- <char-agent></char-agent>
95
-
96
- <script type="module">
97
- import "@mcp-b/char/web-component";
98
-
99
- const agent = document.querySelector("char-agent");
100
- // Call connect() when you have the token
101
- agent.connect({ idToken: "eyJhbGciOi..." });
102
- </script>
13
+ npm i @mcp-b/char
103
14
  ```
104
15
 
105
- ### Monorepo Dogfooding
106
-
107
- ```tsx
108
- import { useRef, useEffect } from "react";
109
- import "@mcp-b/char/web-component";
110
- import type { CharAgentElement } from "@mcp-b/char/web-component";
111
-
112
- const authToken = session?.idToken ?? "";
113
- const agentRef = useRef<CharAgentElement>(null);
114
-
115
- useEffect(() => {
116
- const agent = agentRef.current ?? document.querySelector("char-agent");
117
-
118
- if (!agent) {
119
- const newAgent = document.createElement("char-agent");
120
- document.body.appendChild(newAgent);
121
- // Connect after element is in DOM
122
- (newAgent as CharAgentElement).connect({ idToken: authToken });
123
- } else if (authToken) {
124
- (agent as CharAgentElement).connect({ idToken: authToken });
125
- }
16
+ ## Element registration
126
17
 
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));
130
- }, [authToken]);
131
-
132
- return <char-agent ref={agentRef} />;
18
+ ```ts
19
+ import '@mcp-b/char/web-component' // registers <char-agent>
20
+ import '@mcp-b/char/shell-component' // registers <char-agent-shell>
133
21
  ```
134
22
 
135
- ### Script Tag (Any Website)
136
-
137
- Use `defer` for best performance - it loads the script without blocking page rendering:
23
+ Importing `@mcp-b/char` root also registers both.
138
24
 
139
- ```html
140
- <!-- Recommended: defer loads async, executes after DOM ready -->
141
- <script src="https://unpkg.com/@mcp-b/char/dist/web-component-standalone.iife.js" defer></script>
142
- <char-agent></char-agent>
143
-
144
- <script>
145
- // Authenticate after the page loads (tokens not in DOM attributes)
146
- document.addEventListener('DOMContentLoaded', () => {
147
- const agent = document.querySelector('char-agent');
148
- // Get your token from your auth provider
149
- agent.connect({ idToken: yourIdpToken });
150
- });
151
- </script>
152
- ```
25
+ ## Low-level primitive: `<char-agent>`
153
26
 
154
- Alternative CDN (jsdelivr):
27
+ Use this when your app already owns all host chrome/layout.
155
28
 
156
29
  ```html
157
- <script src="https://cdn.jsdelivr.net/npm/@mcp-b/char/dist/web-component-standalone.iife.js" defer></script>
158
- ```
159
-
160
- Pin to a specific version for production:
161
-
162
- ```html
163
- <script src="https://unpkg.com/@mcp-b/char@0.0.2/dist/web-component-standalone.iife.js" defer></script>
164
- ```
30
+ <char-agent id="assistant" style="width: 420px; height: 100vh" display-mode="inline"></char-agent>
31
+ <script type="module">
32
+ import '@mcp-b/char/web-component'
165
33
 
166
- ### Web Component Attributes
34
+ /** @type {import('@mcp-b/char/web-component').CharAgentElement | null} */
35
+ const agent = document.querySelector('#assistant')
167
36
 
168
- ```html
169
- <!-- PREFERRED: Use connect() method instead of auth-token attribute -->
170
- <char-agent
171
- dev-mode='{"anthropicApiKey": "sk-ant-..."}'
172
- enable-debug-tools="true"
173
- ></char-agent>
174
-
175
- <script>
176
- const agent = document.querySelector('char-agent');
177
- agent.connect({ idToken: 'eyJhbGciOi...' });
37
+ agent?.connect({
38
+ idToken: '<id-token>',
39
+ clientId: '<oidc-client-id>',
40
+ })
178
41
  </script>
179
42
  ```
180
43
 
181
- ## SSR Frameworks (Astro, Next.js, etc.)
182
-
183
- Char requires a browser environment — it uses Shadow DOM, custom elements, and other browser APIs. In SSR frameworks, you must ensure Char only renders on the client.
184
-
185
- ### Astro
186
-
187
- Use `client:only="react"` to skip server-side rendering entirely:
188
-
189
- ```astro
190
- ---
191
- import { Char } from '@mcp-b/char'
192
- ---
193
-
194
- <Char
195
- client:only="react"
196
- devMode={{ anthropicApiKey: import.meta.env.PUBLIC_ANTHROPIC_API_KEY }}
197
- open={true}
198
- />
199
- ```
200
-
201
- `client:load` or `client:visible` will not work because Astro still attempts to render the component on the server first. `client:only="react"` skips SSR entirely and renders only in the browser.
202
-
203
- ### Next.js
44
+ `display-mode` values:
204
45
 
205
- Use dynamic imports with `ssr: false`:
46
+ - `pip`: collapsed
47
+ - `inline`: expanded side panel
48
+ - `fullscreen`: expanded fullscreen
206
49
 
207
- ```tsx
208
- import dynamic from 'next/dynamic'
209
-
210
- const Char = dynamic(() => import('@mcp-b/char').then(m => ({ default: m.Char })), {
211
- ssr: false,
212
- })
213
-
214
- export default function Page() {
215
- return <Char open={true} devMode={{ anthropicApiKey: process.env.NEXT_PUBLIC_ANTHROPIC_API_KEY }} />
216
- }
217
- ```
218
-
219
- ## Props / Attributes
220
-
221
- | Prop | Attribute | Type | Description |
222
- |------|-----------|------|-------------|
223
- | - | - | method | `connect({ idToken })` - Secure authentication (token not in DOM) |
224
- | - | - | method | `connect({ ticketAuth })` - SSR-friendly authentication (pre-fetched ticket) |
225
- | - | - | method | `disconnect()` - Clear authentication |
226
- | `open` | `open` | boolean | Controlled open state (optional) |
227
- | `devMode` | `dev-mode` | object/JSON | Development mode config (optional) |
228
- | `enableDebugTools` | `enable-debug-tools` | boolean | Enable debug tools (default: false) |
229
-
230
- ## Customization
231
-
232
- Customize appearance by setting CSS variables on the host page:
233
-
234
- ```css
235
- char-agent {
236
- /* Brand colors */
237
- --wm-color-primary: #8b5cf6;
238
- --wm-color-primary-foreground: #ffffff;
239
-
240
- /* Layout */
241
- --wm-radius: 12px;
242
- --wm-font-sans: 'Inter', sans-serif;
243
-
244
- /* Backgrounds */
245
- --wm-color-background: #ffffff;
246
- --wm-color-foreground: #1a1a1a;
247
- --wm-color-muted: #f5f5f5;
248
-
249
- /* Messages */
250
- --wm-user-bubble-bg: #8b5cf6;
251
- --wm-user-bubble-text: #ffffff;
252
- --wm-assistant-bubble-bg: #f5f5f5;
253
- --wm-assistant-bubble-text: #1a1a1a;
254
-
255
- /* Composer */
256
- --wm-composer-bg: #ffffff;
257
- --wm-composer-border: #e5e5e5;
258
- --wm-composer-button-bg: #8b5cf6;
259
-
260
- /* Tools */
261
- --wm-tool-bg: #f9fafb;
262
- --wm-tool-border: #e5e7eb;
263
- --wm-tool-approve-bg: #10b981;
264
- --wm-tool-deny-bg: #ef4444;
265
-
266
- /* Code blocks */
267
- --wm-code-bg: #1e1e1e;
268
- --wm-code-text: #d4d4d4;
269
-
270
- /* Font sizes */
271
- --wm-font-size-xs: 0.75rem;
272
- --wm-font-size-sm: 0.875rem;
273
- --wm-font-size-base: 1rem;
274
- --wm-font-size-lg: 1.125rem;
275
- }
276
-
277
- /* Dark mode */
278
- char-agent.dark {
279
- --wm-color-background: #1a1a1a;
280
- --wm-color-foreground: #ffffff;
281
- --wm-color-muted: #2a2a2a;
282
- /* ... other dark mode overrides */
283
- }
284
- ```
50
+ ## Opinionated shell: `<char-agent-shell>`
285
51
 
286
- ## Development Mode
287
-
288
- Use your own API keys during development (stateless):
289
-
290
- ```html
291
- <char-agent
292
- dev-mode='{"anthropicApiKey":"sk-ant-...","openaiApiKey":"sk-...","useLocalApi":true}'
293
- ></char-agent>
294
- ```
295
-
296
- **Development modes:**
297
- - `anthropicApiKey`: Use your own Anthropic API key (falls back to Gemini if not provided)
298
- - `openaiApiKey`: Enable voice mode with your OpenAI key
299
- - `useLocalApi`: Point to localhost instead of production API
300
-
301
- Common combinations:
302
- - `{ useLocalApi: true }` - Internal monorepo development
303
- - `{ anthropicApiKey: "sk-ant-..." }` - External dev with your own key
304
- - `{ anthropicApiKey: "...", openaiApiKey: "...", useLocalApi: true }` - Full stack local dev
305
-
306
- ## Performance
307
-
308
- ### Script Loading
309
-
310
- Always use `defer` or `async` when embedding the standalone script to avoid blocking page rendering:
52
+ Use this when you want built-in host behavior and responsive policy.
311
53
 
312
54
  ```html
313
- <!-- GOOD: defer - loads in parallel, executes after DOM ready -->
314
- <script src="https://unpkg.com/@mcp-b/char/dist/web-component-standalone.iife.js" defer></script>
55
+ <char-agent-shell
56
+ id="assistant-shell"
57
+ panel-width="420"
58
+ pip-position="bottom-center"
59
+ fullscreen-breakpoint="1024"
60
+ ></char-agent-shell>
61
+ <script type="module">
62
+ import '@mcp-b/char/shell-component'
315
63
 
316
- <!-- GOOD: async - loads in parallel, executes ASAP -->
317
- <script src="https://unpkg.com/@mcp-b/char/dist/web-component-standalone.iife.js" async></script>
64
+ /** @type {import('@mcp-b/char/shell-component').CharAgentShellElement | null} */
65
+ const shell = document.querySelector('#assistant-shell')
318
66
 
319
- <!-- BAD: blocks page rendering until script loads -->
320
- <script src="https://unpkg.com/@mcp-b/char/dist/web-component-standalone.iife.js"></script>
67
+ shell?.connect({
68
+ idToken: '<id-token>',
69
+ clientId: '<oidc-client-id>',
70
+ })
71
+ </script>
321
72
  ```
322
73
 
323
- **When to use which:**
324
- - `defer` (recommended) - Script executes after HTML is parsed, preserves execution order
325
- - `async` - Script executes as soon as it loads, good for independent widgets
74
+ Shell mode policy:
326
75
 
327
- ### Preconnect Hint
76
+ - closed -> `pip`
77
+ - open desktop -> `inline`
78
+ - open below breakpoint -> `fullscreen`
328
79
 
329
- Speed up loading by adding a preconnect hint in your `<head>`:
80
+ Availability policy:
330
81
 
331
- ```html
332
- <link rel="preconnect" href="https://unpkg.com" crossorigin>
333
- ```
82
+ - `char-agent-shell` is fail-closed by default.
83
+ - If iframe initialization fails or times out, the shell hides fully (no host chrome footprint).
84
+ - Observability remains available via `char-error`.
334
85
 
335
- ## Features
86
+ ## API summary
336
87
 
337
- - **MCP Tool Support** - Connect to any MCP server
338
- - **Voice Mode** - Talk to your AI assistant
339
- - **Action-First UI** - Shows what the AI is doing
340
- - **Shadow DOM Isolation** - Styles don't leak in or out
341
- - **Stateful Sessions** - Messages persist across page refreshes (when authToken is provided)
342
- - **CSS Variable Theming** - Customize appearance without JavaScript
88
+ `<char-agent>` methods:
343
89
 
344
- ## Migration Guide
90
+ - `connect(options)`
91
+ - `disconnect()`
92
+ - `setAuth(options | null)` (connect when provided, disconnect when null)
93
+ - `setHostContext(partialContext)`
345
94
 
346
- ### Upgrading from Previous Versions
95
+ `<char-agent-shell>` methods:
347
96
 
348
- **Removed props:**
349
- - `apiKey` / `userId` - Replaced by `authToken` (use your existing IDP token)
350
- - `appId` - No longer needed (removed dead code)
351
- - `siteId` - Org-level routing in SSO-first mode (no site scoping)
352
- - `theme` - Use CSS variables instead (see Customization section)
353
- - `isolateStyles` - Always enabled (Shadow DOM is always used)
354
- - `disableShadowDOM` - Removed (Shadow DOM is always enabled)
97
+ - `connect(options)`
98
+ - `disconnect()`
99
+ - `setAuth(options | null)` (connect when provided, disconnect when null)
100
+ - `setHostContext(partialContext)`
101
+ - `setOpen(open)`
102
+ - `toggleOpen()`
355
103
 
356
- **Migration examples:**
104
+ `<char-agent-shell>` events:
357
105
 
358
- ```html
359
- <!-- OLD: -->
360
- <char-agent
361
- app-id="app_123"
362
- site-id="site_123"
363
- api-key="sk_live_xxx"
364
- theme='{"primaryColor":"#ff0000","mode":"dark"}'
365
- isolate-styles="false"
366
- ></char-agent>
367
-
368
- <!-- NEW (SSO-first with connect method): -->
369
- <char-agent></char-agent>
370
- <script>
371
- const agent = document.querySelector('char-agent');
372
- agent.connect({ idToken: 'eyJhbGciOi...' });
373
- </script>
106
+ - `char-shell-open-change` (`{ open: boolean }`)
107
+ - pass-through `char-*` events from inner `<char-agent>`
374
108
 
375
- <!-- NEW (anonymous dev mode): -->
376
- <char-agent dev-mode='{"anthropicApiKey":"sk-ant-..."}'></char-agent>
377
-
378
- <!-- Customize via CSS: -->
379
- <style>
380
- char-agent {
381
- --wm-color-primary: #ff0000;
382
- }
383
- char-agent.dark {
384
- --wm-color-background: #1a1a1a;
385
- }
386
- </style>
387
- ```
388
-
389
- **Benefits:**
390
- - Smaller bundle size (~200 lines removed)
391
- - Simpler API (fewer required attributes)
392
- - More flexible styling (CSS variables work everywhere)
393
- - Consistent Shadow DOM isolation (no edge cases)
394
- - No API keys to manage (uses existing IDP tokens)
109
+ ## Shared mode-policy helpers
395
110
 
396
- ## Development
397
-
398
- ```bash
399
- pnpm --filter @mcp-b/char dev # Watch TS build
400
- pnpm --filter @mcp-b/char dev:css # Watch Tailwind CSS
401
- pnpm --filter @mcp-b/char storybook # http://localhost:6006
402
- pnpm --filter @mcp-b/char build
403
- pnpm --filter @mcp-b/char check:types
404
- pnpm --filter @mcp-b/char test
111
+ ```ts
112
+ import {
113
+ DEFAULT_AVAILABLE_DISPLAY_MODES,
114
+ isDisplayMode,
115
+ isExpandedDisplayMode,
116
+ resolvePolicyDisplayMode,
117
+ resolveSupportedDisplayMode,
118
+ } from '@mcp-b/char/display-mode-policy'
405
119
  ```