# Wire TanStack AI agents to OAuth-protected MCP via Vercel Connect

> Use connectMCPTransport and getConsentChallenge so MCP tokens issue at your owned route—not a pasted env secret to rotate.
> By Dave · 2026-09-24
> Source: https://otf-kit.dev/blog/vercel-connect-tanstack-ai-mcp-auth

If you already run TanStack AI agents that call MCP tools, the boring failure mode is not that the model is dumb. It is that you pasted a long-lived MCP token into env, shipped it with the deployment, and now every rotate is a redeploy plus a hope that no screenshot of `$CONNECT_*` ever leaked. Vercel’s changelog for [Vercel Connect + TanStack AI](https://vercel.com/changelog/vercel-connect-tanstack-ai) (2026-09-24) is aimed at that boundary: install `@vercel/connect/tanstack-ai`, send MCP traffic through `connectMCPTransport`, and when the server returns a consent challenge, catch `getConsentChallenge` and redirect the user. Tokens are issued at runtime at an owned route. They do not live in env for you to rotate.

This post is the keep-path PSEO for that wiring. It is not the EAS/Claude connector path in [Expo MCP connector for Claude](https://otf-kit.dev/blog/expo-mcp-connector-claude). Different runtime, different consent surface, different place the secret is allowed to exist.

## What Connect is doing at the agent boundary

TanStack AI already knows how to call tools. MCP already knows how to expose tools behind OAuth. The missing piece on a Vercel deployment is a first-party transport that refuses to treat “bearer in `$CONNECT_TOKEN`” as the integration.

`connectMCPTransport` is that transport. You point the agent at MCP the same way you would any other tool host, except the bytes do not leave your app with a static secret. When the MCP server needs a user (or org) to grant access, Connect surfaces a consent challenge. Your route handles it with `getConsentChallenge` and redirects. After consent, a runtime token is bound to that session at the route you own.

That is the product claim worth keeping: **owned-route MCP auth**. The agent never becomes a secret store. Env never becomes a token locker. Rotation is “consent again,” not “edit production env and pray.”

If you still think in gateway terms — one billed path for model calls, one for tools — pair this with [AI Gateway delegation](https://otf-kit.dev/blog/gpt-live-1-delegation-ai-gateway). Gateway is the model hop. Connect is the OAuth hop for MCP. Do not collapse them.

## Install and the only import that matters

From the changelog: install `@vercel/connect/tanstack-ai`. That package is the TanStack-specific adapter. Do not cargo-cult a mobile EAS connector, a generic MCP SDK auth helper, or a “put the token in `$CONNECT_MCP_TOKEN`” snippet from an older thread.

Keep Connect configuration in env as *connection* config — whatever `$CONNECT_*` names your dashboard emits — not as the user’s MCP access token. Path-only on the app side: your route owns consent and the agent endpoint.

Honest skeleton from the changelog: TanStack AI (`chat`, `createMCPClient`) with MCP attached through `connectMCPTransport` — not a raw fetch with `Authorization` from `.env`. The same HTTP handler catches `getConsentChallenge` and redirects; it does not retry the model with a guessed token. Storing the resulting token in env to “make CI simpler” undoes the feature.

## `connectMCPTransport`: keep the agent ignorant of OAuth

The agent’s job is tool names, arguments, and when to stop. OAuth is not a tool. If you shove authorize URLs into system prompts, you will get a model that pastes tokens into logs.

`connectMCPTransport` sits under the tool layer. The provider is called before every MCP request, so the token is always fresh. Failed auth is not a chat message; it is a control-flow exception your route understands. That is why `getConsentChallenge` exists as a catchable object rather than a string the model might echo.

Trace transport errors and redirects, not bearer prefixes. Treat consent timeouts as user-wait. Multiple MCP servers can share the Connect consent pattern — not one shared env token.

## `getConsentChallenge` and the redirect you actually ship

Consent is a browser problem. When `getConsentChallenge` fires on the agent route the UI posts to, that route should:

1. Stop the current agent turn. Do not half-apply tool results.
2. Redirect the user to the consent URL Connect gave you (changelog uses a 303).
3. Land back on a path you own after the user accepts or denies.
4. Resume the agent only after a runtime token exists for that session.

“Owned route” is load-bearing. If consent returns to a third-party page you do not control, someone else’s cookie becomes your security model. Keep the callback on your deployment — path-only, like `/connect/consent` next to the agent route.

Denied consent is first-class. Show that MCP tools are unavailable. Do not fall back to a pasted env token “just this once.”

Catch the challenge *before* the model runs. The changelog is explicit: if a consent error is raised inside a tool call instead, it reaches the model as an error string rather than the user as a redirect.

## Runtime tokens vs env tokens

![Pasted env MCP token vs Connect consent at owned route](https://cdn.otf-kit.dev/blog/vercel-connect-tanstack-ai-mcp-auth/inbody1-20260924d.png)

| | Pasted MCP token in env | Connect consent at the route |
|---|---|---|
| Where the secret lives | Deployment env, often copied into previews | Issued at runtime after `getConsentChallenge` redirect |
| Rotate | Change env, redeploy, invalidate every replica | Re-consent; old runtime token dies with the session |
| Blast radius | Anyone with env or a leaked preview | Bound to the user/session that consented |
| Agent code | Tempted to log headers | Transport handles OAuth; agent sees tools |
| Fits TanStack on Vercel | Works until the first leak | What `@vercel/connect/tanstack-ai` is for |

Env is fine for *which* Connect app you are, `$CONNECT_*` identifiers, and public MCP URLs. Env is not fine for the OAuth access token that talks to a customer’s MCP. If your runbook still says “rotate MCP_TOKEN quarterly,” you adopted a rename, not Connect.

## Sequence you can keep in your head

![TanStack agent to connectMCPTransport to consent to runtime token to OAuth MCP](https://cdn.otf-kit.dev/blog/vercel-connect-tanstack-ai-mcp-auth/inbody2-20260924d.png)

1. UI hits your owned agent route.
2. TanStack AI starts a turn and needs an MCP tool.
3. `connectMCPTransport` talks to the MCP server without a long-lived env bearer.
4. Server demands OAuth. Transport yields a consent challenge.
5. Route catches `getConsentChallenge`, redirects the browser.
6. User consents. Connect issues a runtime token at your callback path.
7. Agent retries the tool call. MCP sees a valid OAuth token. Env never held it.

If step 5 is “stuff the token into `$CONNECT_MCP_TOKEN` so we skip redirects in staging,” staging will ship to production.

## Hard split from the Claude MCP connector

We already covered [Expo MCP connector for Claude](https://otf-kit.dev/blog/expo-mcp-connector-claude): EAS builds, Claude, a connector aimed at that editor/runtime pair. This Vercel Connect + TanStack path is a different product surface — different package (`@vercel/connect/tanstack-ai`), host (Vercel route + TanStack), auth UX (`getConsentChallenge` on *your* web route), and secret home (runtime token at the Vercel boundary). Copy-pasting those snippets into a TanStack Vercel app gives you two half-wired OAuth stacks.


## Failure modes that are on you

Redirect loops mean the callback did not persist enough session state — fix the session, not the transport. Ignoring `getConsentChallenge` so the model “tries another tool” leaks partial work; abort the turn. Log that a challenge happened, not URL query params that may carry one-time codes. Use `connectMCPTransport` in local and preview too, or those environments invent env tokens again. Keep AI Gateway credentials (model inference) separate from MCP OAuth (tools) — see [AI Gateway model picks](https://otf-kit.dev/blog/mimo-v2-6-ai-gateway-builders).

## A minimal implementation checklist

Grounded in the changelog — not invented API:

```ts
import { chat, toServerSentEventsResponse } from '@tanstack/ai';
import { createMCPClient } from '@tanstack/ai-mcp';
import { vercelGatewayText } from '@tanstack/ai-vercel-gateway';
import {
  connectMCPTransport,
  getConsentChallenge,
} from '@vercel/connect/tanstack-ai';

export async function POST(request: Request) {
  const { messages } = await request.json();
  const userId = await getUserId(request);
  try {
    const linear = await createMCPClient({
      transport: connectMCPTransport(
        { type: 'http', url: process.env.CONNECT_MCP_URL! },
        'oauth/linear',
        { subject: { type: 'user', id: userId } },
      ),
    });
    const stream = chat({
      adapter: vercelGatewayText('anthropic/claude-opus-5'),
      messages,
      mcp: { clients: [linear] },
    });
    return toServerSentEventsResponse(stream);
  } catch (err) {
    const challenge = getConsentChallenge(err);
    if (challenge) return Response.redirect(challenge.url, 303);
    throw err;
  }
}
```

Checklist: `@vercel/connect/tanstack-ai` adapter; `connectMCPTransport`; catch `getConsentChallenge` on the agent route; owned-path consent callback; no MCP access token in env; deny disables tools (no static fallback); logs omit tokens. If the changelog is silent on a step, do not invent it — read [the entry](https://vercel.com/changelog/vercel-connect-tanstack-ai).

Owned-route auth is the pattern we want when an agent can spend money or read a private repo: the browser user is in the loop, the serverless function is the boundary, and the model is a guest. Install `@vercel/connect/tanstack-ai`, send tools through `connectMCPTransport`, catch `getConsentChallenge` and redirect, let tokens issue at runtime. Leave env for `$CONNECT_*` config. Keep the Claude connector in its own post. For a durable product surface under the agent churn, start from the [OTF templates](https://otf-kit.dev/templates) you own — Connect still owns the OAuth hop.

## Sources

- [Vercel Connect + TanStack AI changelog](https://vercel.com/changelog/vercel-connect-tanstack-ai)
- [AI Gateway delegation (OtF)](https://otf-kit.dev/blog/gpt-live-1-delegation-ai-gateway)
- [Expo MCP connector for Claude (OtF)](https://otf-kit.dev/blog/expo-mcp-connector-claude)
