Announcing CIMD support for MCP Client registration
Learn more

Should you use Typeform MCP or Typeform API for building AI Agents?

TL;DR

  • These are not two views of one product. The MCP server's 63 tools are the only public surface for Contacts, Automations, and response analytics; the REST APIs are the only surface for bulk response export, themes, workspaces, and form-level webhooks.
  • MCP is OAuth 2.0 only and consent is all-or-nothing: users cannot grant a subset of scopes. REST supports granular scopes and personal access tokens, the only workable path for an agent running with no user present.
  • The MCP authorization server rejects any redirect URI whose domain is not on Typeform's allowlist, and registration fails until Typeform adds yours. Shipping a Typeform connector is a scheduling problem, not just an engineering one.
  • Three regions, three MCP endpoints, and the two EU stacks have separate issuers. A token minted at one is invalid at the other, so region belongs in your credential record rather than a config constant.
  • Neither path stores, refreshes, revokes, or attributes tokens for you. Scalekit's Typeform MCP connector runs the per-user OAuth flow, vaults and refreshes tokens, and logs every call against the user who authorized it.

Your agent needs to work with Typeform. It needs to pull NPS out of last quarter's survey, push respondents into a contact list, and fire an automation when someone marks themselves as churn risk. Typeform ships a hosted MCP server and three REST APIs. Most tool comparisons in this series describe an MCP server that covers a slice of a bigger API. Typeform inverts that: each path owns capabilities the other cannot reach, and picking wrong means discovering a hard wall halfway through the build.

What Typeform MCP and the Typeform REST APIs Actually Are

The two paths were built at different times for different consumers, and it shows in what each one can touch. Establish the objects before comparing them.

Typeform MCP

Typeform hosts the server itself at https://api.typeform.com/mcp. It exposes 63 tools across five domains: accounts-* and workspaces-* for resolving where you are, forms-* for building and publishing, insights-* for response analytics, automations-* for workflow construction, and contacts-* for the contacts database. A submit_feedback tool rounds out the set.

Transport is streamable HTTP only. There is no SSE endpoint, and clients configured for SSE fail on connect rather than degrading. Authorization is OAuth 2.0, prompted on first connect. Typeform's own help-center changelog announced the server as beta, and the developer docs carry no GA badge, so treat the contract as one that can still move.

The Typeform REST APIs

There is no single "Typeform API." There are three, and agents need to reason about them separately. The Create API covers forms, workspaces, themes, images, videos, translations, and custom messages. The Responses API covers response retrieval, advanced filtered search, response deletion, and file, audio, and video downloads. The Webhooks API covers form-level webhook registration and delivery history.

Auth is OAuth 2.0 Authorization Code with granular scopes such as forms:read, responses:read, and webhooks:write, or a personal access token for single-account use. Reference material lives on the Typeform developer platform.

Comparing Them Where It Matters for Agents

Four dimensions decide this: what the agent can reach, what auth model you inherit, what you own operationally, and which scenario you are actually building for.

What Your Agent Can Actually Do

The overlap is narrower than the marketing on either side suggests. Form creation and publishing exist on both. Almost everything else lives on exactly one path.

Capability
Typeform MCP
Typeform REST APIs
Create and edit form structure
Yes, via a patch-and-validate chain
Yes, Create API
Publish a form
Yes
Yes
Duplicate a form
Yes, one call
Partial: retrieve, then re-create
Response analytics: aggregates, NPS, time series, top-N
Yes, five insights-* tools
No
Contacts database, lists, form-to-contact mappings
Yes, 26 contacts-* tools
No
Automations: create, add steps, patch triggers, publish, pause
Yes, 19 automations-* tools
No
Bulk response rows, response export, response deletion
No, insights-public_list is per-field only
Yes, Responses API
Themes, images, brand kits
No, forms build structurally but unstyled
Yes, Create API
Workspace create, update, delete, move a form between workspaces
No, list only
Yes, Create API
Form-level webhook create, list, update, delete
No, webhooks exist only as automation steps
Yes, Webhooks API
Retrieve embed codes
No
Yes, Create API
Delete an automation
No
No
Headless execution with no user present
No, interactive OAuth required
Yes, personal access token

Where the MCP Server Is the Only Door

Contacts, Automations, and Insights have no REST equivalent. They do not appear in Typeform's OAuth scope table for the REST APIs, they have no reference section in the developer portal, and Typeform's own "not yet supported" notes route MCP gaps to the Create, Responses, and Webhooks APIs, never to a contacts or automations endpoint.

That has a concrete consequence. If your agent's job is "read the exit survey, segment the detractors into a contact list, and trigger a win-back email automation," there is no REST fallback. MCP is the integration.

Where REST Is the Only Door

The reverse holds for data volume and presentation. insights-public_list returns row-level data for a single field at a time, so anything resembling an export, a warehouse sync, or a right-to-be-forgotten deletion runs through the Responses API.

Styling is the other gap worth planning around. An agent can build a form's entire structure over MCP and still hand the user something wearing the default theme, because themes, images, and brand kits are Create API territory. For a customer-facing form builder, that is a visible defect, not a footnote.

The Auth Path Each One Puts You On

Auth is where the two paths diverge most sharply, and where the choice becomes irreversible for certain agent shapes.

MCP: OAuth Only, All Scopes, Every Time

Typeform MCP supports OAuth 2.0 exclusively. Personal access tokens are explicitly not supported for MCP connections. There is no service-account shortcut and no client-credentials path.

Worse for least privilege: users cannot currently customize the scopes they grant. Connecting to the MCP server means granting every scope the server accepts, including contacts:write and automations:write, even if your agent only reads NPS. Your scoping has to happen above the token, at the tool layer.

The authorization server does support dynamic client registration at POST https://api.typeform.com/oauth/register, PKCE with S256, and public clients with token_endpoint_auth_method: none. Ask for offline_access if the agent has to act after the first access token expires; the REST OAuth apps use offline for the same thing, and mixing them up is a quiet source of missing refresh tokens.

The Allowlist That Gates Your Launch Date

One requirement catches teams late. Typeform's authorization server rejects any redirect URI whose domain is not on its allowlist, so dynamic client registration fails until Typeform adds your domain.

Localhost and 127.0.0.1 work for development, and the major AI clients are pre-approved, which is why the server feels frictionless in Claude or ChatGPT and then blocks the first deploy of your own product. Building a Typeform connector into a multi-tenant B2B product means submitting a connector request and waiting.

REST: Granular Scopes and a Headless Path

The REST side is conventional. You register an OAuth app, request only the scopes you need, and users see exactly that on the consent screen. Personal access tokens cover single-account and internal automation.

That matters most for background agents. A nightly job that exports yesterday's responses has no user to click a consent screen, and MCP has no answer for it. REST does.

Region Routing Is an Auth Problem

Typeform accounts sit in three regions with three MCP endpoints: the default at api.typeform.com, EU data center 1 at api.eu.typeform.com, and EU data center 2 at api.typeform.eu. The two EU endpoints are not interchangeable.

api.eu.typeform.com is an EU resource served by the main authorization server. api.typeform.eu is a separate stack with its own issuer, endpoints, and signing keys, and an access token issued by one is invalid at the other. accounts-list_accounts returns a region per account. Resolve it once at authorization, store it alongside the token, and route on it. Probing endpoints and falling back on failure reads to your customer as an outage.

What You Own in Production

Managed hosting removes some work and adds some. The useful question is what breaks at 2am and who is on the hook for it.

Call-Order Chains Cost You Round Trips

Several MCP tools exist to be called before others, and skipping them means guessing. accounts-list_accounts resolves the account_id nearly every other tool needs. forms-public_get_capabilities returns the field types and logic operators the editing tools accept. insights-public_discover returns the queryable fields and measures for a given form.

Form editing is a four-call chain: capabilities, then forms-public_validate_patch which returns a validation_token, then forms-public_patch_form which requires that token, then forms-public_publish_form. Validation is mandatory, not a dry run. A connector that stops at patch_form leaves the user's edits invisible, which reads as a bug in your product.

Budget accordingly. One user intent frequently costs three to five tool calls, and each one is a model round trip plus a network hop. This overhead is one reason MCP can be significantly more expensive than simpler interfaces — worth understanding before committing to the architecture.

Plan Gating Fails at Call Time

tools/list is identical for every account and safe to cache, so there is no per-user capability negotiation. Feature access is enforced when a tool is called instead, which means the same tool succeeds for one tenant and fails for another.

Two error codes carry it: FEATURE_UNAVAILABLE and PAYMENT_REQUIRED. Branch on the top-level code, never on the prose, and surface the details array to your user. "Video answers is not on your plan" is actionable; "payment required" is not.

Schema Drift and Beta Status

MCP schemas change when Typeform updates the hosted server, and you consume that contract without controlling its cadence. The REST APIs are versioned conventionally, so you migrate on your schedule.

For a deterministic pipeline where an unexpected schema change is an incident, that difference decides the architecture. Typeform's own changelog introduced the MCP server as beta, and the tool surface has already expanded once.

When to Use MCP, When to Use the REST APIs

Both lists below are Typeform-specific. Generic MCP advice will not help you here, because the capability split is the deciding factor more often than developer ergonomics.

Use Typeform MCP When

  • Your agent works with Contacts, Automations, or response analytics. There is no REST alternative, so the decision is already made.
  • You are building an interactive assistant where a user asks "which NPS bucket is growing" or "add everyone who picked pricing to the churn-risk list" and expects an answer in the conversation.
  • You want a Typeform user's workspace roles to constrain the agent automatically. Tool calls run as the authorizing user, so response reads and contact writes inherit their permissions.
  • You are building a form-authoring assistant and can accept default styling, or can hand off to the Create API for theming afterwards.

Use the Typeform REST APIs When

  • The agent runs on a schedule with no user present. A personal access token or a pre-authorized OAuth app is the only workable credential.
  • You are exporting full response rows into a warehouse, running right-to-be-forgotten deletions, or downloading respondent file uploads.
  • You need themes, images, brand kits, embed codes, or workspace lifecycle operations.
  • You need form-level webhooks registered directly rather than as steps inside a Typeform automation.
  • You need a pinned, versioned contract because an unplanned schema change would be a production incident.

The Credential Problem Neither Path Solves

Here is the part that survives whichever path you pick. Both hand you a credential per user and then walk away.

N Tokens Per Tenant

MCP's OAuth flow gives you an access token and, if you asked for offline_access, a refresh token. The REST path gives you an OAuth token or a personal access token. In both cases the token type differs and the infrastructure requirement is identical.

In a multi-tenant B2B agent, every user who connects Typeform produces another credential to encrypt, isolate per tenant, refresh before expiry, revoke on offboarding, and route to the right regional endpoint. At 40 users that is a table. At 4,000 it is a system. Our write-up on access control for multi-tenant AI agents covers why bolting this on later fails.

Where Scalekit Fits

Scalekit's Typeform MCP connector handles the per-user OAuth flow, stores tokens in an AES-256 vault namespaced per tenant, and refreshes them automatically, so credentials never enter your agent runtime or the model context. The connector ships live, which means the allowlist and dynamic client registration plumbing sits on Scalekit's side rather than in an OAuth client you have to get approved.

The MCP versus API decision does not change any of that. For the REST-only gaps, the same connected-account model extends through custom tools and bring your own connector, so the export job and the interactive agent share one credential store and one audit trail.

Connecting a Typeform Agent With Scalekit

Scalekit's catalog currently ships one Typeform connector, typeformmcp, which fronts Typeform's hosted MCP server. The setup below builds a read-only survey analytics agent in Python with LangChain.

Install and Configure

Two dependencies and three environment variables. Find the credential values in the Scalekit dashboard under Developers, then API Credentials.

pip install scalekit-sdk-python langchain-openai
# .env SCALEKIT_ENVIRONMENT_URL= SCALEKIT_CLIENT_ID= SCALEKIT_CLIENT_SECRET=
import os from scalekit import ScalekitClient scalekit_client = ScalekitClient( env_url=os.environ["SCALEKIT_ENVIRONMENT_URL"], client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], ) actions = scalekit_client.actions # Must match the connection name configured in your Scalekit dashboard exactly. CONNECTION_NAME = "typeformmcp" IDENTIFIER = "user_123"

The connection_name string is the single most common integration error. It has to match the dashboard value character for character.

Authorize the User

Look up or create the connected account, then send the user through Typeform's consent screen only if the account is not already ACTIVE.

account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=IDENTIFIER, ).connected_account if account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=CONNECTION_NAME, identifier=IDENTIFIER, state="csrf-token-abc123", user_verify_url="https://yourapp.com/auth/callback", ) print("Authorize Typeform:", link.link) input("Press Enter after authorizing...")

In production, handle the redirect with actions.verify_connected_account_user using the auth_request_id from the callback query string rather than blocking on stdin.

Resolve the Account Before Anything Else

Typeform's most common first-call failure is a missing account_id. Get it out of the way with a direct execute_tool call before the agent loop starts.

result = actions.execute_tool( tool_input={}, tool_name="typeformmcp_accounts_list_accounts", identifier=IDENTIFIER, ) print(result.execution_id) print(result.data) # Typeform payload passes through: # {"items": [{"account_id": "01JXM...RPSQ", "name": "Acme", "region": null, "role": "owner"}]}

Store the region value alongside the connected account. That is the field that decides which Typeform endpoint the user's future calls belong to.

Scope the Tool Surface Before the Model Sees It

Typeform's OAuth consent grants every scope, including contact deletion and automation publishing. Scoping therefore has to happen at the tool layer, and tool_names is where you do it. The agent below is read-only by construction: five tools out of 63.

tools = actions.langchain.get_tools( identifier=IDENTIFIER, connection_names=[CONNECTION_NAME], tool_names=[ "typeformmcp_accounts_list_accounts", "typeformmcp_forms_public_list_forms", "typeformmcp_insights_public_discover", "typeformmcp_insights_public_aggregate", "typeformmcp_insights_public_toplist", ], page_size=100, )

actions.langchain.get_tools returns native StructuredTool objects for the tools this user's connected account is authorized to call, so there is no schema conversion step. For the raw schemas behind a custom adapter, scalekit_client.tools.list_scoped_tools returns the same surface untranslated.

Run the Agent

Bind the tools and run the loop. No Scalekit-specific logic appears past initialization.

from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, ToolMessage tool_map = {t.name: t for t in tools} llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ HumanMessage( "In the Q3 customer survey, rank the top 5 churn reasons " "and give me the NPS for each. Call discover first." ) ] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: result = tool_map[tc["name"]].invoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

The instruction to call discover first is not decoration. Field IDs are per-form, and insights-public_discover is what resolves them. For a broader look at how LangChain tool calling works and where it stops, that post covers the full pattern including multi-tenant credential resolution.

Running Typeform Tools Through a Virtual MCP Server

The tool-list approach above works well for a single connector. Multi-tool agents and multi-tenant products need something stricter, which is what virtual MCP servers provide.

The Token Bloat Problem Is Real Here

Every tool on an MCP server consumes context on every run. Scalekit's own estimate puts a typical tool description at roughly 200 tokens, which puts Typeform's 63 tools north of 12,000 tokens before the agent does any work.

Typeform's descriptions run long. The email-step tool alone ships several hundred words of HTML structure rules. Scoping to the five or ten tools an agent role actually needs is a cost decision as much as a security one.

Define the Server Once, Mint a Token Per Run

A virtual MCP server is a scoped endpoint declaring which connections and which tools are exposed. Define it once per agent role, get a static URL, then mint a short-lived session token bound to one user before each run.

That is the shape multi-tenant products need: one definition serving every customer, with the credential resolving per run.

# Backend: generate a per-user MCP URL before the run inst_response = actions.mcp.ensure_instance( config_name="typeform-insights-agent", user_identifier="user_123", ) mcp_url = inst_response.instance.url

Each URL is pre-authenticated for exactly one user. Sharing a process-wide URL across a multi-user deployment runs every request as whoever authorized it — the failure mode described in our post on when to use a virtual MCP server.

Wire It Into Mastra

Mastra has native MCP support, so it pulls the tool list and Zod schemas straight from the URL. Fetch the current user's URL from your backend, never a shared constant.

npm install @scalekit-sdk/node @mastra/core @mastra/mcp @ai-sdk/openai
import { Agent } from '@mastra/core/agent'; import { MCPClient } from '@mastra/mcp'; import { openai } from '@ai-sdk/openai'; // Per-user URL from your backend, not a process-wide secret const mcpUrl = await getMcpUrlForUser(currentUserId); const mcp = new MCPClient({ servers: { typeform: { url: new URL(mcpUrl) }, }, }); const tools = await mcp.getTools(); const agent = new Agent({ name: 'typeform_insights_agent', instructions: 'You analyse Typeform survey responses. Always resolve the account and ' + 'call the discover tool before running any analytics query.', model: openai('gpt-4o'), tools, }); const result = await agent.generate( 'Rank the top 5 churn reasons in the Q3 exit survey and give me the NPS for each', ); console.log(result.text); await mcp.disconnect();

What Scalekit Adds That the Raw Endpoint Does Not

Typeform's hosted server is a good piece of infrastructure. It is also, by design, indifferent to which of your tenants is calling it.

Tool Call Logs With Real Attribution

A shared token looks fine in a demo. In production, every Typeform action in the audit trail resolves to one service account, and nobody can answer who deleted the contact list.

Scalekit resolves the real user credential server-side before each call and logs the call against the person who authorized it, with 90 days of history and SIEM export. That covers who triggered it, which tool ran, and what came back. The reasoning behind that model is in our post on agent tool observability.

One Auth Pattern Across Every Connector

Typeform is rarely the only tool. A survey agent typically reads Typeform, posts a digest to Slack, and writes back to a CRM, and each of those is another OAuth implementation to build, refresh, and revoke.

With a connected-account model, every connector in the catalog inherits the same vault and the same log. Adding Google Forms or Slack to the survey agent is a connection, not a new auth project.

Revocation That Fails Closed

When a Typeform user revokes access, the connection is invalidated on the next tool call. Requests for that user fail with a clear error, other users in the tenant are unaffected, and the event is logged.

That is the behaviour a security review asks about, and it is difficult to retrofit onto a token table. For more on this, see how to handle token refresh for AI agents and our deep dive on token vaults for agent workflows.

Which One to Build Against

Start from the capability, not the protocol. If the agent touches Contacts, Automations, or response analytics, Typeform MCP is the only public surface, and the OAuth-only constraint comes with it. If the agent exports responses in bulk, styles forms, manages workspaces, or runs unattended, the REST APIs are the only surface.

Most production Typeform agents end up needing both. That is the real planning insight: budget for two integrations, not one, and build the credential layer to serve them together rather than twice.

The token lifecycle does not care which you picked. That is the piece worth putting on infrastructure that already handles per-user isolation, refresh, revocation, and attribution.

Build Your Typeform Agent

Browse the Scalekit Typeform MCP connector or read the connector documentation for the full 63-tool surface and quickstart. Usage tiers are on the pricing page.

The same tradeoffs for neighbouring platforms are covered in Jotform MCP vs Jotform API and Tally MCP vs Tally API.

Building something on Typeform and want another pair of eyes on the auth model? Talk to us for direct help.

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.