MCP server
Connect Claude Code, Claude Desktop, Cursor, or VS Code to Iterant over a hosted, stateless Streamable HTTP MCP endpoint.
The Iterant MCP server gives an agent a small set of typed tools over your brands: what the site is doing, what the opportunity queue holds, how pages perform, how assistants answer about the brand, and how to put Terra to work.
It is a remote server. Nothing to install, nothing to run locally.
Eleven tools: nine reads and two writes. The writes are create_agent_task and get_agent_task, covered in Agent tasks.
Interactive clients (Claude Code, Claude Desktop, Cursor, VS Code) sign in
with OAuth: add the server, a browser opens, you sign in to Iterant and
consent. No credential lands in a config file. Headless callers (n8n, CI
runners, container jobs) use an itr_ API key minted at Settings →
Developers. The install steps below show both.
Endpoint
https://mcp.iterant.ai/mcpThat single path is the whole server. The MCP hostname serves /mcp and the /.well-known/… documents and nothing else: the REST API is not reachable there, and /mcp is not reachable on api.iterant.ai. https://mcp.iterant.ai/mcp is the OAuth resource identity, and a token audienced for it must not be a token for the whole REST API.
Any other path on the MCP host returns a 404 in the standard error shape, indistinguishable from a path that does not exist anywhere.
Transport
Streamable HTTP, stateless, and dual-era: revision 2026-07-28 natively, with clients on 2025-03-26 through 2025-11-25 served through the initialize handshake they expect. The era is chosen per request, the way the revision specifies for a dual-era server: a request carrying per-request _meta (or the 2026-07-28 version header) gets the modern stateless surface, and a request carrying neither gets the 2025-era semantics. Today's Claude, Cursor, and VS Code builds connect either way.
For anyone writing a modern-era client:
- POST only.
GETandDELETEreturn405withAllow: POST. The revision removed the standalone SSE stream, session teardown, andLast-Event-IDresumability, so there is nothing behind either verb. - No sessions. No
Mcp-Session-Idis minted or echoed, and one sent by an older client is ignored. - JSON responses.
application/json, always. AnAcceptheader that does not admit JSON is a406; the endpoint never opens an event stream. - One message per request. An array body is an invalid request (batching left the protocol in
2025-06-18), unparseable JSON is a-32700parse error under HTTP400, and a JSON-RPC response POSTed at the server is refused: this server initiates nothing, so it has nothing to hear an answer to. - Three headers are required and validated against the body.
MCP-Protocol-Versionmust equal the version in_meta,Mcp-Methodmust equal the bodymethod, andMcp-Nameontools/callmust equalparams.name(the=?base64?…?=sentinel encoding is decoded before comparing). A missing or contradicting header is-32020under400. - Every modern request carries
_meta.params._metamust includeio.modelcontextprotocol/protocolVersionandio.modelcontextprotocol/clientCapabilities; sending the2026-07-28header without them is malformed,-32602under400. server/discoveris the front door. It returns the supported versions, the capabilities, and theinstructionsstring, and it is the one method the revision requires of every server. It is answered in both eras, so a dual-era client can probe with it. An unknown modern-era method is a404whose JSON-RPC body is what tells you "wrong method" apart from "no MCP server here".- Origin is checked. A present-but-unlisted
Originis a403; absent is fine, which is what every non-browser client sends. - Notifications get no reply. A message with no
idreturns202with an empty body.
Legacy-era requests keep their 2025-era rules: initialize negotiates the protocol version and returns the instructions, ping answers, the mirrored headers and _meta are not required, results keep their pre-2026-07-28 shapes, and an unknown method is a -32601 at HTTP 200. A version header naming a revision from neither era gets -32022 with every supported version in data.supported.
The server does not implement sessions, sampling, roots, logging, resources, or prompts. Nothing is lost by the last three: the revision deprecated them protocol-wide.
What your credential reaches
An itr_ API key reaches over MCP what it reaches over REST. A brand-pinned key gives the agent one brand, an org-scoped key gives it every brand in the organization. Revoke the key and both surfaces stop working on the next request.
An OAuth access token resolves a person. The organizations that user belongs to decide what the tools can see, and a user who belongs to several reaches the brands of all of them. A token whose subject has no Iterant account is a 401.
Authentication
OAuth 2.1
The default for interactive clients: add the server, the client opens a browser, you sign in to Iterant and consent, and the client holds a token that follows your account. No credential is pasted into a config file, and leaving the organization ends the access.
Iterant advertises its authorization server through RFC 9728 protected-resource metadata, served unauthenticated and CORS-open at both:
https://mcp.iterant.ai/.well-known/oauth-protected-resource/mcp
https://mcp.iterant.ai/.well-known/oauth-protected-resourceBoth return the same document, and the path-suffixed form is the one RFC 9728 specifies for a resource that lives at …/mcp:
{
"resource": "https://mcp.iterant.ai/mcp",
"authorization_servers": ["https://…"],
"bearer_methods_supported": ["header"],
"scopes_supported": ["openid", "offline_access"]
}A 401 from the endpoint carries the same pointer on its WWW-Authenticate challenge, so a client that has never seen Iterant can discover where to authenticate.
Connectors register themselves through OAuth dynamic client registration, so the flow starts without anyone filing a ticket. (Revision 2026-07-28 deprecates DCR in favor of Client ID Metadata Documents but keeps it for authorization servers that have not adopted CIMD yet, and that compatibility path is the one this flow rides today.)
API key
For headless callers: n8n, CI runners, container jobs, anything that cannot complete a browser redirect. Mint a key at Settings → Developers (see API keys) and send it as the bearer:
Authorization: Bearer itr_…The per-client install steps below show where the header goes.
Scopes
Iterant defines no scopes. There is no iterant:read, no iterant:write, and no scope name is reserved for a future one.
Authorization is decided on every call from the credential itself: which organization it belongs to, which brand it is pinned to, and what that organization can see. The scopes_supported values in the metadata document are the standard OIDC ones and say nothing about Iterant's objects.
One consequence: tools/list returns all eleven tools to every authenticated caller. A credential that cannot do something still sees the tool in the list and gets a refusal when it calls it. Today every credential that can read a brand can also start a task for it, so the only refusal a tool raises is a tenancy one.
Install
One command, no credential:
claude mcp add --transport http iterant https://mcp.iterant.ai/mcpRun /mcp inside Claude Code, pick iterant, and choose Authenticate. A browser opens; sign in to Iterant and consent. The server shows as connected, with eleven tools.
Headless or shared environments use a key instead:
claude mcp add --transport http iterant https://mcp.iterant.ai/mcp \
--header "Authorization: Bearer ${ITERANT_API_KEY}"--scope project commits the server to the repository's .mcp.json. That is fine for the OAuth form, which stores no credential, but keep key-authenticated servers in user scope (the default) or behind an environment reference like the one above.
Claude Desktop and Cursor use "mcpServers". VS Code uses "servers".
MCP client configuration has no cross-client standard, and the protocol spec
leaves it out of scope, so copying a block between clients does nothing. Use
the tab for the client you are on.
Choosing a brand
Tools take a brand_slug such as acme, the same slug you see in your dashboard URLs. A UUID is not accepted.
brand_slug is required on all ten brand-scoped tools. It never defaults, even when your credential is pinned to a single brand and even when your organization has one brand in it. An agent that does not know the slug calls list_brands first, and the server validates whatever it passes against what the credential can reach.
Tools only
The server publishes tools only. There are no MCP prompts, so no Iterant slash commands appear in your client, and no MCP resources, so closed vocabularies are named inline in the tool descriptions. Both hold for this phase, see MCP tools.
server/discover returns an instructions string: a short orientation telling the agent to start at list_brands and get_brand_overview, how identifiers work, that one tool spends money and takes five minutes, and how to read metric states and estimates. In a client that runs tool search, that string plus the tool names is all the model sees at session start.
Verifying a connection
The MCP Inspector lists the tools your credential can see, the fastest way to confirm auth:
npx @modelcontextprotocol/inspector --cli https://mcp.iterant.ai/mcp \
--transport http --method tools/list \
--header "Authorization: Bearer itr_YOUR_KEY_HERE"Eleven tools is a healthy answer. A 401 means the key was not accepted: mistyped, truncated, or revoked. The WWW-Authenticate header points at the protected-resource metadata, which is what an OAuth client needs from it.
Any Inspector build works: an older one opens with initialize and is served as a legacy-era client.
Next
MCP tools. Every tool, what it takes, and what it returns.
Agent tasks. What create_agent_task starts, and how it settles.
Last updated on