diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..89f3b2c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,54 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What This Is + +Intercom plugin for Claude Code — a skill-based integration (not compiled code). Skills are pure Markdown files that Claude reads at runtime to interact with Intercom workspaces via a remote MCP server. + +## Architecture + +``` +User → Claude Code CLI → Skill (SKILL.md) → MCP Server (https://mcp.intercom.com/mcp) → Intercom API +``` + +- **No build step, no dependencies, no tests.** The plugin is entirely declarative. +- `.mcp.json` routes the `intercom` namespace to the remote HTTP-based MCP server. +- `.claude-plugin/plugin.json` is the plugin manifest for Claude Code's plugin registry. +- `.claude/settings.local.json` manages local permissions (currently allows WebFetch from raw.githubusercontent.com). + +## Skills + +Three skills live under `skills/`, each in its own directory: + +| Skill | Trigger | Key Files | +| ------------------- | ---------------------------------------------------------- | -------------------------------------------- | +| `install-messenger` | Explicit: `/intercom:install-messenger [framework]` | `SKILL.md`, `references/framework-guides.md` | +| `intercom-analysis` | Auto-triggered by keywords | `SKILL.md`, `references/mcp-tools.md` | +| `customer-360` | Explicit: `/intercom:customer-360 [email or company name]` | `SKILL.md` | + +### Skill File Structure + +Each skill directory contains: + +- `SKILL.md` — Main skill file with YAML frontmatter (`name`, `description`, optional `disable-model-invocation`, `argument-hint`) followed by Markdown instructions Claude reads at runtime. +- `references/` — Supplementary docs loaded on demand for progressive disclosure (keeps SKILL.md concise). + +### Invocation Modes + +- **Auto-triggered:** No `disable-model-invocation` in frontmatter. Claude activates the skill when the user's intent matches the `description` field. +- **Explicit:** `disable-model-invocation: true` in frontmatter. Only invoked via `/intercom:`. + +## Key Conventions + +- **Security-first:** `install-messenger` defaults to JWT-based identity verification (HS256). Insecure mode requires explicit user opt-in. Never default to insecure installation. +- **Progressive disclosure:** Keep SKILL.md focused on workflow logic. Move API references, framework-specific code, and tool documentation into `references/` subdirectories. +- **Read-only access:** The MCP server only supports search and retrieval operations — no create, update, or delete. +- **Regional awareness:** Intercom supports US, EU (Ireland), and Australia data residency. Currently US-only; EU/AU planned. + +## Development Workflow + +1. Edit SKILL.md files or reference documents +2. Test by running skills in Claude Code CLI (e.g., `/intercom:install-messenger react`) +3. For auto-triggered skills, test by typing natural language that matches the skill's `description` field +4. Push to GitHub — the plugin registry picks up changes from the repository diff --git a/README.md b/README.md index 5d8be02..6160f8e 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ Connect your Intercom workspace to Claude Code. Search conversations, analyze cu | Skill | Invocation | Description | |-------|-----------|-------------| -| **Intercom Insights** | Auto-triggered | Analyze conversations, find support patterns, investigate customer issues, and look up contacts. Triggers automatically when you ask about your Intercom data. | -| **Install Messenger** | `/intercom:install-messenger [framework]` | Step-by-step guide to install the Intercom Messenger on your website. Supports React, Next.js, Vue.js, and plain JavaScript. | -| **Intercom Search** | `/intercom:intercom-search [type] [query]` | Quick search for conversations or contacts by keyword, email, or topic. Returns formatted results with follow-up options. | +| **Intercom Analysis** | Auto-triggered | Analyze conversations, find support patterns, investigate customer issues, and look up contacts. Triggers automatically when you ask about your Intercom data. | +| **Install Messenger** | `/intercom:install-messenger [framework]` | Install the Intercom Messenger with secure JWT-based identity verification. Supports React, Next.js, Vue.js, and plain JavaScript. | +| **Customer 360** | `/intercom:customer-360 [email or company]` | Build a comprehensive customer profile with conversation history, account context, and interaction timeline. | ## Usage Examples @@ -28,14 +28,9 @@ Show me the most common topics in open conversations this week Look up all conversations from jane@example.com and summarize her issues ``` -**Search for specific conversations:** +**Build a customer profile:** ``` -/intercom:intercom-search conversations billing error -``` - -**Find contacts at a company:** -``` -/intercom:intercom-search contacts @acme.com +/intercom:customer-360 jane@example.com ``` **Install the Messenger:** @@ -48,9 +43,9 @@ Look up all conversations from jane@example.com and summarize her issues Pull up conversation #12345 and show me the full thread ``` -## Limitations +## Access & Limitations -- **Read-only access** — The plugin can search and retrieve data but cannot create, update, or delete conversations, contacts, or other Intercom objects. +- **Safe, read-only access** — The plugin can search and retrieve data but cannot create, update, or delete conversations, contacts, or other Intercom objects. Your workspace data is never modified. - **US region only** — Currently supports US-hosted Intercom workspaces. EU and Australia region support is planned. - **Rate limits** — Search operations are subject to Intercom API rate limits. The MCP server handles throttling automatically. diff --git a/skills/customer-360/SKILL.md b/skills/customer-360/SKILL.md new file mode 100644 index 0000000..1530de1 --- /dev/null +++ b/skills/customer-360/SKILL.md @@ -0,0 +1,77 @@ +--- +name: customer-360 +license: MIT +description: > + Build a comprehensive customer profile from Intercom data, including + conversation history, account context, and interaction timeline. Use when + the user asks to "look up a customer", "customer profile", "customer 360", + "tell me about this customer", "summarize a customer's history", or provides + a customer email or company name and wants a full picture. +disable-model-invocation: true +argument-hint: "[email or company name]" +--- + +# Customer 360 + +Produce a comprehensive customer profile by aggregating data from across the Intercom workspace. + +## Workflow + +### Step 1: Identify the Customer +- Parse the user's input for an email, name, or company +- Use `search_contacts` to find the contact (by email is most reliable) +- If company name given, search contacts by email domain +- Use `get_contact` to fetch the full profile (attributes, tags, segments, companies) + +### Step 2: Gather Conversation History +- Use `search_conversations` filtered to this contact's conversations +- Paginate to get all conversations (not just the first page) +- For the most recent/relevant conversations, use `get_conversation` for full threads + +### Step 3: Build the Profile +Produce a markdown profile with these sections: + +#### Contact Summary +- Name, email, role (user/lead), company +- Location, timezone, last seen +- Plan/tier (if available via custom attributes) +- Tags and segments + +#### Conversation History +- Total conversation count +- Table: ID | Subject | State | Channel | Date +- Highlight any currently open/snoozed conversations + +#### Key Themes +- Common topics across conversations +- Recurring issues or feature requests +- Sentiment signals (escalations, positive feedback) + +#### Timeline +- Chronological list of significant interactions +- First contact → most recent activity + +#### Open Items +- Unresolved conversations +- Pending questions or follow-ups + +### Step 4: Present and Offer Next Steps +- Present the profile +- Offer to dive deeper into any conversation +- Offer to search for similar customers (same company, same issues) + +## Best Practices +- Always cite conversation IDs so the user can find them in the Intercom inbox +- Note data freshness — states may have changed since events occurred +- If the contact has many conversations, summarize the most recent 10-15 and note the total count +- Handle cases where the contact exists but has no conversations + +## Troubleshooting + +### Contact Not Found +Symptom: No results for the provided email or name. +Solution: Check for typos. Try searching by email domain to find related contacts. Ask the user to confirm the identifier. + +### Too Many Conversations +Symptom: Contact has 50+ conversations, making full analysis impractical. +Solution: Focus on the most recent 10-15 conversations. Summarize older ones by theme rather than individually. Note the total count. diff --git a/skills/install-messenger/SKILL.md b/skills/install-messenger/SKILL.md index fea9a99..e5b0fcb 100644 --- a/skills/install-messenger/SKILL.md +++ b/skills/install-messenger/SKILL.md @@ -1,10 +1,13 @@ --- name: install-messenger +license: MIT description: > - This skill should be used when the user asks to "install Intercom", - "add the Intercom Messenger", "set up Intercom chat widget", - "add customer chat to my website", or "integrate Intercom" into - their website or application. + Install the Intercom Messenger on a website or web application with + secure JWT-based identity verification. Generates backend and frontend + code for React, Next.js, Vue.js, Angular, Ember, and plain JavaScript. + Supports Node.js, Python (Flask/Django), PHP, and Ruby backends. Use when + the user asks to "install Intercom", "add the Intercom Messenger", "set up + Intercom chat widget", "add customer chat to my website", or "integrate Intercom". disable-model-invocation: true argument-hint: "[framework]" --- @@ -27,6 +30,8 @@ Gather these from the user before proceeding: Ask the user for both values. Do not proceed without the Workspace ID. If they don't have the Identity Verification Secret yet, direct them to **Settings > Channels > Messenger > Security** in Intercom to enable it. +In all generated code, replace `YOUR_WORKSPACE_ID` with the user's actual Workspace ID. Do not leave placeholders — substitute the real values they provided. + ## Installation Overview The secure installation has two parts: @@ -99,6 +104,59 @@ def intercom_jwt(): return {'token': token} ``` +### Python / Django Example + +```python +# views.py +import jwt +import time +import os +from django.http import JsonResponse +from django.contrib.auth.decorators import login_required + +INTERCOM_SECRET = os.environ['INTERCOM_IDENTITY_SECRET'] + +@login_required +def intercom_jwt(request): + token = jwt.encode( + { + 'user_id': str(request.user.id), + 'email': request.user.email, + 'name': request.user.get_full_name(), + 'exp': int(time.time()) + 7200, # 2 hours + }, + INTERCOM_SECRET, + algorithm='HS256', + ) + return JsonResponse({'token': token}) +``` + +Add the URL pattern: `path('api/intercom-jwt', views.intercom_jwt)` in your `urls.py`. + +### PHP Example + +Requires the `firebase/php-jwt` package: `composer require firebase/php-jwt` + +```php + (string) $user->id, + 'email' => $user->email, + 'name' => $user->name, + 'exp' => time() + 7200, // 2 hours +], $secret, 'HS256'); + +header('Content-Type: application/json'); +echo json_encode(['token' => $token]); +``` + ### Ruby / Rails Example ```ruby @@ -123,6 +181,8 @@ class Api::IntercomController < ApplicationController end ``` +**Alternative: `intercom-rails` gem** — For simpler setups, Intercom provides a Rails gem that auto-injects the Messenger. Install with `gem "intercom-rails"`, run `rails generate intercom:config YOUR_WORKSPACE_ID`, and configure the secret in the generated initializer. See the [Intercom install page](https://app.intercom.com/a/apps/_/settings/channels/messenger/install) for details. The JWT approach above gives you more control and works with any Ruby backend, not just Rails. + Adapt the example to the user's backend language and framework. The key requirements are: - The endpoint is authenticated (only the logged-in user can get their own JWT) - The secret comes from an environment variable, never hardcoded @@ -143,7 +203,6 @@ Add before the closing `` tag: .then(res => res.json()) .then(({ token }) => { window.Intercom('boot', { - api_base: 'https://api-iam.intercom.io', app_id: 'YOUR_WORKSPACE_ID', intercom_user_jwt: token, }); @@ -160,7 +219,6 @@ For pages where the visitor is not logged in (marketing pages, docs), boot witho ```javascript window.Intercom('boot', { - api_base: 'https://api-iam.intercom.io', app_id: 'YOUR_WORKSPACE_ID', }); ``` @@ -169,21 +227,21 @@ No JWT is needed for anonymous visitors. Intercom tracks them as leads. ## Framework-Specific Installation -If the user is using React, Next.js, Vue.js, or another SPA framework, refer to `references/framework-guides.md` for JWT-integrated installation code. Ask the user what framework they are using if it is not obvious from their codebase. +If the user is using React, Next.js, Vue.js, Angular, Ember, or another SPA framework, refer to `references/framework-guides.md` for JWT-integrated installation code. Ask the user what framework they are using if it is not obvious from their codebase. After reading the framework guide, adapt the code to the user's specific project structure — find their main layout component, app entry point, or equivalent, and integrate the Messenger there. ## Regional Data Centers -Intercom operates in multiple regions. The `api_base` URL must match the user's data residency region: +Most Intercom workspaces are in the US region, which is the default — no `api_base` is needed. Only add `api_base` if the user's workspace is hosted in EU or Australia: -| Region | `api_base` URL | -|--------|----------------| -| US (default) | `https://api-iam.intercom.io` | -| EU (Ireland) | `https://api-iam.eu.intercom.io` | -| Australia | `https://api-iam.au.intercom.io` | +| Region | `api_base` | Required? | +|--------|------------|-----------| +| US (default) | *(not needed)* | No | +| EU (Ireland) | `https://api-iam.eu.intercom.io` | Yes | +| Australia | `https://api-iam.au.intercom.io` | Yes | -Ask the user which region their workspace is hosted in if they mention EU or Australian hosting, GDPR compliance, or data residency requirements. Default to US if unspecified. +If the user mentions EU or Australian hosting, GDPR compliance, or data residency requirements, add the appropriate `api_base` to every `Intercom('boot', ...)` call. Otherwise, omit it. ## Security Best Practices @@ -200,7 +258,6 @@ After shutdown, re-initialize for the next user or as anonymous: ```javascript Intercom('boot', { - api_base: 'https://api-iam.intercom.io', app_id: 'YOUR_WORKSPACE_ID', }); ``` @@ -209,7 +266,7 @@ Always include the shutdown call. Skipping it leaks conversation data between us ### Protect Identifying Attributes -In Intercom (**Settings > Messenger > Security**), mark identifying attributes (email, phone, account IDs) as **protected**. This prevents client-side code from spoofing these values — only the server-signed JWT can set them. +In Intercom (**Settings > Channels > Messenger > Security**), mark identifying attributes (email, phone, account IDs) as **protected**. This prevents client-side code from spoofing these values — only the server-signed JWT can set them. ### Token Expiration @@ -223,7 +280,6 @@ function refreshIntercomToken() { .then(res => res.json()) .then(({ token }) => { window.Intercom('boot', { - api_base: 'https://api-iam.intercom.io', app_id: 'YOUR_WORKSPACE_ID', intercom_user_jwt: token, }); @@ -231,6 +287,32 @@ function refreshIntercomToken() { } ``` +## Troubleshooting + +### JWT Library Not Installed +Error: `Cannot find module 'jsonwebtoken'` (Node.js), `ModuleNotFoundError: No module named 'jwt'` (Python), or `LoadError: cannot load such file -- jwt` (Ruby) +Solution: Install the JWT library for the user's language — `npm install jsonwebtoken`, `pip install PyJWT`, or `gem install jwt`. + +### Wrong Identity Verification Secret +Symptom: Messenger loads but shows "Identity verification failed" or user attributes don't appear. +Cause: The secret used to sign JWTs doesn't match the workspace's Identity Verification Secret. +Solution: Verify the secret in Intercom at **Settings > Channels > Messenger > Security**. Ensure the environment variable holds the correct value for this workspace. + +### Plan Doesn't Support Identity Verification +Symptom: Identity Verification Secret not available in Intercom settings. +Cause: Identity verification is a paid feature not available on all Intercom plans. +Solution: Check the workspace's Intercom plan. If identity verification is unavailable, the user may need to upgrade or use the insecure installation (with explicit acknowledgment of the security trade-off). + +### JWT `exp` in the Past +Symptom: Messenger rejects the token immediately after creation. +Cause: Server clock is wrong or `exp` calculation is incorrect. +Solution: Check the server's system time (`date` command). Ensure NTP is configured. Verify the `exp` value is a future Unix timestamp in seconds (not milliseconds). + +### CORS Errors on JWT Endpoint +Symptom: Browser console shows `Access-Control-Allow-Origin` errors when fetching the JWT. +Cause: The backend JWT endpoint doesn't allow requests from the frontend's origin. +Solution: Configure CORS on the JWT endpoint to allow the frontend origin. For Express: `cors({ origin: 'https://your-app.com', credentials: true })`. For other frameworks, add the appropriate CORS headers. + ## Single-Page App (SPA) Route Changes In SPAs where the page does not fully reload on navigation, notify the Messenger of route changes: @@ -245,9 +327,22 @@ Where to place this depends on the routing library: - **React Router** — In a `useEffect` hook that watches `location` changes - **Next.js App Router** — In a layout component using `usePathname()` - **Vue Router** — In a `router.afterEach()` navigation guard +- **Angular Router** — In a service subscribing to `NavigationEnd` events +- **Ember Router** — In an instance initializer listening to `routeDidChange` Without this, the Messenger may show stale content or miss page-specific triggers. +## Third-Party Integrations + +Intercom also supports installation via these platforms. These don't require writing code — configure them through each platform's UI: + +- **WordPress** — Install the official Intercom plugin from the WordPress plugin directory +- **Shopify** — Install via the Shopify App Store +- **Google Tag Manager** — Add the Intercom tag using the GTM template gallery +- **Segment** — Enable the Intercom destination in your Segment workspace + +For setup instructions, direct users to the install page in Intercom: **Settings > Channels > Messenger > Install**. + ## Installation Checklist After generating the installation code, verify with the user: @@ -256,7 +351,7 @@ After generating the installation code, verify with the user: 2. The Identity Verification Secret is stored as an environment variable (not hardcoded) 3. JWTs include `user_id` and have a short expiration (`exp`) 4. The frontend passes `intercom_user_jwt` when booting the Messenger -5. The Workspace ID and `api_base` are correct for their region +5. The Workspace ID is correct (and `api_base` is set if the workspace is in EU or Australia) 6. The logout flow calls `Intercom('shutdown')` 7. SPA route changes trigger `Intercom('update')` 8. Identifying attributes are marked as protected in Intercom settings @@ -268,7 +363,6 @@ If the user explicitly asks for an "insecure installation" (no JWT, no identity ```html