Building Typeform AI's MCP Connectors

How we built MCP client support into Typeform AI: challenges around dynamic tool schemas, context management, credential security, and building a unified Integrations Platform.

Posted Jul 23, 2026 in Posts

This post was originally published on Typeform’s Engineering blog on Medium: Building Typeform AI’s MCP Connectors

At Typeform, throughout 2026 we’re investing significantly in MCP as the new standard for integrating LLMs with our product, both internally in our systems and externally with ChatGPT and Claude. And we’re not alone: hundreds of tools, products and platforms now include MCP support as part of their offering.

But Claude and ChatGPT aren’t the only MCP clients out there: we’ve recently added MCP connector support to our own Typeform AI, enabling users to extend its capabilities by integrating with other apps — such as Notion, Slack, Shopify, Stripe and Canva! ✨

We’ve previously covered how we built and democratized our MCP infrastructure internally, and how we’ve exposed Typeform’s features to outside clients through our public MCP server — in other words, how we’ve built MCP servers. This post dives deeper into how we’ve built our own MCP client, four of the challenges we faced and how we solved them.

Beyond the Typeform platform

MCP connectors support in Typeform AI opens up the possibility to extend our agent’s capabilities way beyond our product — it allows it to participate in the broader MCP ecosystem.

Typeform AI can leverage MCP connectors to perform a growing range of tasks: for example, it can now fetch your latest brand guidelines from Notion and assets from Canva, and turn them into a Brand Kit; it can send a customized report to Slack, or save it as a Notion page; or help you turn your form into an e-commerce page through Stripe.

While these are just a few basic examples, the takeaway is that the ability to interconnect with other tools makes it easier for users to make the most out of each of them, and MCP makes it possible for our agent to interact with a wide variety of third-parties without needing to build ad-hoc integrations.

New challenges

This is what a basic MCP integration with Typeform AI looks like at a conceptual level:

Compared to exposing functionality as tools in an MCP server, adding support for connecting Typeform AI to arbitrary MCP servers posed different challenges:

  • we need to support authenticating with the corresponding OAuth servers,
  • OAuth credentials need to be stored securely, kept fresh and valid, and safely provided to the MCP client,
  • arbitrary, untrusted 3rd-party MCP tools we don’t control need to be surfaced to Typeform AI,
  • and Typeform AI needs targeted guidance in order to make the best use of MCP connectors.

You’ll notice that the first two aren’t unique to MCP — in fact, our Integrations platform also uses OAuth for obtaining credentials, and stores them securely; more on this later.

Hitting a moving target

Until now, Typeform AI only interacted with a curated set of MCP tools, built and maintained by Typeformers.

This made context management and tuning easy: since we define the shape and inputs/outputs of each tool, we can reliably test and evaluate the agent’s performance and adjust both tools and prompts accordingly.

On the other hand, when you’re connecting to a remote MCP server, the tools it provides could change anytime! The server’s maintainers could, for example:

  • add new tools, exposing newer functionality
  • extend or improve existing tools
  • tweak tool input/output schemas
  • remove tool arguments, or remove tools altogether

From the MCP server maintainer’s perspective, this is great: to an LLM, this is ultimately just text, and it will happily handle any tool schema provided it can figure out where to procure the input data it needs. It’s as if breaking changes weren’t a thing!

But things change dramatically when you’re building the client agent: how do you reliably evaluate its performance and tune prompts when the underlying tool schemas and instructions could change anytime? How do you make sure these are safe, and don’t affect the agent or end-user negatively?

To try and mitigate these issues, here’s a few of the different techniques we’ve stacked together:

  • MCP tool metadata (name, description, input/output schemas…) and outputs are treated as untrusted input: our internal MCP proxy pre-processes all tool definitions, for example limiting their size to a safe maximum
  • Tool snapshotting: our MCP proxy stores a snapshot of each connector’s tools and validates they match on every use, preventing these breaking changes from immediately affecting our agent
  • Eval against known contracts: building on top of tool snapshotting, our evals can target the tool definitions we expect, similarly to API contract testing
  • Human-in-the-loop by default: when new tools are added, users are always asked to approve their use.

We wanted to avoid relying solely on mocked connectors for testing since connectors are third-party dependencies that can change independently of our system, and extensive mocking can reduce the reliability and long-term value of our evaluations. We also needed a way to detect when remote MCP tools changed so that we could update our prompts and integrations accordingly. For this, we landed on contract tests as part of our CI: the test suite refreshes tool definitions for a fixed account and compares them against the locked versions. If a tool changes, the pipeline fails and alerts the team.

For human-in-the-loop flows we leveraged tool annotations from the MCP spec: readOnlyHint, destructiveHint, idempotentHint and openWorldHint. Following the spec means we can support most MCP servers out of the box, and most of our internal MCP tools also follow the same rules.

Keeping agents focused

An additional drawback of the context injection agents experience when interacting with third-party MCP servers is the potential confusion or filling up of context of the agent itself.

While tool definitions can be snapshotted, their actual outputs are by definition dynamic.

Imagine a tool called list_flowers: it’s perfectly fine for us to cache its input and output schemas, but our agent needs to be fed real output data in order to do anything useful with it.

// Input
{
  "type": "object",
  "properties": { "limit": { "type": "integer" } },
  "required": ["limit"],
  "additionalProperties": false
}

// Output
{ "type": "array", "items": { "type": "string" } }

The tool could just return something like ["Roses", "Violets", "Orchids"], but in the real world most of the time outputs are complex and can get quite big — several KB of JSON!

The first step we’ve taken to limit the negative impact of 3rd-party tool outputs filling up our agent’s context window is to scope all MCP Connectors interaction to a smaller, dedicated “Connectors agent” that our main agent can talk to: when Typeform AI chooses to leverage MCP connectors, it will ask the Connectors agent to interact with them to fetch information or perform actions.

The second step and more experimental approach we’re currently testing is similar to and inspired from RTK: intercepting tools that we’ve observed to return very big outputs with additional custom code on our side that “compresses” data before feeding it to the LLM.

One example of this is wrapping 3rd-party tools and exposing more targeted, custom-built tools to our agent. Consider our previous list_flowers example:

[
  {
    "id": 1,
    "name": "Rose",
    "color": "Red",
    "family": "Rosaceae",
    "genus": "Rosa",
    "origin": "Asia",
    "season": "Spring",
    "fragrance": "Strong",
    "is_perennial": true,
    "average_height_cm": 90,
    // A very long image URL!
    "image_url": "<https://cdn.example.com/assets/images/flowers/rosaceae/rosa/red-rose/high-resolution/catalog/2026/spring/featured/rose-product-image-primary-version-12-final-optimized-with-transparent-background-and-additional-descriptive-metadata-for-testing-purposes.jpg?width=2400&height=1600&format=webp&quality=95&crop=center&fit=cover&background=ffffff&cache_buster=9f4b8c2d7e1a6f3c5b0d&tracking_source=list_flowers_tool&tracking_campaign=bogus_flower_catalog_demo&tracking_content=very_long_image_url_example>"
  }
]

Since the image_url property is very big and might not be needed in every single call, we could wrap and extend the tool to add a include_image_url boolean argument! Typeform AI would then set it to true only when it wants to fetch images:

def list_flowers(mcp_client: MCPClient, *args, include_image_url: bool = False, **kwargs):
    flowers = mcp_client.call_tool('list_flowers', *args, **kwargs)

    if include_image_url:
        return flowers

    return [
        # Filter out the image_url property
        {key: value for key, value in flower.items() if key != "image_url"}
        for flower in flowers
    ]

This approach requires slightly more engineering work on our side, as these adapters need to be maintained — but we’ve observed they meaningfully improve our agent’s ability to interact with MCP connectors.

Tuning behavior using skills

So far, we’ve made MCP Connectors predictable and easy to evaluate, and we’ve hand-tuned both the agent architecture and MCP tools for ease of use — but we can go further!

While Typeform AI at this point could already use MCP Connectors meaningfully, it wasn’t very efficient: it had to re-learn everything about each connector on every execution by listing tools, resources and prompts provided by the remote MCP server. This is very flexible and requires little to no maintenance, but we wanted to offer the best possible experience and even more so with partner connectors.

To address this problem, the team authored and evaluated hand-crafted skills — reusable prompts — for Typeform AI to reduce the bootstrap exploration the agent would perform, surface best practices, and highlight key workflows where Typeform and the third-party connector shine when used together; essentially per-connector behavioral guidance.

For example, Typeform supports creating product recommendation quizzes: through a series of questions, respondents are guided to the product that best suits their needs. Up until now, creators who wanted to leverage Stripe’s checkout page had to manually generate payment links and configure them as final redirect URLs for every possible product in their typeform. This is a good use case to leverage Typeform AI and the Stripe connector.

This could conceptually look like the following:

# Stripe connector guidance

## Tool selection by query type
- Search/list resources: use `search_stripe_resources` for customers, products, invoices, subscriptions
- Find API operations: use `stripe_api_search` with keywords (e.g., 'create product', 'list customers')
- Inspect operation parameters: use `stripe_api_details` with the operation_id to see all available parameters and their types
- Read specific resource: use `stripe_api_read` when you have the resource ID
- Create/update resources: use `stripe_api_write` after calling `stripe_api_details` to confirm parameters

## Integration best practices
- One-time payments: Payment Links (no-code) > Checkout Sessions > Payment Element
[...]

This reduces Typeform AI’s need to explore and guess how a connector is meant to be used, which translates to users getting their desired results faster.

Building an MCP-native Integrations Platform

Typeform supports over 120 integrations natively — and thanks to MCP Connectors, the number of tools users can connect is set to grow even further. Most integrations — MCP connectors included — share at least a few common concerns:

  • users need to authenticate with the third-party platform and authorize Typeform to connect to it, which produces a pair of tokens: one short-lived, one long-lived (access token and refresh token respectively);
  • we need to store these tokens securely, and periodically exchange the long-lived one to “refresh” the short-lived token;
  • we need to include the access token in outbound API/MCP calls to the third-party platform.

Adding to point #3, to keep credentials secure we also want to keep them separate from the LLM when interacting with integrations or MCP connectors through Typeform AI.

Finally, as Typeform’s products grow beyond forms, we also needed to design a solution that would be reusable across all our surfaces, Typeform AI included.

We ended up with the following:

Here are the key takeaways from it:

  • our products’ surfaces can interact with a single gateway abstracting away the details of individual integrations;
  • shared UI micro-components handle OAuth flows and authentication with third-party platforms;
  • a unified vault securely stores encrypted credentials, and HTTP and MCP proxies allow downstream services to perform actions and call tools without ever seeing the decrypted tokens;
  • and a single Connectors catalog makes it simple to declaratively configure new MCP connectors.

We heavily leaned on our MCP proxy to make credential injection and refresh transparent, and it eventually evolved into a reusable Integrations Gateway.

Integrations everywhere

We’ve covered some of the challenges we’ve faced introducing MCP Connectors support to Typeform AI, shared the steps we’ve taken so far to address them, and had a look at our Integrations Platform’s architecture from a high level.

We’re not done yet: some of the techniques we’re experimenting with are still evolving, we’re expanding the catalog of supported connectors, and are looking forward to fully adopting our new Integrations Platform across Typeform!