#
tokens: 4214/50000 9/9 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .env.example
├── .gitignore
├── package.json
├── README.md
├── src
│   ├── api
│   │   └── client.ts
│   ├── index.ts
│   ├── tools
│   │   └── conversations.ts
│   └── types
│       └── intercom.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------

```
# Intercom API credentials
INTERCOM_API_KEY=your_api_key_here

# Optional configuration
LOG_LEVEL=info
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Dependencies
node_modules/
yarn.lock
package-lock.json

# Build output
dist/
build/

# Environment variables
.env
.env.local

# IDE files
.vscode/
.idea/
*.swp

# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# OS files
.DS_Store
Thumbs.db
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# MCP Intercom Server

A Model Context Protocol (MCP) server that provides access to Intercom conversations and chats. This server allows LLMs to query and analyze your Intercom conversations with various filtering options.

## Features

- Query Intercom conversations with filtering options:
  - Date range (start and end dates)
  - Customer ID
  - Conversation state
- Secure access using your Intercom API key
- Rich conversation data including:
  - Basic conversation details
  - Contact information
  - Statistics (responses, reopens)
  - State and priority information

## Installation

1. Clone the repository:
```bash
git clone https://github.com/fabian1710/mcp-intercom.git
cd mcp-intercom
```

2. Install dependencies:
```bash
npm install
```

3. Set up your environment:
```bash
cp .env.example .env
```

4. Add your Intercom API key to `.env`:
```
INTERCOM_API_KEY=your_api_key_here
```

5. Build the server:
```bash
npm run build
```

## Usage

### Running the Server

Start the server:
```bash
npm start
```

### Using with Claude for Desktop

1. Add the server to your Claude for Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS or `%AppData%\Claude\claude_desktop_config.json` on Windows):

```json
{
  "mcpServers": {
    "intercom": {
      "command": "node",
      "args": ["/path/to/mcp-intercom/dist/index.js"],
      "env": {
        "INTERCOM_API_KEY": "your_api_key_here"
      }
    }
  }
}
```

2. Restart Claude for Desktop

### Available Tools

#### search-conversations

Searches Intercom conversations with optional filters.

Parameters:
- `createdAt` (optional): Object with `operator` (e.g., ">", "<", "=") and `value` (UNIX timestamp) for filtering by creation date.
- `updatedAt` (optional): Object with `operator` (e.g., ">", "<", "=") and `value` (UNIX timestamp) for filtering by update date.
- `sourceType` (optional): Source type of the conversation (e.g., "email", "chat").
- `state` (optional): Conversation state to filter by (e.g., "open", "closed").
- `open` (optional): Boolean to filter by open status.
- `read` (optional): Boolean to filter by read status.

Example queries:
- "Search for all conversations created after January 1, 2024"
- "Find conversations updated before last week"
- "List all open email conversations"
- "Get all unread conversations"

## Security

- The server requires an Intercom API key to function
- API key should be stored securely in environment variables
- The server only provides read access to conversations
- All API requests are made with proper authentication

## Development

1. Start development mode with auto-recompilation:
```bash
npm run dev
```

2. Run linting:
```bash
npm run lint
```

## Contributing

1. Fork the repository
2. Create a new branch for your feature
3. Make your changes
4. Submit a pull request

## License

MIT
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "mcp-intercom",
  "version": "1.0.0",
  "description": "MCP server for Intercom chat integration",
  "type": "module",
  "main": "dist/index.js",
  "bin": {
    "mcp-intercom": "./dist/index.js"
  },
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc -w",
    "lint": "eslint src --ext .ts",
    "test": "jest"
  },
  "keywords": [
    "mcp",
    "intercom",
    "chat"
  ],
  "author": "",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.4",
    "axios": "^1.7.9",
    "dotenv": "^16.3.1",
    "node-fetch": "^3.3.2",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@types/node": "^20.11.5",
    "@typescript-eslint/eslint-plugin": "^6.19.0",
    "@typescript-eslint/parser": "^6.19.0",
    "eslint": "^8.56.0",
    "typescript": "^5.3.3"
  }
}

```

--------------------------------------------------------------------------------
/src/types/intercom.ts:
--------------------------------------------------------------------------------

```typescript
export interface IntercomConversation {
  id: string;
  created_at: number;
  updated_at: number;
  waiting_since: number;
  snoozed_until?: number;
  source: {
    type: string;
    id: string;
    delivered_as: string;
  };
  contacts: {
    type: string;
    id: string;
    name: string;
  }[];
  statistics: {
    time_to_assignment?: number;
    time_to_first_response?: number;
    time_to_last_close?: number;
    median_time_to_response?: number;
    first_contact_reply_at?: number;
    first_assignment_at?: number;
    first_admin_reply_at?: number;
    first_close_at?: number;
    last_assignment_at?: number;
    last_assignment_admin_reply_at?: number;
    last_contact_reply_at?: number;
    last_admin_reply_at?: number;
    last_close_at?: number;
    reopens: number;
    responses: number;
  };
  state: string;
  read: boolean;
  priority: string;
}
```

--------------------------------------------------------------------------------
/src/api/client.ts:
--------------------------------------------------------------------------------

```typescript
import dotenv from "dotenv";
import { IntercomConversation } from "../types/intercom.js";
import axios from "axios";

dotenv.config();

const INTERCOM_API_BASE = "https://api.intercom.io";

export class IntercomClient {
  private apiKey: string;

  constructor() {
    const apiKey = process.env.INTERCOM_API_KEY;
    if (!apiKey) {
      throw new Error("INTERCOM_API_KEY environment variable is required");
    }
    this.apiKey = apiKey;
  }

  private async request<T>(
    path: string,
    params: Record<string, string> = {}
  ): Promise<T> {
    const url = new URL(path, INTERCOM_API_BASE);
    Object.entries(params).forEach(([key, value]) => {
      url.searchParams.append(key, value);
    });

    const response = await axios.get(url.toString(), {
      headers: {
        Authorization: `Bearer ${this.apiKey}`,
        Accept: "application/json",
      },
    });

    return response.data as T;
  }

  async searchConversations(
    filters: {
      createdAt?: { operator: string; value: number };
      updatedAt?: { operator: string; value: number };
      sourceType?: string;
      state?: string;
      open?: boolean;
      read?: boolean;
      // Add more filters as needed
    } = {},
    pagination: { perPage?: number; startingAfter?: string } = {}
  ) {
    const query: any = {
      operator: "AND",
      value: [],
    };

    if (filters.createdAt) {
      query.value.push({
        field: "created_at",
        operator: filters.createdAt.operator,
        value: filters.createdAt.value.toString(),
      });
    }
    if (filters.updatedAt) {
      query.value.push({
        field: "updated_at",
        operator: filters.updatedAt.operator,
        value: filters.updatedAt.value.toString(),
      });
    }
    if (filters.sourceType) {
      query.value.push({
        field: "source.type",
        operator: "=",
        value: filters.sourceType,
      });
    }
    if (filters.state) {
      query.value.push({
        field: "state",
        operator: "=",
        value: filters.state,
      });
    }
    if (filters.open !== undefined) {
      query.value.push({
        field: "open",
        operator: "=",
        value: filters.open.toString(),
      });
    }
    if (filters.read !== undefined) {
      query.value.push({
        field: "read",
        operator: "=",
        value: filters.read.toString(),
      });
    }

    const body = {
      query,
      pagination: {
        per_page: pagination.perPage || 20,
        starting_after: pagination.startingAfter || null,
      },
    };

    const response = await axios.post(
      `${INTERCOM_API_BASE}/conversations/search`,
      body,
      {
        headers: {
          Authorization: `Bearer ${this.apiKey}`,
          Accept: "application/json",
          "Content-Type": "application/json",
          "Intercom-Version": "2.11",
        },
      }
    );

    return response.data as { conversations: IntercomConversation[] };
  }
}

```

--------------------------------------------------------------------------------
/src/tools/conversations.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";
import { IntercomClient } from "../api/client.js";

export const GetConversationsSchema = z.object({
  startDate: z.string().optional(),
  endDate: z.string().optional(),
  customer: z.string().optional(),
  state: z.string().optional(),
});

export const SearchConversationsSchema = z.object({
  createdAt: z
    .object({
      operator: z.enum(["=", "!=", ">", "<"]),
      value: z.number(),
    })
    .optional(),
  updatedAt: z
    .object({
      operator: z.enum(["=", "!=", ">", "<"]),
      value: z.number(),
    })
    .optional(),
  sourceType: z.string().optional(),
  state: z.string().optional(),
  open: z.boolean().optional(),
  read: z.boolean().optional(),
  // Add more filters as needed
});

export async function searchConversations(
  args: z.infer<typeof SearchConversationsSchema>
) {
  const client = new IntercomClient();
  const params: Parameters<typeof client.searchConversations>[0] = {};

  if (args.createdAt) {
    params.createdAt = args.createdAt;
  }
  if (args.updatedAt) {
    params.updatedAt = args.updatedAt;
  }
  if (args.sourceType) {
    params.sourceType = args.sourceType;
  }
  if (args.state) {
    params.state = args.state;
  }
  if (args.open) {
    params.open = args.open;
  }
  if (args.read) {
    params.read = args.read;
  }

  const { conversations } = await client.searchConversations(params);

  return conversations.map((conv) => ({
    id: conv.id,
    created_at: new Date(conv.created_at * 1000).toISOString(),
    updated_at: new Date(conv.updated_at * 1000).toISOString(),
    state: conv.state,
    priority: conv.priority,
    contacts: conv.contacts.map((contact) => ({
      name: contact.name,
      id: contact.id,
      
    })),

  }));
}

export async function listConversationsFromLastWeek() {
  // Calculate last week's date range
  const client = new IntercomClient();
  const now = new Date();
  const lastWeekStart = new Date(now);
  lastWeekStart.setDate(now.getDate() - 7);
  lastWeekStart.setHours(0, 0, 0, 0);

  const lastWeekEnd = new Date(lastWeekStart);
  lastWeekEnd.setDate(lastWeekStart.getDate() + 6);
  lastWeekEnd.setHours(23, 59, 59, 999);

  // Convert to Unix timestamp (seconds)
  const startTimestamp = Math.floor(lastWeekStart.getTime() / 1000);
  const endTimestamp = Math.floor(lastWeekEnd.getTime() / 1000);

  return client.searchConversations({
    createdAt: {
      operator: ">",
      value: startTimestamp,
    },
    updatedAt: {
      operator: "<",
      value: endTimestamp,
    },
  });
}

// export async function getConversations(
//   args: z.infer<typeof GetConversationsSchema>
// ) {
//   const client = new IntercomClient();

//   const params: Parameters<typeof client.getConversations>[0] = {};

//   if (args.startDate) {
//     params.startDate = new Date(args.startDate);
//   }
//   if (args.endDate) {
//     params.endDate = new Date(args.endDate);
//   }
//   if (args.customer) {
//     params.customer = args.customer;
//   }
//   if (args.state) {
//     params.state = args.state;
//   }

//   const { conversations } = await client.getConversations(params);

//   return conversations.map((conv) => ({
//     id: conv.id,
//     created_at: new Date(conv.created_at * 1000).toISOString(),
//     updated_at: new Date(conv.updated_at * 1000).toISOString(),
//     state: conv.state,
//     priority: conv.priority,
//     contacts: conv.contacts.map((contact) => ({
//       name: contact.name,
//       id: contact.id,
//     })),
//     statistics: {
//       responses: conv.statistics.responses,
//       reopens: conv.statistics.reopens,
//     },
//   }));
// }

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { IntercomClient } from "./api/client.js";
import { SearchConversationsSchema, listConversationsFromLastWeek } from "./tools/conversations.js";
import { z } from "zod";
const server = new Server(
  {
    name: "intercom",
    version: "1.0.0",
  },
  {
    capabilities: {
      tools: {
        "search-conversations": {
          description:
            "Search Intercom conversations with filters for created_at, updated_at, source type, state, open, and read status",
          inputSchema: SearchConversationsSchema,
          outputSchema: z.any(),
        },
        "list-conversations-from-last-week": {
          description: "Fetch all conversations from the last week (last 7 days)",
          inputSchema: z.object({}),
          outputSchema: z.any(),
        },
      },
    },
  }
);

// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
  return {
    tools: [
      {
        name: "search-conversations",
        description:
          "Search Intercom conversations with filters for created_at, updated_at, source type, state, open, and read status",
        inputSchema: {
          type: "object",
          properties: {
            createdAt: {
              type: "object",
              properties: {
                operator: {
                  type: "string",
                  description: 'Operator for created_at (e.g., ">", "<", "=")',
                },
                value: {
                  type: "integer",
                  description: "Timestamp value for created_at filter",
                },
              },
            },
            updatedAt: {
              type: "object",
              properties: {
                operator: {
                  type: "string",
                  description: 'Operator for updated_at (e.g., ">", "<", "=")',
                },
                value: {
                  type: "integer",
                  description: "Timestamp value for updated_at filter",
                },
              },
            },
            sourceType: {
              type: "string",
              description:
                'Source type of the conversation (e.g., "email", "chat")',
            },
            state: {
              type: "string",
              description:
                'Conversation state to filter by (e.g., "open", "closed")',
            },
            open: {
              type: "boolean",
              description: "Filter by open status",
            },
            read: {
              type: "boolean",
              description: "Filter by read status",
            },
          },
        },
      },
      {
        name: "list-conversations-from-last-week",
        description: "Fetch all conversations from the last week (last 7 days)",
        inputSchema: {
          type: "object",
          properties: {},
        },
      },
    ],
  };
});

// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  if (name === "search-conversations") {
    try {
      const validatedArgs = SearchConversationsSchema.parse(args);
      const intercomClient = new IntercomClient();
      const conversations = await intercomClient.searchConversations(
        validatedArgs
      );

      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(conversations, null, 2),
          },
        ],
      };
    } catch (error) {
      if (error instanceof Error) {
        return {
          content: [
            {
              type: "text",
              text: `Error: ${error.message}`,
            },
          ],
        };
      }
      throw error;
    }
  }

  if (name === "list-conversations-from-last-week") {
    try {
      const conversations = await listConversationsFromLastWeek();
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(conversations, null, 2),
          },
        ],
      };
    } catch (error) {
      if (error instanceof Error) {
        return {
          content: [
            {
              type: "text",
              text: `Error: ${error.message}`,
            },
          ],
        };
      }
      throw error;
    }
  }

  throw new Error(`Unknown tool: ${name}`);
});

// Start the server
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Intercom MCP Server running on stdio");
}

main().catch((error) => {
  console.error("Fatal error:", error);
  process.exit(1);
});

```