> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zapier.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AI agent connections & automations

> Connect user accounts via White Label and call Zapier-hosted MCP tools with Streamable HTTP.

# AI agent connections & automations

This guide covers using **Zapier MCP** so your product (or an AI agent inside it) can **discover and call tools** exposed from connected apps. It complements the generic [Token exchange](../token-exchange) page: for MCP you usually exchange a partner-signed **JWT** directly for a **connect token**, then complete a standard OAuth **authorization code** flow to obtain an **MCP access token** for the MCP server.

For embedding pre-built UI or running Actions API without MCP, see [Embedded triggers/actions](./embedded-triggers-actions) and the [Zapier SDK](https://docs.zapier.com/) documentation as applicable.

## Flow overview

```mermaid theme={null}
sequenceDiagram
  actor User
  participant P as Partner
  participant ZAPI as Zapier API
  participant Z as zapier.com
  participant A as App
  participant AAPI as App API

  User->>P: Clicks "Connect"
  P-->>ZAPI: Exchange JWT for Connect token
  Note over P,ZAPI: POST zapier.com/oauth/token
  alt User not found
      ZAPI->>ZAPI: Provision user
  end
  ZAPI-->>P: connect token
  P->>Z: Redirect user to app's authorize URL
  Note over P,Z: connect.zapier.com/oauth/authorize?token=...&resource=...&state=...&redirect_uri=...
  Z->>A: Redirect to app's authorize URL
  User->>A: Grants Zapier access
  A->>Z: Redirect back with app code
  Z-->>AAPI: Exchange app code for tokens
  AAPI-->>Z: App access + refresh tokens
  Z-->>Z: Create app connection
  Z-->>Z: Create MCP server connection
  Z->>P: Redirect back with authorization code
  Note over P,Z: your-domain.com/callback?code=...&state=...
  P-->>ZAPI: Exchange authorization code for tokens
  Note over P,ZAPI: POST zapier.com/oauth/token
  ZAPI-->>P: MCP access + refresh tokens
  P-->>P: Store tokens, connect MCP server
```

## When to use Zapier MCP

Use **MCP** when you want a **protocol-native** integration (tool listing, `callTool`, streaming HTTP transport) against Zapier-hosted MCP servers. Use **Action Runs** (and related APIs) when you already orchestrate HTTP yourself or need fine-grained run/poll control outside MCP.

## Prerequisites

* White Label onboarding complete ([Partner onboarding](../partner-onboarding)): JWKS, callback URLs, JWT expectations, client credentials.
* Backend can sign **partner JWTs** and perform **server-side** calls to `POST https://zapier.com/oauth/token`.
* A registered **redirect URI** for the OAuth authorization-code step (same host/path you use when sending users to the MCP authorize URL).

## 1) Discover apps (`GET /v2/whitelabel/apps`)

Search the catalog for apps your users can connect (server-side). Example:

```http theme={null}
GET https://api.zapier.com/v2/whitelabel/apps?search=Slack
```

This catalog endpoint does **not** require authentication — call it from your backend without an `Authorization` header.

Responses always use the same shape: an envelope with `data`, `links`, and `meta`, and each object in `data` has the same set of app fields. For MCP, the important ones are:

* `id` — app implementation id (used in URLs and MCP paths).
* `is_oauth` — if `true`, the third-party app uses an OAuth-style authorization step for the user.
* `has_fields` — if `true`, the user may need to enter extra identifiers (subdomain, instance URL, etc.) before or instead of OAuth.
* `links.mcp_authorize_url` — start of the MCP OAuth authorize flow.
* `links.mcp_server_url` — MCP server base URL for that app.

Example response:

```json theme={null}
{
  "data": [{
    "type": "app",
    "id": "SlackCLIAPI",
    "title": "Slack",
    "is_oauth": true,
    "has_fields": false,
    "links": {
      "mcp_authorize_url": "https://connect.zapier.com/oauth/authorize",
      "mcp_server_url": "https://mcp.zapier.com/api/v1/connect/SlackCLIAPI"
    }
  }],
  "links": { "next": null, "previous": null },
  "meta": { "count": 1, "limit": 10, "offset": 0 }
}
```

### Custom Connect domain

If Zapier provisions a **custom (sub)domain** for ConnectApp, replace the **origin** of `mcp_authorize_url` with that host before sending users to authorize. **Keep the path** unchanged.

## 2) JWT → connect token

Follow [Token exchange](../token-exchange), pattern **B (MCP)**: exchange the partner-signed JWT for a **connect token** (`requested_token_type=urn:ietf:params:oauth:token-type:connect-token`, `scope=connection:write`). You must send **`resource=<mcp_server_url>`** on that `POST /oauth/token` request — use the **exact same** URL as the `resource` query parameter in step 3. The connect token is returned in the JSON **`access_token`** field.

## 3) Send the user to `mcp_authorize_url`

Redirect the browser (or open a popup / new tab) to `mcp_authorize_url`. **All** of the parameters in the list below are required for this flow. You must also send every standard OAuth authorize parameter your client registration requires — typically **`client_id`** and **`response_type=code`** at minimum, plus any others indicated by **Discovery via OAuth metadata** below.

* `token` — the **connect token** from step 2 (Zapier-specific; in addition to standard OAuth parameters).
* `resource` — the app’s `mcp_server_url`.
* `redirect_uri` — your registered callback.
* `state` — random value you verify on return (CSRF protection).
* `scope` — must be `mcp:run`.

Example query shape:

```
https://connect.zapier.com/oauth/authorize?
  token=<CONNECT_TOKEN>
  &resource=<MCP_SERVER_URL>
  &redirect_uri=https://your-app.com/callback
  &state=<RANDOM_STATE>
  &scope=mcp:run
  &client_id=...
  &response_type=code
```

After the user completes consent, Zapier redirects to `redirect_uri` with `code` and `state`.

### Popup or new tab

If you open authorize in a secondary window, Zapier still redirects **that window** to `redirect_uri`. Use `postMessage` from your callback page to notify the opener, then close the window, if you want to avoid a full-page navigation in your primary app.

## 4) Exchange `code` for MCP access token

`POST https://zapier.com/oauth/token` with `grant_type=authorization_code`, the `code`, `client_id`, `client_secret`, and the same `redirect_uri` as step 3.

The response includes `access_token` (MCP bearer), `refresh_token`, and `expires_in`. Use the **MCP access token** (not the connect token) as `Authorization: Bearer` when talking to the MCP server.

## 5) Refresh the MCP access token

When MCP requests return HTTP `401`, refresh:

```bash theme={null}
curl -X POST https://zapier.com/oauth/token \
  -d grant_type=refresh_token \
  -d refresh_token=<REFRESH_TOKEN> \
  -d client_id=<ZAPIER_CLIENT_ID> \
  -d client_secret=<ZAPIER_CLIENT_SECRET>
```

## 6) Call the MCP server (Streamable HTTP)

The MCP server at `mcp_server_url` supports **Streamable HTTP**. Pass the **MCP access token** as the Bearer token.

Example using the official MCP TypeScript SDK:

```typescript theme={null}
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const transport = new StreamableHTTPClientTransport(new URL(app.links.mcp_server_url), {
  requestInit: {
    headers: { Authorization: `Bearer ${mcpAccessToken}` },
  },
});

const client = new Client({ name: "my-app", version: "1.0.0" });
await client.connect(transport);

const { tools } = await client.listTools();
const result = await client.callTool({
  name: "example_tool_name",
  arguments: { example_key: "example_value" },
});

await client.close();
```

## Discovery via OAuth metadata (optional)

Clients can discover authorization configuration without hardcoding `mcp_authorize_url`:

1. **Protected Resource Metadata** ([RFC 9728](https://www.rfc-editor.org/rfc/rfc9728)): insert `/.well-known/oauth-protected-resource` between the **host** and **path** of `mcp_server_url`.

   Example:

   ```http theme={null}
   GET https://mcp.zapier.com/.well-known/oauth-protected-resource/api/v1/connect/SlackCLIAPI
   ```

   Example JSON body (illustrative):

   ```json theme={null}
   {
     "resource": "https://mcp.zapier.com/api/v1/connect/SlackCLIAPI",
     "authorization_servers": ["https://connect.zapier.com"],
     "scopes_supported": ["mcp:run"]
   }
   ```

2. **Authorization Server Metadata** ([RFC 8414](https://www.rfc-editor.org/rfc/rfc8414)) for `https://connect.zapier.com`:

   ```http theme={null}
   GET https://connect.zapier.com/.well-known/oauth-authorization-server
   ```

<Note>
  **Non-standard authorize parameter:** The authorization endpoint requires **`token=<connect_token>`** (the connect token from the JWT exchange) in addition to normal OAuth query parameters for your client. Plan your authorize URLs accordingly.
</Note>

## Removing an MCP server and connection

To delete a user's MCP server and the connection that was created during the authorization flow, revoke the MCP refresh token by calling the standard OAuth revocation endpoint:

```bash theme={null}
curl -X POST https://zapier.com/oauth/revoke \
  -d token=<MCP_REFRESH_TOKEN> \
  -d client_id=<ZAPIER_CLIENT_ID> \
  -d client_secret=<ZAPIER_CLIENT_SECRET>
```

Both the MCP server and the underlying connection are removed immediately. The user will need to complete the full connection and authorization flow again to restore access.

## Security notes

* Keep **client\_secret**, **user access tokens**, **connect tokens**, and **MCP refresh tokens** on the server. Only the short-lived browser steps should touch the connect token and authorization `code`.
* Validate **`state`** on callback. Reject mismatches.
* Prefer **short-lived** partner JWTs and rotate signing keys using JWKS.

## Reference implementation

Download a minimal CLI chat that demonstrates the full flow above — JWT exchange, browser-based authorization, and an AI-powered chat using MCP tools — built with the [Vercel AI SDK](https://sdk.vercel.ai/) and the official [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk):

<Card title="wl-mcp-demo.zip" icon="download" href="/assets/wl-mcp-demo.zip">
  Single-file TypeScript demo with `package.json`, `.env.example`, and README.
</Card>

The demo uses any OpenAI-compatible LLM provider and handles token persistence so subsequent runs skip browser authorization. See the included README for setup and configuration.

## Related guides

* [Token exchange](../token-exchange)
* [Connection flow](../connection-flow)
* [Error handling](../error-handling)
* [Embedded triggers/actions](./embedded-triggers-actions)
