MCP Server

Connect AI assistants to Front via the MCP server (open beta)

👍

Open beta

This feature is in open beta. Everyone has access, but bug fixes, improvements, and other details are still being worked on. Your feedback in our community group is invaluable for shaping the direction of the MCP server!

Front's MCP server (mcp.frontapp.com/mcp) lets AI agents act on Front conversation data using a single OAuth-authenticated endpoint. Agents authenticate as a specific Front user, with permissions matching that user's role (user-scoped authorization).

This article covers how to connect any MCP-compatible client to Front using an OAuth app you create yourself in the Front developer settings. For the user-facing overview, official directory listings (Claude, and others coming), and use case inspirations for Front teammates, see the general Help Center article on the MCP server.

Looking for examples before you build?

The MCP space in the Front Community has workflow walkthroughs, sample prompts, video demos, and a feedback channel where the team responds directly. If you want to see what's possible before you set anything up, start there.

Overview

Endpointhttps://mcp.frontapp.com/mcp
TransportStreamable HTTP
MCP spec version2025-11-25
AuthOAuth 2.1 + PKCE compatability

Note: Front does not support Dynamic Client Registration. Your AI assistant must support a confidential connection that sends an OAuth client ID and secret. Some popular AI assistants are currently not compatible.
Identity modelPer-user tokens — every tool call attributes to a specific Front teammate
Scopesread, write, send

The identity model is the part most worth internalizing: the agent's effective permissions are exactly the authorizing teammate's permissions. If the teammate can't see an inbox, the agent can't either.

Incompatability list

Some AI assistants do not support confidential connections. These clients cannot connect to the Front MCP server:

  • Codex

Use cases

A few patterns this surface unlocks:

  • Custom agent harnesses. Wire Front into Claude Code, Cursor, or an in-house orchestrator alongside your other tools (GitHub, Notion, Jira, Slack, Linear, your CRM) for cross-system workflows: customer reports a bug, agent reproduces it, identifies the suspect PR, and posts an internal comment with the diagnosis.
  • Multi-tenant partner integrations. Publish an OAuth app that any Front admin can install. Each end user authorizes individually.
  • Triage and routing agents. Scheduled or event-driven agents that search the inbox, classify by topic, assign to the right teammate, and draft a first-pass reply for human review.
  • Conversation insights and enrichment. Pull conversation data into your own pipelines for analysis, KB extraction, or feedback ingestion. The conversation timeline includes messages, comments, tags, assignment history, and more in a single stream.

Set up the MCP server connection

Connecting an AI assistant to Front's MCP server has two general steps: create a Front developer app, configure its OAuth scopes, and then connect your AI client using the app's credentials.

👍

After you complete these two steps, your teammates will be able to find the Front MCP server in your AI assistant and connect to it without having to create a developer app or configure the connection details on the AI client side.

1. Set up the Front developer app with an OAuth feature

  1. Create a developer app and give it an appropriate name (e.g. "Claude MCP Connector")
  2. Click Add feature in the Features tab.
  3. Add an OAuth feature to your developer app.
    1. Note the Client ID and Client secret.
    2. Add a redirect URL that corresponds to the AI assistant you're sending data to. For example, as of this writing, the Claude documentation instructs you to use https://claude.ai/api/mcp/auth_callback for most versions of Claude. If you're using Claude Code or similar CLI, you should enter http://localhost/callback. This URL will allow you to connect to a randomly generated local port.
    3. Enable MCP Server under Feature Access.
      📘

      Only enable the MCP Server option. Enabling other types of feature access can lead to 403 errors when you authorize through your AI assistant.

    4. Under Resource permissions, select the resources available to the MCP server.
      👍

      Request the narrowest set that covers your use case. A read-only research agent only needs read. A drafting copilot needs read + write but not send. Only request send if your agent will actually send messages.

      📘

      Remember that despite the permissions selected in the OAuth client, agents connecting to the MCP server will be further limited by the subset of data and permissions that the user who connects the agent has access to.


2. Connect your AI assistant

Once the app is configured, connect from your MCP client so that you and your team can view the Front MCP server. Once it is set up for your company, individual teammates can find the MCP server in the list of connectors and authorize it for their individual accounts, but they will not need to configure the technical details involving the server URL and credentials, as explained in this section.

The exact configuration syntax for initial configuration varies by AI client, but every client will need:

  • The MCP server URL: https://mcp.frontapp.com/mcp
  • Your app's client_id and client_secret
  • The OAuth scopes the app should request
📘

Use the UI or command line to connect the MCP server

We recommend using your AI assistants UI menus to connect the Front MCP server, or terminal commands for CLI assistants. Configuring JSON files is not recommended for most users because varying local paths and environment settings can cause issues. We've provided two recommended examples for Claude below, plus a general JSON shape if you have to modify your AI assistant's configuration file directly.

If you have issues connecting the MCP server, please reach out to your AI assistant's support team and share this documentation with them so they can recommend the correct steps.

Example for Claude (Web and Desktop)

  1. Open Settings and navigate to the Connectors tab (or the Customize page).
  2. Click + and then Add custom connector.
  3. Enter the server URL: https://mcp.frontapp.com/mcp
  4. Click Advanced settings and enter your OAuth Client ID and Client Secret.
  5. Click Add and complete the OAuth authentication flow.
📘

You may have to close out Claude and re-open it before it sees the new MCP server and has you authorize permissions.

Example for Claude Code

  1. In Front, make sure your OAuth feature contains the http://localhost/callback redirect URL.
  2. In your terminal, run the following command and enter your client secret when prompted:
    📘

    Including MCP_CLIENT_SECRET is optional. If you don't include it, Claude should prompt you for it after. If it fails to do so, then include it inline.

    👍

    You can name the MCP server something other than front if you desire.

    MCP_CLIENT_SECRET=<your-client-secret> claude mcp add --transport http --client-id <your-client-id> --client-secret front https://mcp.frontapp.com/mcp
  3. Exit Claude and re-open it in your terminal so that it pulls the latest list of servers.
  4. Run /mcp to see a list of MCP servers.
  5. Select the server you added and choose to Authenticate it.
  6. Authorize in your browser.

Example for generic MCP server config (confirm with your client's docs on exactly how you structure and pass these properties) - Not recommended for most users

{
  "mcpServers": {
    "front": {
      "url": "https://mcp.frontapp.com/mcp",
      "transport": "streamable-http",
      "oauth": {
        "client_id": "<your-client-id>",
        "client_secret": "<your-client-secret>",
        "scopes": ["read", "write", "send"]
      }
    }
  }
}

Tips for authorization

📘

Match your client's requested scopes to your OAuth app. By default, many AI assistants (including Claude) will request every scope the MCP server supports. If those scopes exceed what you enabled on the Front OAuth app, the authorization flow may return an error.

If you encounter this error, configure your client to only request the scopes your app is set up for. In Claude, for example, this is set in the client's JSON config — see Claude's docs on restricting OAuth scopes for the exact syntax. Other clients have similar settings; check their documentation if you hit an authorization error.

When the client first connects, each user will be redirected to Front to log in and consent to the requested scopes. The resulting token is bound to that specific Front user and they can only act on data their Front role allows.

📘

A note on enterprise deployment: If your AI assistant is centrally managed (e.g. Claude Enterprise, ChatGPT Enterprise), your IT team may need to allow the connection or distribute the app credentials. Each end user still completes the OAuth consent flow individually — this is how Front guarantees that every tool call has a real, attributable user behind it.

Rate limits

The MCP server uses separate rate limits from the Core API and other Front platform features, so you don't need to worry about stability issues for your existing integrations. We will publish more specifics on rate limits once the MCP server exits beta.

Tool reference

📘

Please note that the tools are still under development for the beta. The exact list, names, and descriptions are subject to change as we evolve them in response to feedback and further development.

📘

The MCP server currently exposes the following tools. To get the most updated list, ask your AI agent to refresh the tool list.

All write tools that mutate state in user-visible ways carry destructiveHint: true, which prompts MCP clients to request per-call user confirmation.


Conversations

ToolTypeDescriptionFlags
search_conversationsReadFull-text and filter search across conversationsquery — full-text search string (optional if at least one filter is provided)
scopemy_conversations (default), my_workspace, or all_inboxes; must set to all_inboxes for unassigned conversations to appear
filters.inboxId — restrict to inbox inb_xxx
filters.teammateId — restrict to assigned teammate tea_xxx
filters.teamId — restrict to team tim_xxx
filters.tags — array of tag IDs tag_xxx (matches all)
filters.status — one of open, archived, trashed, spam
filters.after / filters.before — date range YYYY-MM-DD
cursor — pagination cursor from previous call
read_conversationReadFull timeline of a conversation, its contents, and its header information: subject, message content, internal comments, tag/assignment changes, status transitions, rule actions, and other metadataconversationId (required) — public conversation ID cnv_xxx
limit — max timeline entries to return (default 50, max 200)
cursor — pagination cursor for timeline entries
create_draftWriteCreates a draft reply on an existing conversation, or a new outbound conversation. Does not send.body (required) — draft body text
conversationId — public conversation ID cnv_xxx for a reply draft; omit to create a new outbound draft (requires channelId)
channelId — channel ID cha_xxx; required for new outbound drafts, optional override for reply drafts
bodyFormathtml (default), markdown, or plain
inReplyToMessageId — pin to specific message; defaults to latest
replyAll — reply to all original recipients (default true)
shared — share with teammates (default false)
to — recipients for new outbound drafts (channel handles)
subject — subject line for new outbound email drafts
cc / bcc — CC/BCC recipients for new outbound email drafts
update_draftWriteUpdates the body, subject, or recipients of an existing draft. Uses version token for conflict detection.draftId (required) — public draft message ID msg_xxx from list_drafts or read_message
version (required) — opaque version token from read_message.draft_version; fails if draft changed since read
body — new draft body; omit to leave unchanged
bodyFormathtml (default), markdown, or plain
subject — new subject for email drafts; omit to leave unchanged
to / cc / bcc — replace recipient lists; pass [] to clear
channelId — override the sending channel cha_xxx
takeOver — if true, claim authorship from another teammate before editing
delete_draftWriteDiscards an unsent draft owned by the authenticated teammate. Owner-only.draftId (required) — public draft message ID msg_xxx from list_drafts or read_message
version (required) — opaque version token from read_message.draft_version; fails if draft changed since read
send_messageSend
(destructive)
Sends a draft message created via create_draft. The draft must be in compose state, owned by the authenticated teammate, and have at least one recipient.draftId (required) — draft ID msg_xxx as returned by create_draft
update_conversation_statusWrite (destructive)Archives or reopens a conversation. Can also set a ticket status. If the requester is the assignee, the change is global; otherwise only their view is affected.conversationId (required) — public conversation ID cnv_xxx
statusopen or archived (provide either this or statusId, not both)
statusId — ticket-status tag sts_xxx from list_statuses; sets ticket status and updates open/archived state to match
assign_conversationWriteAssigns a conversation to a teammate, or unassigns itconversationId (required) — public conversation ID cnv_xxx
assigneeId (required) — teammate ID tea_xxx to assign, or null to unassign

Messages

ToolTypeDescriptionFlags
read_messageReadFetches a single message by ID with full content. Returns body (quoted replies stripped), recipients, attachments, author, draft status, and delivery error if applicable. Use this to get the full message body when read_conversation truncates content.messageId (required) — message ID msg_xxx
get_attachmentReadGets a single attachment on a message or comment. Returns metadata (filename, content type, size) plus a short-lived download URL. Attachment IDs come from read_message.attachmentId (required) — public attachment link ID fil_xxx, as returned by read_message

Comments

ToolTypeDescriptionFlags
add_commentWritePosts an internal comment on a conversation. Not visible to customers. Supports @mentions auto-resolved to teammate names.conversationId (required) — public conversation ID cnv_xxx
body (required) — comment body text; @mentions resolved automatically

Tags

ToolTypeDescriptionFlags
list_tagsReadLists all tags in the workspace, including hierarchyname_query — filter by tag name keywords
inbox_ids — filter to tags that apply to specific inboxes inb_xxx
all_inboxes — filter to tags that apply to all inboxes (true/false)
is_visible_in_conversation_lists — filter by list visibility
parent_tag_id — filter by parent tag tag_xxx for hierarchical tags
limit — max results (default 50, max 100)
offset — pagination offset
tag_conversationWriteAdds or removes tags on a conversation. Idempotent — safe to call multiple times.conversationId (required) — public conversation ID cnv_xxx
addTags — array of tag IDs tag_xxx to add
removeTags — array of tag IDs tag_xxx to remove

Contacts & Accounts

ToolTypeDescriptionFlags
search_contactsReadSearches contacts by name, email, or handlequery (required) — search string (name, email, or handle)
cursor — pagination cursor from previous call
read_contactReadReturns a full contact record including handles and linked accountscontactId (required) — public contact card ID crd_xxx
search_accountsReadSearches accounts (companies) by namename_query — filter by account name keywords
limit — max results (default 10, max 25)
offset — pagination offset
read_accountReadReturns the full account (company) recordaccount_id (required) — numeric account ID or public account ID acc_xxx

Organization

ToolTypeDescriptionFlags
list_inboxesReadLists inboxes accessible to the authenticated username_query — filter by inbox name keywords
access_modeeveryone or restricted
ai_enabled — filter to AI-enabled inboxes (true/false)
ticketing_enabled — filter to ticketing-enabled inboxes (true/false)
business_hours_enabled — filter by business hours status
global_rules_enabled — filter by company rules status
teammate_ids — filter to inboxes accessible by specific teammates
circle_ids — filter to inboxes accessible by specific teammate groups
limit — max results (default 25, max 50)
offset — pagination offset
list_channelsReadLists channels accessible to the authenticated user. Use to discover channel IDs before calling tools that require one.name_query — filter by channel display name or address keywords
inbox_ids — filter to channels belonging to specific inboxes inb_xxx
types — filter by channel type (e.g. email, chat, sms, gmail, twilio, etc.)
account_statuses — filter by channel account status (ok, quarantined, degraded, offline, deleted)
limit — max results (default 25, max 50)
offset — pagination offset
list_teammatesReadLists all teammates and their roles in the workspacename_query — search by name or email keywords
statuses — filter by account status: active, pending, pending_email, blocked (defaults to active statuses)
limit — max results (default 10, max 25)
offset — pagination offset
list_teamsReadLists all teams in the workspacename_query — filter by team name or alias keywords
limit — max results (default 10, max 25)
offset — pagination offset
list_statusesReadLists the company's ticket statuses. Returns empty when ticketing is not enabled.name_query — filter by status name keywords
limit — max results (default 50, max 100)
offset — pagination offset
list_draftsReadLists in-flight draft messages authored by the authenticated teammatelimit — max drafts to return (default 50, max 200)

Troubleshooting

401 Unauthorized on every request

The token is invalid, expired, or has been revoked. Common causes:

  • The user revoked the app's access in Front's connected apps settings.
  • A workspace admin uninstalled the OAuth app entirely.
  • The access token expired and the refresh flow didn't fire. Re-authenticate to mint a new token.

403 Forbidden on a tool that worked before

The token has scopes for some tools but not the one being called. For example, a read-scoped token will get 403 on add_comment. Re-authorize the user and request the broader scope set on consent.

This also fires if the authenticated Front user lost access to the relevant inbox, team, or resource between authorization and the tool call. Permissions are enforced at call time against the user's current Front role.

429 Too Many Requests

Rate limits are tiered per teammate per minute:

  • Light reads (list_*, get_*): 120/min
  • Heavy reads (search_conversations, read_conversation): 30/min
  • Writes (drafts, comments, tags, assignment, status, contacts): 20/min
  • Send (send_reply): 20/min

Additional caps apply at the per-teammate (1,000/hour) and per-workspace (10,000/hour) level. The response includes a Retry-After header and an x-ratelimit-tier header indicating which tier was exceeded.

If you're seeing rate-limit errors during normal operation, the most common cause is a polling loop that's tighter than it needs to be. Approval-by-polling loops should generally poll on the order of seconds-to-minutes, not sub-second.

Feedback

To share feedback during this beta, please join our community group and create a new post.