Skip to main content
Glama
FirstReply MCP Server

FirstReply MCP Server

Official
by FirstReply
README.md
# FirstReply MCP Server

MCP ([Model Context Protocol](https://modelcontextprotocol.io)) server that exposes
[FirstReply](https://firstreply.ai) customer support operations as tools for AI
assistants like Claude. It lets an assistant browse and answer customer
conversations, look up contacts, search and maintain the knowledge base, draft
and translate replies, draft and schedule social media posts, manage
classifications, templates, prompts, webhooks and saved filters, pull support
and publishing statistics and manage organization API keys, all
scoped and permission-checked by the FirstReply REST API.

The server speaks MCP protocol `2026-07-28` and the earlier `2025-*` revisions
over stdio and HTTP, built on the v2 MCP TypeScript SDK.

## Tools

Tools are named `resource_action`. Every organization-scoped tool takes an
`organizationId` (see `organization_list`). Tools that send something to a
customer or delete data are annotated as destructive.

### Organization, members & API keys

| Tool | Description |
|------|-------------|
| `organization_list` | List accessible organizations |
| `organization_get` | Get an organization with its settings and AI prompts |
| `organization_update` | Update name, language, auto-close and the custom AI prompts |
| `organization_usage` | Plan, trial state and inbox / member counts against the limits |
| `member_list` | List organization members |
| `member_get` | Get one member |
| `member_create` | Add a member by email with a permission level |
| `member_update` | Change a member's permission level |
| `member_delete` | Remove a member (destructive) |
| `api_key_list` | List the organization's named API keys (metadata only) |
| `api_key_create` | Create a named API key; plaintext returned once |
| `api_key_delete` | Delete (revoke) an API key (destructive) |

### Inboxes

| Tool | Description |
|------|-------------|
| `inbox_list` | List the inboxes (email, WhatsApp, Messenger, Instagram, LinkedIn, Google Play, App Store, website chat) and whether Publishing is on |
| `inbox_get` | Get one inbox |
| `inbox_statistics` | Statistics per inbox |
| `inbox_refresh` | Fetch new messages from an inbox now |
| `inbox_test` | Test the connection of an inbox to its platform |

### Conversations & messages

| Tool | Description |
|------|-------------|
| `conversation_list` | List / filter / search conversations (status, inbox, member, contact, classification, language, spam, priority, date ranges, search operators) |
| `conversation_get` | Get one conversation |
| `conversation_thread` | Get a conversation with all of its messages |
| `conversation_messages` | List the messages of a conversation |
| `conversation_reply` | Send a reply to the customer, or store an internal note or a draft (destructive) |
| `conversation_close` | Close a conversation |
| `conversation_read` | Mark a conversation as read |
| `conversation_spam` | Mark a conversation as spam or not spam |
| `conversation_update` | Assign, reprioritize, reclassify, rename, change language, contact or spam flag |
| `conversation_delete` | Delete a conversation (destructive) |
| `conversation_stats` | Conversation statistics for a date range |
| `conversation_stats_by_member` | Statistics per member for a date range |
| `conversation_metrics` | Response and resolution metrics for a date range |
| `conversation_totals` | Current open / unread totals |
| `message_get` | Get one message |
| `message_update` | Edit a draft or internal note |
| `message_send` | Send a draft (destructive) |
| `message_delete` | Delete a message (destructive) |

The `search` argument of `conversation_list`, `search` and the saved filters
understands the FirstReply search syntax: words are AND-ed and prefix matched,
`"quoted phrases"`, `-word`, and the operators `from:`, `subject:`, `is:`
(`open`, `closed`, `unread`, `read`, `assigned`, `unassigned`, `ai`), `has:`
(`attachment`, `draft`), `before:` and `after:` (`YYYY-MM-DD`).

### Contacts

| Tool | Description |
|------|-------------|
| `contact_list` | List / search contacts |
| `contact_get` | Get one contact with custom fields |
| `contact_create` | Create a contact |
| `contact_update` | Update a contact |
| `contact_delete` | Delete a contact (destructive) |
| `contact_field_list` | List the custom contact fields |

### Social posts

| Tool | Description |
|------|-------------|
| `post_list` | List / filter posts (status, channel, period, text); `order: asc` for upcoming posts |
| `post_get` | Get a post with its channels, publish state and latest metrics |
| `post_metrics` | Metric snapshots of every channel of a published post |
| `post_comments` | The comment threads on a post, as conversations |
| `post_write` | Write, shorten or rewrite a post text with AI, for a platform and a character limit; saves nothing |
| `post_create` | Save a post as a draft |
| `post_schedule` | Schedule a new post or an existing draft for a time on the given channels |
| `post_reschedule` | Move a post to another time; text, channels and media stay |
| `post_analytics` | Totals, daily series, top posts and channels for a period |

Posts target inboxes with Publishing on (`publishingEnabled` in `inbox_list`).
There is no tool to publish right away, approve, retry or delete a post: a
public post cannot be taken back, so the assistant drafts or schedules and a
person can still change it. Scheduled times have to be at least 10 minutes
ahead. When the organization requires approval, a member's scheduled post
waits for a manager (status `PENDING_APPROVAL`); such a post is not
rescheduled with manager rights (an API key acts as the owner), since saving
it would approve it. Comments are only read: hiding, deleting or liking a
comment and answering it privately act publicly on the platform and stay in
FirstReply.

### Classifications

| Tool | Description |
|------|-------------|
| `classification_list` | List classifications with their automation settings |
| `classification_get` | Get one classification |
| `classification_create` | Create a classification |
| `classification_update` | Update a classification (unchanged settings are kept) |
| `classification_delete` | Delete a classification, optionally moving its conversations (destructive) |
| `classification_impact` | Preview what deleting a classification affects |
| `classification_statistics` | Conversation counts per classification |

### Knowledge base

| Tool | Description |
|------|-------------|
| `knowledge_search` | Search or browse knowledge entries |
| `knowledge_get` | Get one knowledge entry |
| `knowledge_create` | Add a question / answer entry |
| `knowledge_update` | Update a knowledge entry |
| `knowledge_delete` | Delete a knowledge entry (destructive) |
| `knowledge_source_list` | List synced knowledge sources with their sync state |
| `knowledge_source_get` | Get one knowledge source |
| `knowledge_source_sync` | Start a sync now |
| `knowledge_source_status` | Sync status of a source |

Knowledge source configurations hold credentials and are never returned.

### AI & search

| Tool | Description |
|------|-------------|
| `ai_draft_reply` | Generate an AI reply draft for a conversation without sending it |
| `ai_prompt` | Run a free-form prompt with the organization's persona and knowledge |
| `ai_translate` | Translate a text, e.g. a reply into the customer's language |
| `ai_test` | Simulate an incoming message: classification, spam verdict and reply |
| `search` | Global search across conversations, contacts, knowledge and messages |

### Templates, prompts, webhooks & filters

| Tool | Description |
|------|-------------|
| `template_list` / `template_get` / `template_create` / `template_update` / `template_delete` | Reply templates and their language variants |
| `prompt_list` / `prompt_get` / `prompt_create` / `prompt_update` / `prompt_delete` | Saved AI prompts |
| `webhook_list` / `webhook_get` / `webhook_create` / `webhook_update` / `webhook_delete` | Webhooks and their events (including `post.create`, `post.update`, `post.delete`) |
| `webhook_test` | Send a test event to a webhook |
| `webhook_rotate_secret` | Generate a new signing secret (returned once) |
| `filter_list` / `filter_get` / `filter_create` / `filter_update` / `filter_delete` | Saved conversation filters |

Not covered, by design: authentication and profile endpoints, inbox creation and
OAuth connections, knowledge source creation, billing, attachments and CSV
import / export, publishing posts right away, approving posts, media
uploads, comment moderation and private replies to comments. Those stay in the
FirstReply web app.

## Requirements

- Node.js >= 20
- A FirstReply organization API key

## Authentication

The server authenticates against the FirstReply REST API with an
**organization API key** (`fr_...`), created in FirstReply under
Organization → Development → API Keys. Organizations support multiple named
keys with optional expiry, so it is best to create a dedicated key for this
MCP server. The key is sent as `Authorization: Bearer`.

API key requests run with the permissions of the organization owner: treat
the key as a secret and rotate it by creating a new key and deleting the old
one.

A key only reaches its own organization: `organization_list` returns just
that organization, and requests for any other organization are refused with
403. To work across several organizations, configure one server per key.

## Usage (stdio, e.g. Claude Desktop)

```json
{
  "mcpServers": {
    "firstreply": {
      "command": "npx",
      "args": ["-y", "@firstreply/mcp"],
      "env": {
        "FIRSTREPLY_API_TOKEN": "fr_..."
      }
    }
  }
}
```

### Claude Code

```bash
claude mcp add firstreply --env FIRSTREPLY_API_TOKEN=fr_... -- npx -y @firstreply/mcp
```

## Usage (HTTP)

```bash
npm run build && npm run start:http   # listens on :8080
```

Stateless: every request gets its own server instance, 2026-07-28 requests
are answered directly and 2025-era clients over a stateless streamable-HTTP
transport. Every request must carry `Authorization: Bearer fr_...`. A
`/health` endpoint is provided for probes.

## Environment variables

| Variable | Description |
|----------|-------------|
| `FIRSTREPLY_API_TOKEN` | Organization API key (stdio mode) |
| `FIRSTREPLY_API_URL` | API base URL (default `https://api.firstreply.ai`) |
| `PORT` | HTTP port (HTTP mode, default 8080) |
| `MCP_ENDPOINT_PATH` | HTTP endpoint path (default `/`) |

## Development

```bash
npm install
npm run typecheck
npm test          # builds and runs node:test suites
npm run build     # compiles TypeScript to dist/
```

### Project structure

```
src/
├── index.ts              # stdio entry point
├── http-server.ts        # HTTP entry point (Express + createMcpHandler)
├── server.ts             # MCP server factory (McpServer + registerTool)
├── tools.ts              # loads the tool modules, exposes the registry
├── tools/registry.ts     # add() builder, schema helpers, dispatch
├── tools/<resource>.ts   # one module per resource (conversations, contacts, ...)
└── client.ts             # minimal FirstReply REST client
test/
├── tools.test.ts         # tool definition checks and handler tests against a fake client
└── protocol.test.ts      # stdio and HTTP end to end, both protocol eras, against an API stub
```

Adding a tool: call `add(name, description, zodShape, annotations, handler)`
in the matching `src/tools/<resource>.ts`. The zod shape is validated by the
SDK and published to clients as JSON Schema; every argument needs a
description (the tests check).

TDQS

B3.1/5.0

Scored across 84 tools

Disambiguation4/5

Tools generally target distinct resource-action pairs, and descriptions clarify overlaps like conversation_reply vs message_send. However, some pairs (conversation_thread vs conversation_get + conversation_messages, search vs knowledge_search) could cause hesitation. Overall mostly distinct.

Naming Consistency5/5

All tool names follow a consistent snake_case resource_action pattern (e.g., conversation_list, webhook_create, knowledge_source_sync). There are no mixed conventions, making the set predictable and easy to navigate.

Tool Count1/5

84 tools is an extreme mismatch for an MCP server, as the sheer volume will overwhelm an agent's context and decision-making. According to the calibration, 50+ tools merits the lowest score despite the platform's breadth.

Completeness3/5

Core workflows for conversations, contacts, classifications, templates, prompts, and webhooks have full CRUD. However, key lifecycle operations are missing for inboxes (no create/update/delete) and knowledge sources (no create/update/delete), creating notable dead ends for administrative tasks.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues