diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c95e00b..74d15ab 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,8 +9,8 @@ { "name": "char", "source": "./", - "description": "One-shot setup for Char embedded AI agent with WebMCP browser tools", - "version": "3.0.0" + "description": "One-shot setup for Char publishable-key embeds, shell-first UX, and WebMCP browser tools", + "version": "3.1.0" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 1856498..3534849 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "char", - "version": "3.0.1", - "description": "One-shot setup for Char embedded AI agent with WebMCP browser tools", + "version": "3.1.0", + "description": "One-shot setup for Char publishable-key embeds, shell-first UX, and WebMCP browser tools", "author": { "name": "WebMCP", "email": "alex@mcp-b.ai" diff --git a/README.md b/README.md index 4f5a1fe..c567a56 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ # Char Plugin for Claude Code -One-shot setup for [Char](https://docs.usechar.com) embedded AI agent with WebMCP browser tools. +One-shot setup for [Char](https://docs.usechar.com) embedded AI agents with WebMCP tools. ## What is Char? -Char is an embeddable AI agent that can interact with your web application through WebMCP tools. Users chat with the agent, and it can fill forms, click buttons, navigate pages, and perform any action you expose as a tool. +Char is an embeddable AI agent that can use your WebMCP tools to operate your product UI. + +- Embed UI via `` (preferred) or `` +- Authenticate embeds with a **publishable key** +- Optionally attach per-user identity with `idToken` +- Test tools directly with Chrome DevTools MCP ## Installation @@ -30,17 +35,16 @@ Char is an embeddable AI agent that can interact with your web application throu After installing, run: -``` +```text /char:setup ``` -This will: -1. Connect you to Char (creates account/org automatically via OAuth) -2. Configure allowed domains for your app -3. Install the npm package -4. Add the agent to your HTML/React/Vue app -5. Apply styling to match your theme -6. Verify everything works +The setup flow is optimized for: +1. Publishable-key auth (`publishableKey` required) +2. Script-tag-first embeds (`@latest` CDN) +3. `` default UX (pill + panel + responsive fullscreen) +4. WebMCP tooling + Chrome DevTools MCP verification +5. Framework guidance (Next.js/SSR client-only embedding) ## What's Included @@ -48,72 +52,47 @@ This will: | Server | Purpose | |--------|---------| -| `char-saas` | Organization management, SSO config | +| `char-saas` | Organization management (keys, domains, SSO config) | | `char-docs` | Char documentation search | | `webmcp-docs` | WebMCP API documentation | -| `chrome-devtools` | Browser automation for testing | +| `chrome-devtools` | Browser automation for testing tools and embed behavior | ### Skills -- `/char:setup` - Complete setup wizard (visual integration, live preview, recipes, troubleshooting) -- `/char:webmcp` - WebMCP tool writing patterns and best practices +- `/char:setup` - End-to-end setup using publishable keys, shell-first UI, and test workflow +- `/char:webmcp` - WebMCP tool-writing patterns, implementation, and dogfooding ### Agents -- `integration-specialist` - Autonomous agent that creates comprehensive WebMCP tool coverage for your codebase - -## After Setup - -### Add WebMCP Tools - -Let the agent interact with your UI: +- `integration-specialist` - Autonomous agent to build comprehensive WebMCP tool coverage -```typescript -import '@mcp-b/global'; +## Recommended Embed (Script Tag + Shell) -navigator.modelContext.registerTool({ - name: 'add_to_cart', - description: 'Add a product to the shopping cart', - inputSchema: { - type: 'object', - properties: { - productId: { type: 'string' }, - }, - required: ['productId'], - }, - execute: async ({ productId }) => { - document.querySelector(`[data-product="${productId}"] .add-btn`)?.click(); - return { content: [{ type: 'text', text: 'Added to cart' }] }; - }, -}); +```html + + + ``` -### Configure SSO (Production) +## Next.js / SSR Note -``` -mcp__char-saas__manage_idp_config({ - action: "update", - idp_type: "okta", - idp_domain: "company.okta.com", - idp_client_id: "your-client-id" -}) -``` +`` and `` are browser custom elements and should be rendered in a **client component**. -### Customize Styling - -```css -char-agent { - --char-color-primary: #your-brand-color; - --char-color-background: #ffffff; - --char-color-foreground: #0f172a; -} -``` +- Do not rely on server rendering for the widget runtime +- Use `dynamic(..., { ssr: false })` or a `'use client'` wrapper +- Pass `idToken` client-side with `connect()` / `setAuth()` ## Documentation -- [Char Docs](https://docs.usechar.com) - Full documentation -- [WebMCP Docs](https://docs.mcp-b.ai) - Tool registration API -- [Identity Providers](https://docs.usechar.com/identity-providers) - SSO setup guides +- [Char Docs](https://docs.usechar.com) +- [Embedding Guide](https://docs.usechar.com/guides/embedding-agent) +- [Framework Guides](https://docs.usechar.com/guides/frameworks/index) +- [WebMCP Docs](https://docs.mcp-b.ai) ## Support diff --git a/skills/setup/SKILL.md b/skills/setup/SKILL.md index 327d76a..6230db8 100644 --- a/skills/setup/SKILL.md +++ b/skills/setup/SKILL.md @@ -1,389 +1,222 @@ --- name: char-setup -version: 3.0.0 -description: Set up Char - an AI agent platform with WebMCP browser automation tools and embedded chat widgets. Use when the user wants to add Char to their website, set up WebMCP tools, integrate the embedded agent widget, or add AI chat functionality with browser automation. +version: 3.1.0 +description: Set up Char embeds with the current publishable-key auth model, shell-first UX, WebMCP tools, and Chrome DevTools MCP testing. Use when integrating Char into a website/app, configuring the pill/panel experience, setting up WebMCP tools, or handling Next.js/SSR-safe embedding. allowed-tools: Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion, mcp__chrome-devtools__*, mcp__char-docs__*, mcp__webmcp-Docs__*, mcp__char-saas-staging__* --- # Char Setup Assistant -Guides you through integrating Char AI agents into websites with WebMCP browser automation tools. +Integrate Char using the current runtime contract and preferred deployment path. -## Quick Reference - -| Task | Command/Action | Where | -|------|---------------|-------| -| **Install Chrome DevTools MCP** | `claude mcp add --transport stdio chrome-devtools -- npx -y @mcp-b/chrome-devtools-mcp` | Terminal | -| **Create demo page** | Ask: "Create a Char demo page" | This skill auto-creates | -| **Add to existing page** | Ask: "Add Char to my index.html" | This skill adds widget | -| **Match page design** | Ask: "Set up Char matching my page's style" | Auto-maps CSS variables | -| **Live preview on any site** | Ask: "Preview Char on https://example.com" | CDP injection | -| **Verify setup** | Look for `✅ Char embedded agent initialized!` | Browser console | -| **Customize styling** | See [CUSTOMIZATION.md](references/CUSTOMIZATION.md) | Reference docs | -| **Write WebMCP tools** | Use the `/char:webmcp` skill | Separate skill | -| **Troubleshoot** | See [TROUBLESHOOTING.md](references/TROUBLESHOOTING.md) | Reference docs | - -## Success Criteria - -After setup, you should see: - -✅ **Page loads successfully** -- HTML file opens in browser without errors - -✅ **Widget appears** -- `` renders and the chat UI opens - -✅ **Console confirms initialization** -``` -✅ Char embedded agent initialized! -💡 Try saying: "Fill out the contact form with test data" -``` +## Current Contract (Use This) -✅ **Agent responds** -- Click the pill, type a message, get a response +- `publishableKey` is required +- `idToken` is optional (per-user identity) +- Prefer `` for most integrations +- Prefer script-tag CDN (`@latest`) for fastest, always-current setup +- Treat the embedded UI as iframe-owned -✅ **WebMCP tools work** -- Agent can interact with page elements (click, fill forms) +Do not use legacy `dev-mode`, `auth-token`, `clientId`, `organizationId`, or `ticketAuth` patterns. -If any check fails, see [Troubleshooting](references/TROUBLESHOOTING.md). - -## What is Char? - -**Char** is an AI agent platform for embedding conversational agents on websites with: -- **WebMCP tools** - Browser automation (click, fill forms, navigate, screenshot) -- **Embedded agent widget** - Drop-in chat UI powered by `@mcp-b/char` -- **Auth-token mode** - Production embed using your auth token -- **Dev mode** - Quick start using your own Anthropic API key (no backend needed) - -**Note:** Use the `` web component for embedding. - -## Prerequisites - -### Required -- This skill (you're already using it!) -- Anthropic API key (for devMode) -- Chrome, Edge, or Chromium 90+ -- Node.js 14+ (for verification script) - -### Verify Prerequisites - -Run the verification script to check your setup: - -```bash -node scripts/verify-setup.js - -# Optional: Check API key format -node scripts/verify-setup.js --check-api-key sk-ant-... -``` - -Expected output: -``` -✓ Node.js 24.11.1 (>= 14.0.0) -✓ Google Chrome 120 found (>= 90) -✓ Demo template found -✓ Network connectivity to api.anthropic.com -✓ All required checks passed! -``` - -### Optional MCP Servers (Recommended) - -**Chrome DevTools MCP** - Enables automated browser testing and visual integration: -```bash -claude mcp add --transport stdio chrome-devtools -- \ - npx -y @mcp-b/chrome-devtools-mcp -``` -Verify installation: `/mcp` and look for `chrome-devtools` - -**MCPB Documentation (WebMCP Server)** - Source of truth for WebMCP API names/signatures (do not guess): -```bash -# Check if installed: -/mcp -# Look for a WebMCP Server / MCPB documentation tool -``` - -## Quick Start - -The fastest path is **dev mode** - works immediately with your Anthropic API key. -For production, use **auth-token** (see [references/PRODUCTION.md](references/PRODUCTION.md)). +## Quick Reference -1. I'll check which MCP servers are available -2. Create an HTML page with Char embedded agent -3. Inject your API key -4. Verify setup (automated if Chrome DevTools MCP available, manual otherwise) +| Task | Recommended path | +|------|------------------| +| Fastest embed | Script tag + `` | +| Advanced host control | `` directly | +| Per-user identity | `connect({ publishableKey, idToken })` or `setAuth({ publishableKey, idToken })` | +| Tool development | Register tools with `@mcp-b/global`, then test via Chrome DevTools MCP | +| Next.js | Client component only (`'use client'` + `dynamic(..., { ssr: false })`) | +| Pill/panel behavior | Use shell defaults, then tune `panel-width` and theme vars | ## Setup Workflow -**CRITICAL - Check MCP Availability First:** -Before starting, I'll check which MCP servers are available by examining available tools. This determines the testing approach. If the MCPB documentation (WebMCP Server) is available, use it to verify API names/signatures before writing tool registration code. - -### Step 1: Auto-detect or Create Demo Page +### Step 1: Validate Local Tooling -**If you have an existing HTML page:** -- Tell me the path and I'll add Char to it +Required: +- Chrome/Edge/Chromium +- A publishable key from Char dashboard -**If starting fresh:** -- I'll create a minimal demo page from [assets/templates/demo.html](assets/templates/demo.html) -- The template includes a contact form and interactive counter for testing +Recommended: +- Chrome DevTools MCP +- WebMCP Docs MCP +- Char SaaS MCP -### Step 2: Add Char Embedded Agent Widget - -**First, install the package:** +Install Chrome DevTools MCP if missing: ```bash -npm install @mcp-b/char @mcp-b/global +claude mcp add --transport stdio chrome-devtools -- npx -y @mcp-b/chrome-devtools-mcp@latest ``` -**Then add the widget to your page.** - -**Recommended: Collapsible sidebar** (pushes content left when opened): +### Step 2: Prefer Script Tag + Shell -```html - -
- -
- -
- - -
-
- -
-
-
-``` - -**Alternative: Fixed overlay** (for simple pages without flex layout): +Use this as the default integration unless the user has a strong reason not to. ```html - - - + + + ``` -For stateless devMode (testing only): +Why this path: +- Always up to date (`@latest`) +- No bundler wiring needed +- Includes the built-in pill/panel/fullscreen shell behavior -```html - -``` +### Step 3: Add Per-User Identity (Optional) -For bundlers (Vite/Next/etc.), import the web component in your entry file: +If the host app has signed-in users: ```ts -import '@mcp-b/char/web-component'; +shell.setAuth({ + publishableKey: "pk_live_...", + idToken, +}); ``` -**Shadow DOM note:** The widget always renders inside Shadow DOM. WebMCP tools cannot target elements inside the widget. Treat it as a black box and customize via CSS variables on ``. +Never place `idToken` in HTML attributes. -### Step 2.5: Match Host Page Styling (CRITICAL) +### Step 4: Configure WebMCP Tools -**The widget MUST visually blend with the host page!** +Install polyfill once in app entry: -Extract the host page's design tokens and apply them as CSS variables: - -```typescript -// Add this code after the widget is loaded (in your main.ts/main.js) -const bodyStyles = getComputedStyle(document.body); -const linkElement = document.querySelector('a'); -const linkColor = linkElement ? getComputedStyle(linkElement).color : '#646cff'; - -const agent = document.querySelector('char-agent') as HTMLElement; -if (agent) { - agent.style.setProperty('--char-color-background', bodyStyles.backgroundColor); - agent.style.setProperty('--char-color-foreground', bodyStyles.color); - agent.style.setProperty('--char-color-primary', linkColor); - agent.style.setProperty('--char-color-muted', bodyStyles.backgroundColor); -} +```bash +npm install @mcp-b/global ``` -**Or apply directly in HTML:** - -```html - - +```ts +import "@mcp-b/global"; ``` -**Available CSS variables:** -- `--char-color-background` - Main background color -- `--char-color-foreground` - Main text color -- `--char-color-primary` - Brand/accent color (buttons, links) -- `--char-color-muted` - Secondary background color -- `--char-color-border` - Border colors -- Many more - see [CUSTOMIZATION.md](references/CUSTOMIZATION.md) - -**If you skip this step, the widget will look jarring and out of place!** - -### Step 3: Verify WebMCP Tools - -**Path A: With Chrome DevTools MCP (Automated)** - -If Chrome DevTools MCP is available, I'll run automated tests to verify: -- ✅ Page loads correctly -- ✅ Embedded agent initializes -- ✅ Form elements are clickable -- ✅ WebMCP tools are functional - -**Path B: Without Chrome DevTools MCP (Manual)** - -If Chrome DevTools MCP is not available, I'll provide manual testing instructions: - -1. Open `demo.html` in Chrome/Edge -2. Open DevTools (F12) → Console tab -3. Look for: `✅ Char embedded agent initialized!` -4. Click the chat widget (bottom-right) -5. Try: "Fill out the contact form with test data" -6. Verify the agent can interact with the page +Register app tools through `navigator.modelContext.registerTool(...)`. +Keep tools scoped to app capabilities (forms, navigation, reads, actions). -**Recommendation:** Install Chrome DevTools MCP for automated testing in future setups. +### Step 5: Dogfood with Chrome DevTools MCP -### Step 4: Launch in Browser +Use Chrome DevTools MCP to verify tool registration and runtime behavior: -I'll use the `open` command (macOS) or `start` (Windows) to launch your page in the default browser. - -If you have a local dev server running, I'll navigate to that URL instead. - -## Visual Integration (Chrome DevTools MCP) - -When Chrome DevTools MCP is available, I can automatically analyze your page's design and generate matching CSS variable overrides for Char. - -**What I'll do:** -1. **Extract page styles** - Colors, fonts, border radius, and design patterns -2. **Generate matching styles** - Map your styles to Char's CSS variables -3. **Take screenshots** - Before/after verification at desktop and mobile viewports -4. **Check accessibility** - Verify contrast ratios meet WCAG AA standards - -**Example output:** +```text +list_webmcp_tools summary=true +call_webmcp_tool name="your_tool" arguments={} +call_webmcp_tool name="your_tool" arguments={"field":"value"} ``` -🎨 Extracted: Primary #667eea, Background #fff, Font: system-ui -⚙️ Generated CSS variables with matching colors and typography -📸 Screenshots: collapsed, expanded, mobile viewports -✅ Visual integration complete! -``` - -See [Visual Integration Guide](references/VISUAL_INTEGRATION.md) for the complete workflow, best practices, and detailed examples. -## Live Preview (Chrome DevTools MCP) +For shell/widget checks: +- Confirm pill appears when closed +- Confirm panel opens/closes cleanly +- Confirm mobile/narrow viewport becomes fullscreen +- Confirm page content is not obscured unexpectedly -Preview what Char looks like on **any live website** — without touching their codebase. Uses CDP to extract design tokens, inject the Char bundle, and build a themed collapsible sidebar. +If needed, add debug tools on the element: -**What I'll do:** -1. **Navigate** to the target URL and screenshot -2. **Extract** CSS custom properties (light + dark mode) -3. **Fetch** the Char IIFE bundle from jsdelivr CDN -4. **Inject** themed sidebar as a flex sibling (pushes content left) -5. **Screenshot** the result in both themes - -**Example:** -``` -"Preview what Char looks like on https://app.example.com/dashboard" +```html + ``` -**Result:** A fully themed Char sidebar on their live site — realistic preview of the final integration. - -See [Live Preview Guide](references/LIVE_PREVIEW.md) for the complete step-by-step workflow and gotchas. - -## WebMCP Tools Reference +## Pill and Panel Positioning (Shell) -Once set up, your embedded agent can use these tools: +`` is the preferred host UX and owns: +- Collapsed pip/pill behavior +- Desktop inline panel behavior +- Mobile fullscreen behavior +- Resize + edge-tab interactions -| Tool | What It Does | -|------|--------------| -| `click` | Click buttons, links, elements | -| `fill` | Fill form inputs | -| `navigate` | Navigate to URLs | -| `take_snapshot` | Capture page text content | -| `take_screenshot` | Capture visual screenshot | -| `evaluate_script` | Run JavaScript on page | -| `hover` | Hover over elements | -| `press_key` | Keyboard input | +Recommended tuning knobs: +- `panel-width="420"` for desktop width baseline +- CSS variables (`--char-color-*`) on `char-agent-shell` or ancestor +- host page layout that leaves room for right-side panel when open -See [references/WEBMCP_REFERENCE.md](references/WEBMCP_REFERENCE.md) for complete tool documentation. +Use `` directly only if the host app already has a custom shell/layout system. -## Registering Custom WebMCP Tools +## Next.js and SSR-Safe Embedding -To create page-specific tools that your embedded agent can use, run `/char:webmcp` — the WebMCP tool-writing skill covers everything: -- Registration patterns (React, Vanilla JS) -- Read/fill and fill/submit form tools -- Navigation, read-only, and action tools -- Testing with Chrome DevTools MCP +The widget runtime is browser-only. Use client rendering for custom elements. -## Examples +### Recommended pattern -**Example 1: Add to existing page** -> "Add Char to my index.html file" +1. Create a client component for Char. +2. Import `@mcp-b/char/shell-component` in that component. +3. Render with `dynamic(..., { ssr: false })` from server layouts/pages. +4. Call `setAuth()` on mount with `publishableKey` (+ optional `idToken`). -**Example 2: Create demo from scratch** -> "Create a Char demo page with a contact form" +Example: -**Example 3: Visual integration** -> "Set up Char and make sure it matches my page's design" +```tsx +// app/components/char-shell-client.tsx +"use client"; -**Example 4: Live preview on a customer's site** -> "Preview what Char looks like on https://app.example.com/dashboard" +import { useEffect, useRef } from "react"; +import "@mcp-b/char/shell-component"; +import type { CharAgentShellElement } from "@mcp-b/char/shell-component"; -## Important Notes +export function CharShellClient({ idToken }: { idToken?: string }) { + const ref = useRef(null); -- **API Key Security**: Never commit your Anthropic API key to git -- **Dev Mode Only**: Stateless mode is for development/testing -- **Production**: Use `auth-token` (see [references/PRODUCTION.md](references/PRODUCTION.md)) -- **Browser Required**: You need Chrome/Edge for WebMCP tools to work -- **MCP Servers**: Optional but recommended for automated testing and visual integration + useEffect(() => { + ref.current?.setAuth({ publishableKey: "pk_live_...", idToken }); + }, [idToken]); -## Troubleshooting + return ; +} +``` -See [references/TROUBLESHOOTING.md](references/TROUBLESHOOTING.md) for common issues. +```tsx +// app/layout.tsx (server component) +import dynamic from "next/dynamic"; +import type { ReactNode } from "react"; + +const CharShellClient = dynamic(() => import("./components/char-shell-client").then(m => m.CharShellClient), { + ssr: false, +}); + +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + + {children} + + + + ); +} +``` -## Additional Resources +### SSR-friendly approach (recommended) -### Getting Started -- [RECIPES.md](references/RECIPES.md) - Common setup patterns (quick copy-paste examples) -- [Quick Reference](#quick-reference) - Command cheat sheet (this page) +Server render your app normally, then hydrate Char on the client only. +This avoids hydration mismatches and still keeps the page SSR-fast. -### Configuration -- [CUSTOMIZATION.md](references/CUSTOMIZATION.md) - Complete theming and styling guide -- [VISUAL_INTEGRATION.md](references/VISUAL_INTEGRATION.md) - Design integration best practices -- [LIVE_PREVIEW.md](references/LIVE_PREVIEW.md) - Live preview via CDP injection (sales demos, onboarding) +## Iframe Clarification -### Implementation -- `/char:webmcp` skill - How to write custom WebMCP tools (patterns, testing, best practices) -- [TESTING_GUIDE.md](references/TESTING_GUIDE.md) - Testing procedures with Chrome DevTools MCP +`` and `` both host iframe-owned UI. +Customization is done via element attributes, JS API, and CSS variables. +Do not attempt to query widget internals from WebMCP tools. -### Reference -- [WEBMCP_REFERENCE.md](references/WEBMCP_REFERENCE.md) - Complete tool documentation -- [TROUBLESHOOTING.md](references/TROUBLESHOOTING.md) - Common issues and solutions +## Success Criteria -### Examples -- [EXAMPLE_INTEGRATION.md](references/EXAMPLE_INTEGRATION.md) - Complete Cypress RealWorld App integration +- Embed loads with no console errors +- Agent can send/receive messages +- Pill/panel behavior works as expected +- WebMCP tools are discoverable and callable +- Next.js integrations run client-only without SSR/hydration issues -### Advanced -- [PRODUCTION.md](references/PRODUCTION.md) - Production deployment with stateful backend +## References -### Development -- [CHANGELOG.md](CHANGELOG.md) - Version history -- [CONTRIBUTING.md](CONTRIBUTING.md) - How to improve this skill +- [RECIPES.md](references/RECIPES.md) — copy/paste integration patterns +- [PRODUCTION.md](references/PRODUCTION.md) — publishable-key rollout + security checklist +- [CUSTOMIZATION.md](references/CUSTOMIZATION.md) — theming and shell styling +- [TESTING_GUIDE.md](references/TESTING_GUIDE.md) — tool and UI validation flow +- [LIVE_PREVIEW.md](references/LIVE_PREVIEW.md) — CDP preview on live sites ---- +## Usage Examples -**Next Steps After Setup:** -1. Chat with your embedded agent -2. Ask it to interact with your page -3. Watch WebMCP tools in action -4. Customize the widget styling -5. Move to production when ready +- "Set up Char with publishable key on my `index.html` and use shell mode" +- "Add Char to my Next.js app in a client-only component" +- "Configure WebMCP tools and test them with Chrome DevTools MCP" +- "Make the Char pill and panel match our design tokens" diff --git a/skills/setup/assets/templates/demo.html b/skills/setup/assets/templates/demo.html index bac3577..def447f 100644 --- a/skills/setup/assets/templates/demo.html +++ b/skills/setup/assets/templates/demo.html @@ -283,27 +283,26 @@

Interactive Counter

console.log('🎨 Design tokens exposed:', window.DESIGN_TOKENS); - - - - + + + + + window.addEventListener('DOMContentLoaded', () => { + const shell = document.getElementById('char-shell'); + shell?.setAuth({ publishableKey: 'REPLACE_WITH_PUBLISHABLE_KEY' }); - - + console.log('✅ Char embedded agent shell initialized!'); + console.log('💡 Try saying: \"Fill out the contact form with test data\"'); + }); + diff --git a/skills/setup/references/CUSTOMIZATION.md b/skills/setup/references/CUSTOMIZATION.md index 60e15e4..dfb79f0 100644 --- a/skills/setup/references/CUSTOMIZATION.md +++ b/skills/setup/references/CUSTOMIZATION.md @@ -1,55 +1,31 @@ -# Customizing the Embedded Agent Widget +# Customization Guide (Shell + Agent) -Guide to styling and configuring the Char embedded agent. +Style Char via CSS variables on `` or ``. -## Web Component Only - -Char is embedded via the `` web component. - -## Basic Embed +## Preferred Embed Base ```html - - -``` - -### Dev Mode (Testing Only) - -```html - -``` - -## Web Component Attributes - -| Attribute | Type | Description | -| --- | --- | --- | -| `auth-token` | string | Auth token for production usage | -| `dev-mode` | JSON | Dev mode config (stateless testing only) | -| `open` | boolean | Controlled open state | -| `enable-debug-tools` | boolean | Enable debug tools (prefixed with `debug_`) | - -### dev-mode JSON - -```json -{ - "anthropicApiKey": "sk-ant-...", - "openaiApiKey": "sk-...", - "useLocalApi": true -} + + + ``` ## Styling with CSS Variables -Customize the widget by setting CSS variables on the `` element: - ```html ``` @@ -68,78 +44,54 @@ Customize the widget by setting CSS variables on the `` element: --char-font-sans ``` -### Semantic Variables +## Shell Positioning and Size -```css ---char-user-bubble-bg ---char-user-bubble-text ---char-assistant-bubble-bg ---char-assistant-bubble-text ---char-composer-bg ---char-composer-border ---char-composer-text ---char-composer-placeholder ---char-composer-button-bg ---char-composer-button-text ---char-tool-bg ---char-tool-border ---char-tool-text ---char-tool-header-bg ---char-tool-approve-bg ---char-tool-approve-text ---char-tool-deny-bg ---char-tool-deny-text ---char-code-bg ---char-code-text +```html + ``` -## Dark Mode +Notes: +- Shell manages pill/panel/fullscreen behavior. +- Desktop open mode is right-side inline panel. +- Mobile open mode is fullscreen. -Use `prefers-color-scheme` to swap variables: +## Dark Mode -```html - +} ``` -## Shadow DOM Limitations +## Runtime Auth and Identity -Shadow DOM is always enabled. +Use JS APIs for auth values: -- The widget renders inside Shadow DOM (no opt-out). -- WebMCP tools cannot select or interact with elements inside the widget. -- Use CSS variables on `` for styling. - -## Development vs Production - -**Dev/Test (stateless):** -- Use `dev-mode` with your Anthropic API key -- No persistence +```ts +shell.setAuth({ publishableKey: "pk_live_...", idToken }); +``` -**Production (auth-token):** -- Use `auth-token` for production embeds -- Persistent threads and usage tracking +Do not pass user tokens in DOM attributes. -## Security Best Practices +## Iframe Boundary -- Never ship provider API keys (Anthropic/OpenAI) to production clients. -- Use `auth-token` for production embeds. -- Keep `dev-mode` only for local testing. +The visible assistant UI is iframe-owned. WebMCP tools cannot target internal widget DOM. +Customize with: +- CSS variables +- element attributes +- JS API (`setAuth`, `setHostContext`, `setOpen`, `toggleOpen`) ## Next Steps -- See [VISUAL_INTEGRATION.md](./VISUAL_INTEGRATION.md) for design matching -- See [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) for common issues +- See [VISUAL_INTEGRATION.md](./VISUAL_INTEGRATION.md) +- See [PRODUCTION.md](./PRODUCTION.md) +- See [TESTING_GUIDE.md](./TESTING_GUIDE.md) diff --git a/skills/setup/references/LIVE_PREVIEW.md b/skills/setup/references/LIVE_PREVIEW.md index 775991f..366da93 100644 --- a/skills/setup/references/LIVE_PREVIEW.md +++ b/skills/setup/references/LIVE_PREVIEW.md @@ -1,344 +1,111 @@ -# Live Preview via CDP Injection +# Live Preview via Chrome DevTools MCP -Preview what Char looks like on any live website — without touching their codebase. Uses Chrome DevTools MCP to extract styles, inject the Char IIFE bundle, and build a themed collapsible sidebar. +Preview Char on a live website without editing that site's source code. -**Requires:** Chrome DevTools MCP server +## Goal ---- +- Extract host design tokens +- Inject Char shell script (`@latest`) +- Mount `` with publishable key +- Verify pill/panel placement and styling -## When to Use +## Requirements -- **Sales demos**: "Here's what Char looks like on your site" -- **Customer onboarding**: Extract exact design tokens before writing integration code -- **Design verification**: Test light/dark mode theming on the real page -- **Quick prototyping**: See the sidebar layout before committing to code changes +- Chrome DevTools MCP available +- A valid `publishableKey` (`pk_live_...` or `pk_test_...`) ---- +## Workflow -## Step-by-Step Workflow +### 1) Navigate + baseline screenshot -### Step 1: Navigate and Screenshot - -``` -mcp__chrome-devtools__new_page({ url: "https://example.com/dashboard" }) -mcp__chrome-devtools__take_screenshot() +```text +new_page url="https://example.com" +take_screenshot ``` -Orient yourself — understand the layout before injecting anything. - -### Step 2: Extract CSS Custom Properties - -Run via `evaluate_script` to pull design tokens from `:root`: - -```js -() => { - const root = getComputedStyle(document.documentElement); - const vars = [ - '--primary', '--primary-foreground', - '--background', '--foreground', - '--card', '--card-foreground', - '--muted', '--muted-foreground', - '--border', '--input', '--ring', - '--sidebar', '--sidebar-foreground', - '--accent', '--accent-foreground', - '--destructive', '--radius', - ]; - const result = {}; - vars.forEach(v => { result[v] = root.getPropertyValue(v).trim() }); - return result; -} -``` - -**For dark mode:** Toggle the site's dark mode (find and click their toggle button), then run the same script again. - -**Tip:** If their variables use OKLCH, you can map them directly to `--char-*` variables — no color space conversion needed. +### 2) Extract host tokens -### Step 3: Extract Fonts +Use `evaluate_script` to capture colors/fonts from `:root` and `body`. +Map key values to `--char-color-*` and `--char-font-sans`. -```js -() => { - const cs = getComputedStyle(document.body); - return { - fontFamily: cs.fontFamily, - monoFont: getComputedStyle( - document.querySelector('code') || document.body - ).fontFamily, - }; -} -``` - -### Step 4: Map the DOM Structure +### 3) Inject shell bundle -Understand the layout before injecting — find the outermost flex container: - -```js -() => { - function describeEl(el, depth = 0) { - if (depth > 3) return null; - const cs = getComputedStyle(el); - return { - tag: el.tagName, - classes: el.className?.substring?.(0, 80) || '', - display: cs.display, - position: cs.position, - width: cs.width, - flexDirection: cs.flexDirection, - childCount: el.children.length, - children: depth < 2 - ? Array.from(el.children).slice(0, 6).map(c => describeEl(c, depth + 1)) - : undefined, - }; - } - return describeEl(document.getElementById('root') || document.body.children[0]); -} -``` - -**What you're looking for:** The outermost `display: flex` container. The Char sidebar will become a flex sibling of their existing content. - -### Step 5: Check CSP - -The Content Security Policy determines how you can load the bundle: - -```js -async () => { - const r = await fetch('/'); - return r.headers.get('content-security-policy'); -} -``` - -Key directives to check: -- `script-src` — does it allow `unsafe-eval`? (needed for the `eval()` approach) -- `connect-src` — does it allow `https:`? (needed to fetch the bundle) - -### Step 6: Fetch the Char IIFE Bundle - -**Use jsdelivr** (not unpkg — unpkg has CORS issues on `@latest` redirects): +Use `evaluate_script`: ```js async () => { - const r = await fetch( - 'https://cdn.jsdelivr.net/npm/@mcp-b/char@latest/dist/web-component-standalone.iife.min.js' - ); - const text = await r.text(); - window.__charCode = text; - return { ok: true, size: text.length }; -} -``` - -Then eval it in a **separate** `evaluate_script` call (splitting avoids timeout on the ~2MB bundle): - -```js -() => { - eval(window.__charCode); - return { registered: !!customElements.get('char-agent') }; + const src = "https://cdn.jsdelivr.net/npm/@mcp-b/char@latest/dist/shell-standalone.iife.js"; + await new Promise((resolve, reject) => { + const s = document.createElement("script"); + s.src = src; + s.defer = true; + s.onload = () => resolve(); + s.onerror = reject; + document.head.appendChild(s); + }); + return { registered: !!customElements.get("char-agent-shell") }; } ``` -### Step 7: Inject CSS Variables - -Map the extracted design tokens to `--char-*` variables: +### 4) Inject shell element and theme vars ```js () => { - const style = document.createElement('style'); + const style = document.createElement("style"); style.textContent = ` - :root { - --char-color-primary: /* extracted --primary */; - --char-color-background: /* extracted --card or --background */; - --char-color-foreground: /* extracted --foreground */; - --char-color-primary-foreground: /* extracted --primary-foreground */; - --char-color-muted: /* extracted --muted */; - --char-color-muted-foreground: /* extracted --muted-foreground */; - --char-color-border: /* extracted --border */; - --char-color-input: /* extracted --input */; - --char-color-ring: /* extracted --ring or --primary */; - --char-color-error: /* extracted --destructive */; - --char-radius: /* extracted --radius */; - --char-font-sans: /* extracted fontFamily */; - --char-font-mono: /* extracted monoFont */; - } - .dark { - /* dark mode overrides — same variables with dark values */ + char-agent-shell { + --char-color-primary: #2563eb; + --char-color-background: #ffffff; + --char-color-foreground: #0f172a; + --char-color-border: #e2e8f0; } `; document.head.appendChild(style); - return 'css injected'; -} -``` - -### Step 8: Build the Collapsible Sidebar DOM - -The sidebar is a **flex sibling** of the app's main content — not a fixed overlay. This pushes content left when opened. - -```js -() => { - // Find the app shell (outermost flex container) - const appShell = document.querySelector('.flex.h-screen.w-full') - || document.querySelector('[class*="flex"]'); - appShell.style.overflow = 'hidden'; - - // Create the collapsible panel - const panel = document.createElement('div'); - panel.id = 'char-panel'; - panel.style.cssText = ` - flex-shrink: 0; - overflow: hidden; - border-left: 1px solid transparent; - transition: width 0.2s ease-out; - width: 0; - background: var(--background); - `; - - // Inner container stays at 420px (prevents re-layout during animation) - const inner = document.createElement('div'); - inner.style.cssText = 'width: 420px; height: 100%;'; - - // Create the web component - const agent = document.createElement('char-agent'); - agent.style.cssText = 'display: block; width: 100%; height: 100%;'; - - inner.appendChild(agent); - panel.appendChild(inner); - appShell.appendChild(panel); - - // Create toggle button - const btn = document.createElement('button'); - btn.id = 'char-toggle'; - btn.style.cssText = ` - position: fixed; right: 16px; bottom: 16px; z-index: 40; - width: 48px; height: 48px; border-radius: 50%; - display: flex; align-items: center; justify-content: center; - background: var(--primary); color: var(--primary-foreground); - border: none; cursor: pointer; - box-shadow: 0 4px 12px rgba(0,0,0,0.15); - `; - btn.innerHTML = ''; - document.body.appendChild(btn); - - // Wire up toggle - btn.addEventListener('click', () => { - panel.style.width = '420px'; - panel.style.borderColor = 'var(--border)'; - btn.style.display = 'none'; - }); - document.addEventListener('char-close', () => { - panel.style.width = '0'; - panel.style.borderColor = 'transparent'; - btn.style.display = 'flex'; - }); - - return 'sidebar injected'; -} -``` - -**Why flex sibling, not fixed overlay:** -- Content pushes left — no z-index fights, no hidden content -- Matches the standard Char integration pattern (collapsible sidebar) -- Gives a realistic preview of what the final integration looks like - -### Step 9: Activate the Widget -For dev mode (requires Anthropic API key): + let shell = document.querySelector("char-agent-shell"); + if (!shell) { + shell = document.createElement("char-agent-shell"); + shell.setAttribute("panel-width", "420"); + document.body.appendChild(shell); + } -```js -() => { - const agent = document.querySelector('char-agent'); - agent.setAttribute('dev-mode', JSON.stringify({ - anthropicApiKey: 'sk-ant-...' - })); - agent.setAttribute('open', 'true'); - return 'dev-mode set'; + return { mounted: true }; } ``` -For production auth (if you have an IDP token): +### 5) Connect auth ```js () => { - const agent = document.querySelector('char-agent'); - agent.connect({ idToken: '...', clientId: '...' }); - return 'connected'; + const shell = document.querySelector("char-agent-shell"); + shell?.setAuth({ publishableKey: "pk_live_..." }); + return { connected: !!shell }; } ``` -### Step 10: Open and Screenshot +### 6) Verify behavior -```js -() => { document.getElementById('char-toggle').click(); return 'opened'; } -``` +- pill appears while closed +- panel opens on interaction +- resize to mobile and verify fullscreen mode -``` -mcp__chrome-devtools__take_screenshot() +```text +take_screenshot +resize_page width=390 height=844 +take_screenshot ``` -Toggle dark mode and screenshot again to verify both themes. +## Notes ---- - -## Collapsible Sidebar Pattern - -The recommended integration pattern docks Char as a right sidebar that pushes content: - -``` -div.flex.h-screen.w-full.overflow-hidden (app shell) -├── div.flex.min-w-0.flex-1 (existing content) -│ ├── sidebar -│ ├── main content -│ └── etc. -├── div#char-panel.shrink-0.overflow-hidden (agent panel — 420px / 0px) -│ └── div (inner 420px) -│ └── char-agent -└── button#char-toggle.fixed (toggle — only when closed) -``` +- Prefer shell for realistic preview (pill + panel behavior). +- Char UI is iframe-owned. You theme it via CSS variables, not internal DOM selectors. +- If CSP blocks third-party script injection, capture design tokens and provide integration instructions instead of live injection. -**Key rules:** -1. **Flex sibling** — the agent panel must be a direct sibling of the main content, not nested inside it -2. **`shrink-0`** — prevents the panel from being compressed by flex -3. **`overflow-hidden`** on the app shell — prevents horizontal scrollbar during the width transition -4. **Inner div at 420px** — prevents re-layout of the Char UI during the collapse animation -5. **Width transition** — animate between `width: 420px` (open) and `width: 0` (closed) - ---- - -## Gotchas +## Common Issues | Issue | Cause | Fix | -|-------|-------|-----| -| `Failed to fetch` from unpkg.com | CORS headers stripped on `@latest` redirect | Use jsdelivr instead | -| Script timeout on large eval | ~2MB IIFE in one `evaluate_script` call | Split into fetch + eval in two separate calls | -| Widget stuck on "Authenticating" | No auth token or API key provided | Set `dev-mode` attribute with API key, or call `connect()` with idToken | -| Widget stuck on "Loading thread" | Anonymous WebSocket 404 | Anonymous devMode is localhost-only; production needs real SSO auth | -| `connect({ anthropicApiKey })` doesn't work | `connect()` is for SSO only (idToken/ticketAuth) | Use `setAttribute('dev-mode', JSON.stringify({ anthropicApiKey: '...' }))` instead | -| Panel visible but empty | Shadow DOM renders on a child `
`, not the `` element itself | Check `agent.children[0].shadowRoot` not `agent.shadowRoot` | -| Flex layout breaks when Char is inside content | Char's `:host` sets `width: 100%; height: 100%` | Put it in its own flex sibling with `shrink-0`, never inside the content area | -| CDP click timeout on toggle button | `Locator.click` timed out | Use `evaluate_script` to call `btn.click()` directly | - ---- - -## Summary Checklist - -1. Screenshot the page -2. Extract CSS vars (light + dark) -3. Extract fonts -4. Map the DOM tree to find the flex shell -5. Check CSP -6. Fetch bundle from jsdelivr → `window.__charCode` -7. `eval(window.__charCode)` -8. Inject `--char-*` CSS variables -9. Build sidebar DOM (panel + toggle button) -10. Set `dev-mode` attribute or call `connect()` -11. Click toggle, screenshot, verify both themes - ---- - -## Generating the Setup Document - -After a successful live preview, generate a customer-facing setup document that includes: - -1. **npm install command** — `npm install @mcp-b/char` -2. **React component code** — collapsible sidebar pattern with `` component -3. **CSS variables** — the exact `--char-*` values extracted from their site (light + dark) -4. **Layout diagram** — their specific DOM structure with the agent panel placement -5. **Color mapping table** — which of their CSS vars maps to which Char var - -This document gives the customer everything they need to integrate Char into their codebase. +|------|-------|-----| +| Shell never appears | Invalid/missing key or fail-closed availability | Use valid publishable key and check console/network logs | +| `ORIGIN_NOT_ALLOWED` | Origin not allowlisted for key | Add target domain to key allowlist | +| Script blocked | CSP disallows CDN script | Use a local/dev sandbox or provide non-injected integration handoff | +| Styles look off | Missing token mapping | Set `--char-color-*` vars based on extracted host values | diff --git a/skills/setup/references/PRODUCTION.md b/skills/setup/references/PRODUCTION.md index 5dd23ea..ac84cb9 100644 --- a/skills/setup/references/PRODUCTION.md +++ b/skills/setup/references/PRODUCTION.md @@ -1,181 +1,80 @@ -# Production Setup Guide +# Production Setup Guide (Publishable Key) -Move from stateless devMode to production using `auth-token`. +Move from setup to production using the current embed contract. ---- +## Auth Contract -## Dev/Test vs Production +- `publishableKey`: required +- `idToken`: optional +- Ticket exchange is handled by the widget runtime -### Dev Mode (Stateless) +Do not use legacy `dev-mode` or `auth-token` patterns. -```html - -``` - -**Characteristics:** -- ✅ Quick setup -- ✅ No backend setup required -- ✅ Full WebMCP tools -- ❌ No thread persistence -- ❌ No user tracking -- ❌ Not suitable for production - -### Production Mode (auth-token) - -```html - -``` - -**Characteristics:** -- ✅ Persistent threads -- ✅ User-level tracking -- ✅ No provider API keys in client code - -**Important:** The `auth-token` must be injected at runtime from your auth provider - it's not a static string! - ---- +## Baseline Production Embed -## Integration Examples - -### Vanilla JavaScript (CDN) +### Script tag + shell (recommended) ```html - - - - + + ``` -### React + Okta - -```tsx -import '@mcp-b/char/web-component'; -import { useOktaAuth } from '@okta/okta-react'; - -function ChatWidget() { - const { authState } = useOktaAuth(); +### Low-level agent - if (!authState?.isAuthenticated || !authState.idToken) { - return null; - } - - return ( - - ); -} +```html + + ``` -### React + Auth0 - -```tsx -import '@mcp-b/char/web-component'; -import { useAuth0 } from '@auth0/auth0-react'; -import { useEffect, useState } from 'react'; - -function ChatWidget() { - const { isAuthenticated, getIdTokenClaims } = useAuth0(); - const [token, setToken] = useState(); +## Add Per-User Identity - useEffect(() => { - if (isAuthenticated) { - getIdTokenClaims().then(claims => { - if (claims?.__raw) setToken(claims.__raw); - }); - } - }, [isAuthenticated, getIdTokenClaims]); - - if (!token) return null; - - return ; -} -``` - -### Generic Pattern +If users are authenticated in your app, attach `idToken` client-side. ```ts -import '@mcp-b/char/web-component'; - -// In your HTML: - -// Set token when user authenticates -yourAuth.onAuthStateChanged(async (user) => { - if (user) { - const token = await yourAuth.getIdToken(); - document.getElementById('chat-widget')?.setAttribute('auth-token', token); - } -}); +shell.setAuth({ publishableKey: "pk_live_...", idToken }); ``` ---- - -## Migration Path - -### Step 1: Configure Your IDP - -- Set up your Identity Provider (Okta, Auth0, Azure AD, etc.) in the Char dashboard -- Configure the JWKS endpoint and issuer URL - -### Step 2: Get the Token at Runtime - -- Use your auth provider's SDK to get the user's ID token -- The token must be a valid JWT with a `sub` claim - -### Step 3: Pass the Token to the Widget - -```tsx -// The token comes from your auth system, not hardcoded! - -``` - -### Step 4: Remove devMode - -- Remove `dev-mode` and any client-side Anthropic/OpenAI keys -- Production uses your auth token for identity - -### Step 5: Verify Persistence - -- Reload the page and confirm the same thread history -- Verify the user identity matches your auth system - ---- +Keep `idToken` out of HTML attributes. -## Voice Mode +## Next.js / SSR -Voice mode is only enabled in devMode via `openaiApiKey`. Production voice configuration is not exposed via the web component at this time. +Use client-only rendering for Char components. ---- +- `'use client'` component for shell/agent +- `dynamic(..., { ssr: false })` at server boundary +- call `setAuth()` in `useEffect` -## Troubleshooting +SSR-friendly approach: +- Server-render the app normally +- Hydrate Char client-side only -**401 / Invalid token** -- Confirm `auth-token` is a valid JWT from your configured IDP -- Check that the token hasn't expired -- Verify the JWKS endpoint is accessible +## Production Checklist -**Widget doesn't appear** -- Ensure the user is authenticated before creating the widget -- Check browser console for errors +- [ ] Publishable key created in dashboard +- [ ] Allowed origins/domains configured for key +- [ ] Key is `pk_live_...` for production +- [ ] Optional `idToken` comes from your auth provider at runtime +- [ ] WebMCP tools tested from Chrome DevTools MCP +- [ ] Widget theme variables match product design tokens +- [ ] Key rotation/revocation process documented -**No persistence** -- Ensure `auth-token` is present -- Ensure you are not passing `dev-mode` +## Common Errors ---- +- `MISSING_PUBLISHABLE_KEY`: missing `publishable-key` or `setAuth({ publishableKey })` +- `INVALID_KEY`: key revoked/invalid +- `ORIGIN_NOT_ALLOWED`: origin missing from key allowlist +- `INVALID_TOKEN` / `TOKEN_EXPIRED`: refresh and pass a valid `idToken` -## Summary +## Security Notes -- **Dev/Test**: `dev-mode` with Anthropic API key -- **Production**: `auth-token` from your IDP (injected at runtime) -- **Never** ship dev-mode API keys to production clients -- The token must come from your auth provider dynamically - don't hardcode it! +- Publishable keys are public embed credentials, not secret API keys. +- Restrict key usage with origin/domain allowlists. +- Rotate keys if unexpectedly exposed. +- Pass user identity via `idToken` in JS APIs (`setAuth`/`connect`), not attributes. diff --git a/skills/setup/references/RECIPES.md b/skills/setup/references/RECIPES.md index c512ae5..9a1f942 100644 --- a/skills/setup/references/RECIPES.md +++ b/skills/setup/references/RECIPES.md @@ -1,467 +1,167 @@ -# Common Setup Recipes +# Common Setup Recipes (Publishable Key Model) -Quick copy-paste patterns for common Char integration scenarios. +Copy/paste recipes for the current Char setup model. --- -## Recipe 1: Basic Demo Page +## Recipe 1: Fastest Production-Ready Embed (Script Tag + Shell) -**Goal**: Create a working Char demo in 30 seconds - -**When to use**: Testing, learning, or quick prototypes - -**Steps**: -1. Ask: "Create a Char demo page" -2. Provide your Anthropic API key when prompted -3. Open the generated demo.html in Chrome/Edge - -**Expected result**: -- HTML file created with contact form and counter -- Char widget visible in bottom-right -- Console shows: `✅ Char embedded agent initialized!` -- Agent can interact with form elements - -**Example request**: -``` -Create a Char demo page for testing WebMCP tools -``` - ---- - -## Recipe 2: Add to Existing Website - -**Goal**: Integrate Char into your live website - -**When to use**: Adding AI chat to production site - -**Prerequisites**: -- Existing HTML file or website -- Anthropic API key (dev mode) OR auth token (production) - -**Steps**: -1. Ask: "Add Char to my website at /path/to/index.html" -2. Skill adds widget code before `` -3. Injects API key (dev mode) or auth token placeholder (production) -4. Verify in browser console - -**What gets added** (choose one): -```html - - -``` +**Use when:** You want the default Char UX quickly. ```html - - + + + ``` -**Example request**: -``` -Add Char to my homepage at ~/my-site/index.html -``` +Notes: +- `@latest` keeps the embed up to date. +- Shell handles pill/panel/fullscreen behavior automatically. --- -## Recipe 3: Visual Integration (Match Your Brand) - -**Goal**: Automatically match Char's styling to your website's design - -**When to use**: Ensuring visual consistency +## Recipe 2: Add Per-User Identity -**Prerequisites**: -- Chrome DevTools MCP installed -- Website with consistent design (colors, fonts, border radius) +**Use when:** Your app has logged-in users and you want attributed sessions. -**Steps**: -1. Ask: "Set up Char and match my page's design at /path/to/page.html" -2. Skill analyzes page styles automatically -3. Extracts colors, fonts, border radius -4. Generates matching CSS variable overrides -5. Takes before/after screenshots -6. Applies CSS variables to Char widget - -**What happens**: -``` -📸 Taking baseline screenshot... -🎨 Extracting page styles... - Primary: #667eea - Background: #ffffff - Font: -apple-system, BlinkMacSystemFont, 'Segoe UI' - Radius: 8px -⚙️ Generating CSS variables... -✨ Applying CSS variables to Char... -📸 Taking verification screenshots (collapsed, expanded, mobile) -✅ Visual integration complete! +```html + + + ``` -**Example request**: -``` -Set up Char on my landing page and make sure it matches the purple gradient style -``` +Do not place `idToken` in HTML attributes. --- -## Recipe 4: Add Char to Any Website (Custom Element) - -**Goal**: Embed the Char AI agent using a simple `` custom element +## Recipe 3: Low-Level Control with `` -**When to use**: ANY website (framework-agnostic) +**Use when:** You already own host shell/layout behavior. -**Prerequisites**: -- Modern browser (Chrome 90+, Firefox 95+, Safari 15+) -- Anthropic API key for dev mode - -**Installation** (Choose one): - -**Option A: CDN (Recommended - Zero Configuration)** ```html - - - - -``` - -**Option B: NPM Install** (For bundlers) -```bash -npm install @mcp-b/char -``` - -```html - - - - -``` - -**Complete Example**: -```html - - - - My App with Char - - -

My Application

-

Your app content here...

- - - - - - - - -``` - -**With CSS Variable Customization**: -```html - - - -``` - -**Why This Approach?** -- ✅ **Zero configuration** - No build tools, no framework setup -- ✅ **Framework agnostic** - Works everywhere -- ✅ **Shadow DOM** - Styles are always isolated -- ✅ **Auto-updating** - CDN serves latest version -- ✅ **Type-safe** - TypeScript definitions included - -**Example request**: -``` -Add Char to my website ``` --- -## Recipe 5: Production Setup (auth-token) +## Recipe 4: Next.js Client-Only Embed -**Goal**: Use auth-token for production usage +**Use when:** App Router/SSR is enabled. -**When to use**: Production websites with persistent conversations +```tsx +// app/components/char-shell-client.tsx +"use client"; -**Prerequisites**: -- Auth token from your auth system +import { useEffect, useRef } from "react"; +import "@mcp-b/char/shell-component"; +import type { CharAgentShellElement } from "@mcp-b/char/shell-component"; -**Steps**: -1. Copy your auth token -2. Pass `auth-token` to the widget -3. Remove `dev-mode` from production - -**Code**: -```html - -``` +export function CharShellClient({ idToken }: { idToken?: string }) { + const ref = useRef(null); -**See**: [PRODUCTION.md](./PRODUCTION.md) for complete deployment guide + useEffect(() => { + ref.current?.setAuth({ publishableKey: "pk_live_...", idToken }); + }, [idToken]); -**Example request**: -``` -I'm ready to move from dev mode to production with auth-token - walk me through it + return ; +} ``` ---- - -## Recipe 6: Custom WebMCP Tools (Framework-Agnostic) - -**Goal**: Register page-specific tools that your embedded agent can use +```tsx +// app/layout.tsx +import dynamic from "next/dynamic"; +import type { ReactNode } from "react"; -**When to use**: Adding custom automation for your specific application +const CharShellClient = dynamic(() => import("./components/char-shell-client").then(m => m.CharShellClient), { ssr: false }); -**Prerequisites**: -- `@mcp-b/global` installed - -**Installation**: -```bash -npm install @mcp-b/global +export default function RootLayout({ children }: { children: ReactNode }) { + return ( + + + {children} + + + + ); +} ``` -**Example** - Add a "fill login form" tool using vanilla registration: +--- -```typescript -const modelContext = window.navigator?.modelContext; -if (!modelContext?.registerTool) { - throw new Error('navigator.modelContext.registerTool is not available. Did you import @mcp-b/global first?'); -} +## Recipe 5: Register a WebMCP Tool -modelContext.registerTool({ - name: 'fill_login_form', - description: 'Fill the login form with credentials', - inputSchema: { - type: 'object', - properties: { - username: { type: 'string', description: 'Username or email' }, - password: { type: 'string', description: 'Password' }, - }, - required: ['username', 'password'], - }, - execute: async ({ username, password }) => { - const usernameInput = document.querySelector('#username'); - const passwordInput = document.querySelector('#password'); - - if (!usernameInput || !passwordInput) { - throw new Error('Login inputs not found'); - } - - usernameInput.value = username; - passwordInput.value = password; - return { - content: [{ type: 'text', text: 'Login form filled' }], - }; +```ts +import "@mcp-b/global"; + +navigator.modelContext.registerTool({ + name: "open_support_modal", + description: "Open the support modal on the current page", + inputSchema: { type: "object", properties: {} }, + annotations: { destructiveHint: false }, + execute: async () => { + document.querySelector("[data-support-open]")?.dispatchEvent(new MouseEvent("click", { bubbles: true })); + return { content: [{ type: "text", text: "Support modal opened" }] }; }, }); ``` -**Now your agent can**: -- See these tools when on the login page -- Call: `fill_login_form({ username: "test@example.com", password: "password123" })` -- Add a `submit_login()` tool if you want explicit form submission +--- -**Progressive Disclosure**: Register tools only on pages where they apply. +## Recipe 6: Test Tools with Chrome DevTools MCP ---- +```text +list_webmcp_tools summary=true +call_webmcp_tool name="open_support_modal" arguments={} +``` -## Recipe 7: Dark Mode Support +Validation checklist: +- Tool appears in list +- Tool description is clear +- Tool executes without console errors +- UI reflects expected side effect -**Goal**: Char matches user's dark/light mode preference +--- -**When to use**: Sites with dark mode toggle +## Recipe 7: Theme Shell and Agent -**Auto-detection** (recommended): ```html - - -``` - -**Example request**: -``` -Set up Char with dark mode support that follows the user's system preference -``` - ---- - -## Recipe 8: Multiple Agents on One Page - -**Goal**: Run multiple specialized agents (e.g., sales, support, FAQ) - -**When to use**: Different agent personalities or knowledge bases - -**Note**: Multiple widgets are possible but not recommended. Keep one primary widget unless you have a strong reason. - -**Code**: -```html - - - - - - - ``` -Each agent has independent conversation history and can have different tools/knowledge. - --- -## Recipe 9: Testing WebMCP Tools - -**Goal**: Verify WebMCP browser automation works correctly - -**When to use**: After setup, before deployment - -**Manual test**: -1. Open demo page in Chrome/Edge -2. Open DevTools console (F12) -3. Click Char widget -4. Try these commands: - - "Fill out the form with test data" - - "Click the increment button 5 times" - - "What's the current counter value?" - - "Take a screenshot of this page" - -**Expected results**: -- Form fields populate automatically -- Counter increments to 5 -- Agent reports counter value accurately -- Screenshot saved/shown - -**With Chrome DevTools MCP** (automated): -``` -Ask the skill: "Verify WebMCP tools are working" -``` - -The skill runs automated tests and reports success/failure. - ---- - -## Recipe 10: Troubleshooting Common Issues - -**Goal**: Quickly diagnose and fix setup problems - -**Problem**: Widget doesn't appear - -**Solution checklist**: -```bash -# 1. Check console for errors -# Open DevTools (F12) → Console tab -# Look for: import errors, CORS errors, API key errors +## Migration Notes -# 2. Verify CDN is accessible -# Check Network tab: https://cdn.jsdelivr.net/npm/@mcp-b/char@latest/dist/web-component-standalone.iife.js should load (200 OK) - -# 3. Confirm API key format -# Should start with: sk-ant-api03-... - -# 4. Check browser compatibility -# Chrome/Edge 90+, Firefox 88+, Safari 14+ - -# 5. Verify serving over HTTP/HTTPS (not file://) -python -m http.server 8000 # Then open http://localhost:8000 -``` - -**See**: [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) for complete guide - ---- - -## Recipe 11: Live Preview on Customer Site (CDP) - -**Goal**: Preview what Char looks like on any live website without touching their code - -**When to use**: Sales demos, customer onboarding, design verification - -**Prerequisites**: -- Chrome DevTools MCP installed -- Target site accessible in Chrome - -**Steps**: -1. Ask: "Preview what Char looks like on https://example.com/dashboard" -2. Skill navigates to the page and extracts design tokens -3. Fetches the Char IIFE bundle from jsdelivr CDN -4. Injects a themed collapsible sidebar with matching colors -5. Takes before/after screenshots (light + dark mode) - -**What happens**: -``` -📸 Navigating to target page... -🎨 Extracting CSS variables (light + dark)... -📐 Mapping DOM structure... -📦 Fetching Char bundle from jsdelivr... -💉 Injecting themed sidebar... -📸 Screenshots: light mode, dark mode -✅ Live preview complete! -``` - -**Result**: A fully themed Char sidebar on their live site — content pushes left when opened, just like the real integration would look. - -**See**: [LIVE_PREVIEW.md](./LIVE_PREVIEW.md) for the complete step-by-step workflow - -**Example requests**: -``` -Preview what Char looks like on https://app.example.com -Show me Char on my site at https://myapp.dev/dashboard -Inject Char into the live page at https://staging.example.com -``` - ---- - -## Quick Tips - -**Fastest setup**: -``` -"Create a Char demo page" → Provide API key → Done -``` - -**Best visual match**: -``` -"Set up Char matching my page's design" (requires Chrome DevTools MCP) -``` - -**Production checklist**: -1. Use `auth-token` (not devMode) -2. Enable HTTPS -3. Monitor usage and logs -4. Test at scale - -**Getting help**: -- [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) - Common issues -- [CUSTOMIZATION.md](./CUSTOMIZATION.md) - Styling reference -- [VISUAL_INTEGRATION.md](./VISUAL_INTEGRATION.md) - Design tips -- [WEBMCP_REFERENCE.md](./WEBMCP_REFERENCE.md) - Tool docs -- [PRODUCTION.md](./PRODUCTION.md) - Deployment guide - ---- +Legacy patterns to remove: +- `dev-mode` +- `auth-token` +- `clientId`/`organizationId` in `connect()` +- `ticketAuth` -**Need something not covered here?** Ask the char-setup skill directly - it adapts to your specific needs. +Current contract: +- `publishableKey` required +- `idToken` optional diff --git a/skills/setup/references/TESTING_GUIDE.md b/skills/setup/references/TESTING_GUIDE.md index 1b6c432..e95de6a 100644 --- a/skills/setup/references/TESTING_GUIDE.md +++ b/skills/setup/references/TESTING_GUIDE.md @@ -1,269 +1,96 @@ -# Testing Guide +# Testing Guide (Publishable Key + Shell) -How to test Char embedded agent integrations using Chrome DevTools MCP and manual verification. +How to validate Char integrations with Chrome DevTools MCP and manual fallback. ---- +## Baseline Test Fixture -## Phase 1: Stateless Testing (Development) +Use a page with: +- `` or `` +- `publishableKey` configured +- at least one WebMCP tool registered in host app -**Goal**: Verify basic functionality without backend +## Phase 1: Embed Runtime Validation -**Setup**: -```html - - -``` - -**Tests**: -1. Widget appears -2. Chat opens on click -3. Agent responds to messages -4. WebMCP tools execute (using Chrome DevTools MCP to verify) -5. No console errors - -**Advantages**: -- Fast iteration -- No backend setup required -- Easy debugging - -**Disadvantages**: -- No conversation persistence -- No user identification -- Not suitable for production - ---- - -## Phase 2: Stateful Testing (Production Preparation) - -**Goal**: Verify backend integration and persistence - -**Setup**: -```html - - -``` - -**Tests**: -1. Conversation persists across page reloads -2. User identification works -3. Multiple users have separate conversations -4. Backend API endpoints respond correctly -5. Database stores messages - -**Advantages**: -- Production-like environment -- Conversation history -- User tracking - -**Disadvantages**: -- Requires auth-token issuance -- Requires allowed origins setup - ---- - -## Automated Testing with Chrome DevTools MCP - -The Chrome DevTools MCP server enables **fully automated verification**: - -```typescript -// Pseudo-code for automated test flow - -// 1. Navigate to page -await mcp__chrome-devtools__navigate_page({ - url: 'http://localhost:3000' -}) - -// 2. Take snapshot to verify page loaded -const snapshot = await mcp__chrome-devtools__take_snapshot() -// Parse snapshot to find widget element - -// 3. Click widget to open chat -await mcp__chrome-devtools__click({ - uid: 'char-pill' -}) - -// 4. Wait for chat to open -await mcp__chrome-devtools__wait_for({ - text: 'How can I help you today?' -}) - -// 5. Fill chat input and send message -await mcp__chrome-devtools__fill({ - uid: 'chat-input', - value: 'Fill out the contact form with test data' -}) -await mcp__chrome-devtools__press_key({ - key: 'Enter' -}) - -// 6. Wait for agent to execute tool -await mcp__chrome-devtools__wait_for({ - text: 'I\'ve filled out the contact form' -}) - -// 7. Verify form was filled -const formSnapshot = await mcp__chrome-devtools__take_snapshot() -// Parse to verify form fields have values - -// 8. Take screenshot for visual verification -await mcp__chrome-devtools__take_screenshot({ - filePath: './test-results/form-filled.png' -}) +Verify: +1. Element is present (`char-agent-shell` preferred) +2. No console errors on load +3. Shell behavior works: + - closed: pill visible + - desktop open: inline right panel + - narrow viewport open: fullscreen -// Success! -``` - ---- - -## Manual Testing (Fallback) - -If Chrome DevTools MCP is not available: - -1. **Open browser** to target URL -2. **Check console** for `Char embedded agent initialized!` -3. **Click widget** to open chat -4. **Test commands**: - - "What is on this page?" (snapshot tool) - - "Fill out the form with test data" (fill tool) - - "Click the submit button" (click tool) - - "Take a screenshot" (screenshot tool) -5. **Verify results** manually - ---- +## Phase 2: Auth Validation -## Verifying Tool Registration +### Key-only mode -Use `diff_webmcp_tools` to check what tools are registered: - -```bash -# Check current tools -const tools = await mcp__chrome-devtools__diff_webmcp_tools() - -# Tools are named: webmcp_{host}_{page}_toolname -# Example: webmcp_localhost_3000_page0_fill_login_form +```ts +shell.setAuth({ publishableKey: "pk_live_..." }); ``` -### Progressive Disclosure Test - -1. Navigate to page A -2. Check tools - should see page A tools -3. Navigate to page B -4. Check tools again - page A tools gone, page B tools appear - ---- - -## Common Test Scenarios - -### Test Login Flow +Verify agent can chat. -```typescript -// 1. Navigate to login -await mcp__chrome-devtools__navigate_page({ url: 'http://localhost:3000/signin' }) +### User-attributed mode (optional) -// 2. Verify login tools exist -const tools = await mcp__chrome-devtools__diff_webmcp_tools() -// Should include: fill_login_form, submit_login - -// 3. Fill the form -await mcp__chrome-devtools__fill({ - uid: 'username-input', - value: 'testuser' -}) -await mcp__chrome-devtools__fill({ - uid: 'password-input', - value: 'password123' -}) - -// 4. Submit -await mcp__chrome-devtools__click({ - uid: 'submit-button' -}) - -// 5. Verify redirect to dashboard -await mcp__chrome-devtools__wait_for({ - text: 'Welcome, testuser' -}) +```ts +shell.setAuth({ publishableKey: "pk_live_...", idToken }); ``` -### Test Form Filling via Agent +Verify attributed identity and session behavior. -```typescript -// 1. Open the chat widget -await mcp__chrome-devtools__click({ uid: 'chat-pill' }) +## Phase 3: WebMCP Tool Validation -// 2. Ask agent to fill form -await mcp__chrome-devtools__fill({ - uid: 'chat-input', - value: 'Fill the contact form with name "John Doe", email "john@example.com", and message "Test message"' -}) -await mcp__chrome-devtools__press_key({ key: 'Enter' }) +Use Chrome DevTools MCP: -// 3. Wait for agent to complete -await mcp__chrome-devtools__wait_for({ - text: 'filled', - timeout: 10000 -}) - -// 4. Verify form fields -const snapshot = await mcp__chrome-devtools__take_snapshot() -// Check snapshot contains "John Doe" and "john@example.com" +```text +list_webmcp_tools summary=true +call_webmcp_tool name="your_tool" arguments={} +call_webmcp_tool name="your_tool" arguments={"field":"value"} ``` ---- - -## Console Verification - -Check for these console messages: - -| Message | Meaning | -|---------|---------| -| `Char embedded agent initialized!` | Widget loaded successfully | -| `WebMCP tools registered: N` | N tools available to agent | -| `Connected to host` | Backend connection established (stateful mode) | - -Check for errors: -```typescript -const messages = await mcp__chrome-devtools__list_console_messages({ - types: ['error'] -}) -// Should be empty or contain only non-critical errors +Verify: +- tool is discoverable +- read path works +- mutation path works +- result payload is understandable + +## Automated Browser Validation Flow + +Suggested command sequence: + +```text +new_page url="http://localhost:3000" +take_snapshot +list_console_messages +list_webmcp_tools summary=true +call_webmcp_tool name="your_tool" arguments={} +take_screenshot +resize_page width=375 height=812 +take_screenshot ``` ---- - -## Screenshot Verification +## Manual Fallback (No CDP) -Capture screenshots at key milestones: +1. Open the page in Chrome/Edge +2. Confirm no console errors +3. Open Char and send a message +4. Ask the agent to run one WebMCP-backed action +5. Confirm host UI changed as expected -```typescript -// Widget collapsed -await mcp__chrome-devtools__take_screenshot({ - filePath: './screenshots/widget-collapsed.png' -}) - -// Widget expanded -await mcp__chrome-devtools__click({ uid: 'chat-pill' }) -await mcp__chrome-devtools__take_screenshot({ - filePath: './screenshots/widget-expanded.png' -}) - -// Mobile viewport -await mcp__chrome-devtools__resize_page({ width: 375, height: 667 }) -await mcp__chrome-devtools__take_screenshot({ - filePath: './screenshots/mobile.png' -}) -``` +## Common Failures ---- +| Error | Cause | Fix | +|------|-------|-----| +| `MISSING_PUBLISHABLE_KEY` | Key not supplied | Set `publishable-key` or call `setAuth({ publishableKey })` | +| `INVALID_KEY` | Revoked/invalid key | Regenerate key in dashboard | +| `ORIGIN_NOT_ALLOWED` | Origin not allowlisted | Add current origin to key/domain allowlist | +| Tool not listed | Registration never ran | Ensure `@mcp-b/global` is imported and component mounted | +| Tool call fails | Schema mismatch | Re-check input shape and descriptions | -## Troubleshooting Test Failures +## Quality Checklist -| Issue | Cause | Solution | -|-------|-------|----------| -| Widget not found | Widget didn't load | Check console for errors, verify script loaded | -| Tools not registered | `navigator.modelContext.registerTool` not called | Verify the registration code runs on page load | -| Agent not responding | API key invalid | Check Anthropic API key is correct | -| Form not filled | Wrong element UID | Take snapshot, find correct UID | -| Timeout waiting for text | Agent slow or failed | Increase timeout, check agent logs | +- [ ] Shell UX (pill/panel/fullscreen) behaves correctly +- [ ] Publishable key path works +- [ ] Optional `idToken` path works +- [ ] At least one WebMCP tool is callable +- [ ] No critical console/network errors +- [ ] Screenshots captured for desktop + mobile diff --git a/skills/setup/scripts/setup.js b/skills/setup/scripts/setup.js index e255d1f..136ebaa 100644 --- a/skills/setup/scripts/setup.js +++ b/skills/setup/scripts/setup.js @@ -8,29 +8,25 @@ */ import { readFile, writeFile } from 'node:fs/promises'; -import { join } from 'node:path'; /** - * Replace API key placeholder in demo HTML + * Replace publishable key placeholder in demo HTML */ -export async function injectApiKey(htmlPath, apiKey) { +export async function injectPublishableKey(htmlPath, publishableKey) { const content = await readFile(htmlPath, 'utf-8'); - if (!apiKey || !apiKey.startsWith('sk-ant-')) { - throw new Error('Invalid Anthropic API key. Must start with "sk-ant-"'); + if (!publishableKey || !publishableKey.startsWith('pk_')) { + throw new Error('Invalid publishable key. Must start with "pk_"'); } - const updated = content.replaceAll( - 'REPLACE_WITH_API_KEY', - apiKey - ); + const updated = content.replaceAll('REPLACE_WITH_PUBLISHABLE_KEY', publishableKey); await writeFile(htmlPath, updated, 'utf-8'); return { success: true, path: htmlPath, - message: `API key injected into ${htmlPath}` + message: `Publishable key injected into ${htmlPath}`, }; } @@ -43,9 +39,9 @@ export async function verifyDemoPage(htmlPath) { const checks = { hasEmbeddedAgent: content.includes('@mcp-b/char') || content.includes('cdn.jsdelivr.net/npm/@mcp-b/char'), - hasApiKey: !content.includes('REPLACE_WITH_API_KEY'), + hasPublishableKey: !content.includes('REPLACE_WITH_PUBLISHABLE_KEY'), hasStyles: content.includes('