Skip to main content
Glama
mohnori

Chatwoot MCP Server

by mohnori
README.md
# Chatwoot MCP Server

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-green.svg)](https://nodejs.org)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org)

A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that connects AI assistants like Claude to your [Chatwoot](https://www.chatwoot.com) instance. Manage customer conversations, read messages, send replies, and filter by date ranges -- all through natural language.

## Features

- **5 MCP Tools** - List, filter, and inspect conversations; read and send messages
- **Advanced Filtering** - Filter conversations by date range, status, assignee, inbox, and labels
- **Dual Authentication** - API token (recommended) or JWT email/password
- **Type-Safe** - Built with TypeScript and OpenAPI-generated types
- **Dual Output** - Markdown (human-readable) and JSON (machine-readable) response formats

## Tools

| Tool | Description |
|------|-------------|
| `chatwoot_list_conversations` | List conversations with status, assignee, and inbox filters |
| `chatwoot_get_conversation` | Get full details for a specific conversation |
| `chatwoot_list_messages` | List all messages in a conversation |
| `chatwoot_create_message` | Send a reply or create an internal note |
| `chatwoot_filter_conversations` | Filter by date range, status, assignee, inbox, and labels |

## Quick Start

### Prerequisites

- [Node.js](https://nodejs.org) >= 18
- A Chatwoot account with API access

### Install

```bash
git clone https://github.com/mohnori/chatwoot-mcp.git
cd chatwoot-mcp
npm install
npm run build
```

### Configure

Copy the example environment file and fill in your credentials:

```bash
cp .env.example .env
```

Edit `.env`:

```bash
CHATWOOT_BASE_URL="https://your-chatwoot-instance.com"
CHATWOOT_API_TOKEN="your_api_token_here"
CHATWOOT_ACCOUNT_ID="your_account_id_here"
```

**Getting your API token:**
1. Log in to Chatwoot
2. Click your avatar → **Profile Settings**
3. Scroll to the bottom → copy your **Access Token**

**Getting your account ID:**
Your account ID is the number in the URL when you're logged in: `app.chatwoot.com/app/accounts/<ID>/...`

### Run

```bash
npm start
```

## MCP Configuration

### Claude Code

```bash
claude mcp add chatwoot \
  -e CHATWOOT_BASE_URL="https://your-chatwoot-instance.com" \
  -e CHATWOOT_API_TOKEN="your_api_token" \
  -e CHATWOOT_ACCOUNT_ID="your_account_id" \
  -- node /path/to/chatwoot-mcp/dist/index.js
```

> Replace `/path/to/chatwoot-mcp` with the actual path where you cloned the repository.

### Claude Desktop

Add to your Claude Desktop configuration file:

- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "chatwoot": {
      "command": "node",
      "args": ["/path/to/chatwoot-mcp/dist/index.js"],
      "env": {
        "CHATWOOT_BASE_URL": "https://your-chatwoot-instance.com",
        "CHATWOOT_API_TOKEN": "your_api_token_here",
        "CHATWOOT_ACCOUNT_ID": "your_account_id_here"
      }
    }
  }
}
```

> Replace `/path/to/chatwoot-mcp` with the actual path where you cloned the repository.
> On Windows, use double backslashes: `"C:\\Users\\you\\chatwoot-mcp\\dist\\index.js"`

### Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `CHATWOOT_BASE_URL` | Yes | Your Chatwoot instance URL (no trailing slash) |
| `CHATWOOT_ACCOUNT_ID` | Yes | Your Chatwoot account ID (numeric) |
| `CHATWOOT_API_TOKEN` | Yes* | API access token (recommended method) |
| `CHATWOOT_EMAIL` | Yes* | Email for JWT auth (alternative method) |
| `CHATWOOT_PASSWORD` | No | Password for JWT auth (needed for token refresh) |

*Either `CHATWOOT_API_TOKEN` or `CHATWOOT_EMAIL` is required.

## Tools Reference

### chatwoot_list_conversations

List conversations from an account with optional filters.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `status` | string | No | `"open"` | `"open"`, `"resolved"`, `"pending"`, `"snoozed"`, or `"all"` |
| `assignee_type` | string | No | - | `"me"`, `"unassigned"`, or `"all"` |
| `inbox_id` | number | No | - | Filter by inbox ID |
| `page` | number | No | `1` | Page number |
| `response_format` | string | No | `"markdown"` | `"markdown"` or `"json"` |

### chatwoot_get_conversation

Get detailed information about a specific conversation.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `conversation_id` | number | Yes | Conversation ID |
| `response_format` | string | No | `"markdown"` or `"json"` |

### chatwoot_list_messages

List all messages in a conversation.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `conversation_id` | number | Yes | Conversation ID |
| `response_format` | string | No | `"markdown"` or `"json"` |

### chatwoot_create_message

Send a message or create an internal note in a conversation.

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `conversation_id` | number | Yes | - | Conversation ID |
| `content` | string | Yes | - | Message content |
| `message_type` | string | No | `"outgoing"` | `"outgoing"` or `"incoming"` |
| `private` | boolean | No | `false` | `true` for internal notes |

### chatwoot_filter_conversations

Filter conversations using advanced criteria. Uses Chatwoot's POST filter API with support for date ranges, status, assignee, inbox, and labels. Returns 25 results per page.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `date_from` | string | No | Created after this date (YYYY-MM-DD) |
| `date_to` | string | No | Created before this date (YYYY-MM-DD) |
| `activity_from` | string | No | Last activity after this date (YYYY-MM-DD) |
| `activity_to` | string | No | Last activity before this date (YYYY-MM-DD) |
| `status` | string | No | `"open"`, `"resolved"`, `"pending"`, or `"snoozed"` |
| `assignee_id` | number | No | Filter by assignee agent ID |
| `inbox_id` | number | No | Filter by inbox ID |
| `label` | string | No | Filter by label name |
| `page` | number | No | Page number (default: 1) |
| `response_format` | string | No | `"markdown"` or `"json"` |

**Date filter behavior:** Date boundaries are exclusive. To get conversations for a single day like Feb 21, use `date_from="2026-02-20"` and `date_to="2026-02-22"`.

## Example Queries

Once connected, you can ask Claude things like:

- "Show me all open conversations"
- "What are the details of conversation #123?"
- "List all messages in conversation #456"
- "Send a reply to conversation #789 saying 'Thank you for contacting us!'"
- "Add an internal note to conversation #101 about the customer's issue"
- "Find all conversations created on Feb 21"
- "Show me resolved conversations from last week with the label 'urgent'"

## Development

### Project Structure

```
chatwoot-mcp/
├── src/
│   ├── index.ts                  # MCP server entry point & tool registration
│   ├── constants.ts              # Shared constants and enums
│   ├── chatwoot-types.ts         # Auto-generated OpenAPI types
│   ├── services/
│   │   ├── chatwoot-client.ts    # API client with auth middleware
│   │   ├── chatwoot-auth.ts      # JWT authentication
│   │   ├── token-cache.ts        # JWT token persistence
│   │   └── error-handler.ts      # Error formatting utilities
│   ├── schemas/
│   │   └── common.ts             # Shared Zod validation schemas
│   └── tools/
│       ├── conversations.ts      # List & get conversation tools
│       ├── messages.ts           # List & create message tools
│       └── filter-conversations.ts  # Advanced conversation filtering
├── test/
│   ├── chatwoot-client.test.ts   # Integration tests
│   ├── setup.ts                  # Test environment setup
│   └── unit/                     # Unit tests (mocked, no API calls)
│       ├── error-handler.test.ts
│       ├── build-filter-payload.test.ts
│       ├── conversations.test.ts
│       ├── messages.test.ts
│       ├── filter-conversations.test.ts
│       ├── schemas.test.ts
│       ├── chatwoot-auth.test.ts
│       └── token-cache.test.ts
├── .env.example                  # Environment variable template
├── package.json
├── tsconfig.json
└── vitest.config.ts
```

### Scripts

| Command | Description |
|---------|-------------|
| `npm run build` | Compile TypeScript to `dist/` |
| `npm start` | Run the compiled server |
| `npm run dev` | Run in development mode with auto-reload |
| `npm test` | Run integration tests |
| `npm run test:watch` | Run tests in watch mode |
| `npm run clean` | Remove the `dist/` directory |
| `npm run generate-types` | Regenerate OpenAPI types from swagger.json |

### Regenerating Types

If the Chatwoot API changes, download the latest OpenAPI spec and regenerate types:

1. Download `swagger.json` from your Chatwoot instance at `/swagger/v1/swagger.json`
2. Place it in the project root
3. Run `npm run generate-types`

### Running Tests

Unit tests run without any credentials. Integration tests require a real Chatwoot instance:

```bash
# Unit tests only (no credentials needed)
npm test -- test/unit/

# All tests (requires .env with valid credentials)
cp .env.example .env
# Edit .env with your credentials
npm test
```

## Troubleshooting

### API Token Returns 401

If using a self-hosted Chatwoot with nginx, add this to your nginx config:

```nginx
server {
    underscores_in_headers on;  # Required for api_access_token header
}
```

### JWT Tokens Expire

JWT tokens expire (typically after 2 weeks). Keep `CHATWOOT_PASSWORD` in your `.env` for automatic refresh, or switch to API token authentication.

### MCP Connection Errors

If you see JSON parsing errors when connecting, ensure you are using `node dist/index.js` directly (not `npx`). Some npm packages output debug information to stdout during installation, which corrupts the MCP stdio protocol.

## Tech Stack

- **[@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk)** - MCP server framework
- **[openapi-fetch](https://github.com/openapi-ts/openapi-typescript)** - Type-safe API client
- **[openapi-typescript](https://github.com/openapi-ts/openapi-typescript)** - OpenAPI type generation
- **[Zod](https://github.com/colinhacks/zod)** - Runtime schema validation
- **[Vitest](https://vitest.dev)** - Test framework

## Attribution

Originally created by [Hugo Blanc](https://github.com/hugoblanc/chatwoot-mcp).

## License

[MIT](LICENSE)

## Contributing

Contributions are welcome! Some ideas for additional tools:

- Contact management (create, update, search)
- Team and agent operations
- Inbox configuration
- Labels and custom attributes
- Reports and analytics

TDQS

A3.9/5.0

Scored across 5 tools

Disambiguation3/5

list_conversations and filter_conversations overlap significantly; both retrieve conversations with filtering, though filter adds date/label/advanced criteria. The other three tools (get_conversation, list_messages, create_message) are clearly distinct.

Naming Consistency5/5

All tools follow a consistent chatwoot_ verb_noun pattern in snake_case: list_conversations, get_conversation, list_messages, create_message, filter_conversations. No style mixing or irregularities.

Tool Count4/5

Five tools is a reasonable size for a focused Chatwoot conversation/message server. It feels slightly thin compared to the full platform scope, but each tool has a distinct role and the count is appropriate for a targeted integration.

Completeness3/5

Core read and send workflows are covered (list/get conversations, list/create messages), but there are notable gaps: no way to update conversation status, assignee, or labels, and no create conversation or contact management. The surface handles common support tasks but lacks lifecycle management.

Maintenance

ActivityInactive
ResponsivenessNo issues