Announcing CIMD support for MCP Client registration
Learn more

Intercom MCP vs Intercom API for AI Agents (2026)

TL;DR

  • The Intercom MCP server ships 13 tools and is read-heavy. It covers search and retrieval for conversations, contacts, and companies, plus full read and write for Help Center articles. Replying, assigning, closing, tagging, and ticket creation are absent from the MCP surface; those are REST-only.
  • Intercom MCP auth is not OAuth-only. It accepts both an OAuth flow and a static Bearer access token, so the usual "MCP can't run headless" claim does not hold here. The harder MCP constraints are capability and region: the MCP server is limited to US-hosted workspaces.
  • Intercom OAuth issues no refresh tokens, and access tokens do not expire on a schedule. They stay valid until a teammate revokes access or the app deauthorizes. The credential problem is therefore revocation and isolation, not refresh.
  • For multi-tenant B2B agents, both paths give you one credential per customer Intercom workspace. Neither path stores, isolates, or revokes those credentials for you. That is infrastructure regardless of which path you choose.
  • Scalekit's Intercom connector vaults the per-teammate credential, resolves it server-side on every call, enforces scope before the request, and logs every action for 90 days. It also exposes the action tools (reply, assign, close) the official MCP server does not.

Your support agent needs to read and act on Intercom. Intercom ships both a hosted MCP server at mcp.intercom.com and a REST API at api.intercom.io. They are not the same surface: different tool coverage, different auth options, different operational constraints in production. The obvious read on MCP (faster, simpler, do everything) is wrong for Intercom specifically, and the reasons are worth knowing before you commit engineering time. Here is how to pick.

What Intercom MCP and Intercom API actually are

Intercom MCP

Intercom hosts a remote MCP server at https://mcp.intercom.com/mcp over Streamable HTTP, with a deprecated legacy SSE endpoint maintained for backward compatibility. It is built and run by Intercom on Cloudflare infrastructure, and it was introduced in September 2025. One regional constraint matters up front: the MCP server is currently supported only in US-hosted Intercom workspaces. Authentication accepts an OAuth flow (recommended) or a Bearer access token passed in the Authorization header.

Official docs: developers.intercom.com/docs/guides/mcp

Intercom API

The Intercom REST API is the full platform surface at api.intercom.io, versioned by date and number through the Intercom-Version header. It exposes conversations, tickets, contacts, companies, tags, articles, data events, data export, webhook subscriptions, admins, custom objects, and the Fin Agent API. Authentication is a Bearer token in the Authorization header, supplied as either a workspace Access Token (for your own workspace) or an OAuth access token (for other workspaces).

Official docs: developers.intercom.com/docs/references/rest-api/api.intercom.io

Comparing them where it matters for agents

What your agent can actually do

The MCP server is solid at the conversational read path: searching across conversations and contacts, pulling a single conversation with all its parts, resolving companies, and managing Help Center content. For an agent whose job is to find context and answer questions, that surface is enough. The gap appears the moment the agent needs to act on a conversation or touch anything operational.

Capability
Intercom MCP
Intercom REST API
Search conversations and contacts
Yes
Yes
Read a single conversation or contact
Yes
Yes
Reply to a conversation (public reply or private note)
No
Yes
Assign, snooze, close, or reopen a conversation
No
Yes
Tag or untag a conversation or contact
No
Yes
Create or update a contact
No, read only
Yes
Create or update a company
No, read only
Yes
Create, update, or search tickets
No
Yes
Convert a conversation to a ticket
No
Yes
List, search, create, and update Help Center articles
Yes
Yes
Subscribe to change events through webhooks
No
Yes
Export conversation and message data in bulk
No
Yes
Region availability
US workspaces only
All regions

What the MCP server actually ships, from Intercom's own docs

Because aggregator sites disagree on the count (you will see four, six, and other numbers), here is the authoritative list. Intercom's MCP documentation lists 13 tools. Two are universal, eleven are direct API tools.

The universal tools are search (a query DSL across object_type:conversations or object_type:contacts) and fetch (full detail for a conversation, contact, or company ID).

The direct tools are search_conversations, get_conversation, search_contacts, get_contact, list_companies, get_company, list_articles, search_articles, get_article, create_article, and update_article.

What is not in that list

Read the inventory by what it omits. Conversations, contacts, and companies are read-only through MCP. The only write tools are create_article and update_article, both scoped to the Help Center. There is no reply, no assign or close, no tag, no contact or company write, no ticket tool of any kind, no event subscription, and no data export. A support agent that resolves tickets, replies to customers, or routes conversations cannot do those jobs through the official MCP server. It can read, and it can author knowledge base articles.

The auth path each one puts you on

Intercom does something its peers do not: the MCP server accepts both an OAuth flow and a static Bearer token. So the common "MCP forces an interactive browser flow, which blocks background agents" argument is simply not true for Intercom. A headless agent can drive the MCP server with a workspace access token in the Authorization header.

The REST API uses the same Bearer model, with two token sources. A workspace Access Token, created in the Developer Hub, is for private apps acting on your own workspace; no consent flow is involved. An OAuth access token, issued through the Authorization Code flow, is for public apps acting on other people's workspaces, which is the multi-tenant B2B case.

The token lifecycle nobody expects

Here is the Intercom-specific detail that changes your production design: Intercom's OAuth implementation has no refresh tokens, and access tokens do not expire on a fixed schedule. A token stays valid until the authorizing teammate revokes it in Intercom, or the app deauthorizes itself.

That inverts the usual agent-auth worry. You are not fighting 401s and refresh races. You are holding a long-lived credential that keeps working until someone explicitly kills it. A non-expiring token is a larger standing liability, not a smaller one, because the failure mode is silent over-retention rather than noisy expiry. Your agent needs to detect revocation, isolate tokens per tenant, and have a clean way to invalidate them. None of that comes from the protocol.

What you own in production

Intercom runs the MCP server, maintains its tool schemas, and normalizes the endpoints behind it. You still own token storage, revocation detection, tenant isolation, and the US-region constraint. When Intercom updates the hosted server, the tool schemas can change without a versioning contract, so an agent built against a specific tool signature may need attention after an upstream change.

On the REST API path, you own everything: endpoint selection, request construction, pagination, error handling, and the token lifecycle. The tradeoff is stability. The REST API is explicitly versioned through the Intercom-Version header, so you pin a version and migrate on your own schedule. For a deterministic pipeline where an unannounced schema change is an incident, that control matters.

Rate limits are a real constraint at agent volume

Intercom's published defaults for private apps are 10,000 API calls per minute per app and 25,000 API calls per minute per workspace. The detail that breaks naive implementations: the per-minute budget is distributed across 10-second windows, so a default of 10,000 per minute is roughly 1,666 operations per 10-second window. A burst during an initial sync blows past the window and starts returning 429s even though you are well under the per-minute figure. MCP tool calls and direct REST calls both consume this budget; the protocol you pick does not change the math.

When to use MCP, when to use the API

Use Intercom MCP when:

  • Your agent is interactive and user-present, for example a teammate querying conversations and customer context through Claude or another MCP host
  • The job is read and retrieval: searching conversations, pulling history, resolving contacts and companies, or authoring Help Center articles
  • Your workspaces are US-hosted and the region restriction is not a blocker
  • You want Intercom to maintain the tool schemas and you do not want to write API integration code for a read-only assistant

Use the Intercom REST API when:

  • Your agent acts: replying, assigning, closing, snoozing, tagging, creating or updating tickets, or writing contacts and companies
  • Your agent must react to changes through webhooks, or export conversation data in bulk
  • You operate in non-US regions where the MCP server is unavailable
  • You are building a deterministic pipeline and need explicit version control over the contract you depend on

The credential problem that exists on both paths

Whichever path you choose, every customer in a multi-tenant agent has their own Intercom credential. Fifty customers, fifty workspace tokens, fifty lifecycles to manage. The MCP OAuth flow gives you a token per authorizing teammate. The REST API's OAuth flow gives you the same. In both cases, the token has to live somewhere: encrypted at rest, isolated per tenant, and revocable when a teammate disconnects or leaves.

Neither path solves this for you. And Intercom's no-expiry model raises the stakes, because a token you forgot about does not lapse on its own; it keeps working. Your agent has no built-in way to know a teammate revoked access unless you watch for the failure and handle it.

Scalekit's Intercom connector handles the OAuth flow, vaulted per-tenant token storage, and revocation handling for both paths, so the MCP versus API decision does not change your auth infrastructure.

Building the Intercom agent with Scalekit

Token Vault and per-workspace isolation

Scalekit stores each authorizing teammate's Intercom credential in an AES-256 token vault, namespaced per tenant. The credential is resolved server-side at request time and never enters your agent runtime or the LLM context. Tenant A's token is unreachable by an agent acting for tenant B on the same connection. When a teammate revokes Intercom access, the connection fails closed on the next call, and the event is recorded. Because Intercom tokens do not expire, vaulted storage plus revocation handling is the actual lifecycle requirement here, not refresh.

Scoped tool calling, including the actions the MCP server lacks

Scalekit's Intercom connector is built on the REST API, so it exposes the action surface the official MCP server omits: intercom_conversation_reply, intercom_conversation_assign, conversation close, private notes, alongside intercom_conversations_list, intercom_conversation_get, intercom_contacts_search, and intercom_contact_get. The agent receives only the tools the authorizing teammate is permitted to call, not a flat catalog. What the teammate cannot do in Intercom, the agent cannot do either, because Intercom enforces teammate permissions and assignment rules on the resolved token.

Why a scoped surface matters

Tool bloat is an accuracy problem and a cost problem. LLMs select poorly when handed every available tool, and every tool in context consumes tokens before the agent does any work. list_scoped_tools returns the authorized subset for this teammate, this connection, this task. The fix is not better prompting. It is surface reduction.

Connect a user and set the connection

Register the Intercom connection once in the Scalekit dashboard under AgentKit, Connections. The connectionName you pass in code must match the connection name configured in the dashboard exactly; this is the most common integration error.

import { ScalekitClient } from '@scalekit-sdk/node'; import { ConnectorStatus } from '@scalekit-sdk/node/lib/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENV_URL, process.env.SCALEKIT_CLIENT_ID, process.env.SCALEKIT_CLIENT_SECRET, ); const { connectedAccount } = await scalekit.actions.getOrCreateConnectedAccount({ connectionName: 'intercom', identifier: 'user_123', }); if (connectedAccount?.status !== ConnectorStatus.ACTIVE) { const { link } = await scalekit.actions.getAuthorizationLink({ connectionName: 'intercom', identifier: 'user_123', }); console.log('Authorize Intercom:', link); }
import os import scalekit.client scalekit_client = scalekit.client.ScalekitClient( client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), env_url=os.getenv("SCALEKIT_ENV_URL"), ) actions = scalekit_client.actions response = actions.get_or_create_connected_account( connection_name="intercom", identifier="user_123", ) if response.connected_account.status != "ACTIVE": link = actions.get_authorization_link(connection_name="intercom", identifier="user_123") print("Authorize Intercom:", link.link) input("Press Enter after authorizing...")

Discover the scoped tools, then run the Claude tool-use loop

Discovery comes first: list_scoped_tools retrieves the tools the current teammate's connected account is authorized to call. Scalekit returns schemas in Anthropic's native input_schema format, so no conversion is needed before the agent loop. Execution runs through execute_tool, with the credential resolved server-side on each call.

from google.protobuf.json_format import MessageToDict import anthropic client = anthropic.Anthropic() # Discovery and scope: only the tools this teammate is authorized to call scoped_response, _ = actions.tools.list_scoped_tools( identifier="user_123", filter={"connection_names": ["intercom"]}, page_size=100, ) llm_tools = [ { "name": MessageToDict(t.tool).get("definition", {}).get("name"), "description": MessageToDict(t.tool).get("definition", {}).get("description", ""), "input_schema": MessageToDict(t.tool).get("definition", {}).get("input_schema", {}), } for t in scoped_response.tools ] messages = [{"role": "user", "content": "List open conversations unassigned for 4+ hours and reply to the billing one."}] while True: response = client.messages.create( model="claude-sonnet-4-6", max_tokens=1024, tools=llm_tools, messages=messages, ) if response.stop_reason == "end_turn": print(response.content[0].text) break tool_results = [] for block in response.content: if block.type == "tool_use": result = actions.execute_tool( tool_name=block.name, identifier="user_123", tool_input=block.input, ) tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": str(result.data), }) messages.append({"role": "assistant", "content": response.content}) messages.append({"role": "user", "content": tool_results})
import Anthropic from '@anthropic-ai/sdk'; const anthropic = new Anthropic(); // Discovery and scope: only the tools this teammate is authorized to call const { tools } = await scalekit.tools.listScopedTools('user_123', { filter: { connectionNames: ['intercom'] }, pageSize: 100, }); const llmTools = tools.map(t => ({ name: t.tool.definition.name, description: t.tool.definition.description, input_schema: t.tool.definition.input_schema, })); const messages = [ { role: 'user', content: 'List open conversations unassigned for 4+ hours and reply to the billing one.' }, ]; while (true) { const response = await anthropic.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, tools: llmTools, messages, }); if (response.stop_reason === 'end_turn') { const text = response.content.find(b => b.type === 'text'); if (text?.type === 'text') console.log(text.text); break; } const toolResults = []; for (const block of response.content) { if (block.type === 'tool_use') { const result = await scalekit.actions.executeTool({ toolName: block.name, identifier: 'user_123', toolInput: block.input, }); toolResults.push({ type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(result.data) }); } } messages.push({ role: 'assistant', content: response.content }); messages.push({ role: 'user', content: toolResults }); }

The same scoped tools inside LangChain

If you orchestrate with LangChain, wrap the scoped tools as structured tools and hand them to the agent. The scope and execution model is identical; only the framework adapter changes.

import { DynamicStructuredTool } from '@langchain/core/tools'; import { createReactAgent } from '@langchain/langgraph/prebuilt'; import { z } from 'zod'; const { tools } = await scalekit.tools.listScopedTools('user_123', { filter: { connectionNames: ['intercom'] }, pageSize: 100, }); const lcTools = tools.map(t => new DynamicStructuredTool({ name: t.tool.definition.name, description: t.tool.definition.description, schema: z.object({}).passthrough(), func: async (args) => { const result = await scalekit.actions.executeTool({ toolName: t.tool.definition.name, identifier: 'user_123', toolInput: args, }); return JSON.stringify(result.data); }, })); const agent = createReactAgent({ llm, tools: lcTools });

Use a per-user MCP endpoint instead

If your host expects MCP rather than SDK tool calls, Scalekit's Virtual MCP gives each teammate a scoped, user-specific MCP endpoint with no server for you to deploy, host, or maintain. One server definition serves all teammates; before each run, a short-lived session token is minted scoped to that teammate's connected account. The endpoint is static; the identity is per-user.

// MCP host config: a per-user, scoped Scalekit endpoint { "mcpServers": { "intercom": { "transport": "streamable-http", "url": "https://mcp.scalekit.com/intercom", "headers": { "Authorization": "Bearer ${SCALEKIT_USER_TOKEN}" } } } }

Observability for multi-tool, multi-tenant agents

A shared token looks fine in a demo. In production, every call looks like one service account, attribution breaks, and per-tenant analytics are meaningless. Scalekit resolves the real teammate credential on each call, so every Intercom action is logged with who triggered it, which tool ran, and what came back. That history is retained for 90 days and tied to the teammate who authorized it. For a multi-tenant support agent, that is the difference between an answerable audit question and a three-week investigation.

When you need an action Scalekit does not expose yet

If your agent needs an Intercom capability that is not yet a Scalekit tool, for example ticket creation or webhook-driven triggers, request it rather than dropping back to raw API plumbing. Ask in the Scalekit Slack community, or talk to a Scalekit engineer.

Which one to build against

If your Intercom agent is interactive and its job is reading conversations, retrieving customer context, or authoring Help Center content in a US-hosted workspace, the MCP server is the faster path to a working assistant. If your agent replies, assigns, closes, tags, files tickets, reacts to webhooks, or runs outside the US, build against the REST API; the MCP server's read-heavy surface and region limit are genuine constraints, not configuration. Most production support agents end up on the API for the action path and may use MCP for read-only context. Either way, the credential management problem is identical, and that is what needs production-grade infrastructure.

Browse the Scalekit Intercom connector: scalekit.com/connectors/intercom

Connector docs: docs.scalekit.com/agentkit/connectors/intercom

No items found.
Agent Auth Quickstart
On this page
Share this article
Agent Auth Quickstart

Acquire enterprise customers with zero upfront cost

Every feature unlocked. No hidden fees.
Start Free
$0
/ month
1 million Monthly Active Users
100 Monthly Active Organizations
1 SSO connection
1 SCIM connection
10K Connected Accounts
Unlimited Dev & Prod environments