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

# Unstructured Transform MCP server installation for Vercel AI SDK

> Learn how to connect the Unstructured Transform MCP server to the Vercel AI SDK. Your agents can then point to your files and have Transform start producing partitioned, enriched, chunked, and embedded data based on your files in minutes.

The [Vercel AI SDK](https://ai-sdk.dev) is an open-source TypeScript toolkit for building AI applications and agents.
Unlike end-user AI tools that add the Transform MCP server through a settings screen, the AI SDK connects to it *from
code*: you load the Transform MCP server's tools through the SDK's built-in MCP client and hand them to a model call
such as `generateText` or `streamText`. Because Transform is a hosted *remote* MCP server, there is nothing to install
or run locally: you point the client at the server URL and authenticate with your Unstructured API key.

<Tip>
  If you want a running app rather than a script, deploy the ready-made
  [Transform MCP × Vercel AI SDK template](https://github.com/Unstructured-IO/unstructured-mcp-integrations/tree/main/transform/vercel-ai-sdk). It is a
  Next.js chat app wired to the Transform MCP server, with a one-click **Deploy to Vercel** button that prompts for the
  two API keys below.
</Tip>

## Requirements

You will need:

* Node.js 18 or later on your local development machine. To check, in your terminal, run `node --version`. [Install Node.js](https://nodejs.org/en/download).
* An Unstructured API key, which the Transform MCP server uses as a bearer token. [Get an API key](/transform/code#get-your-unstructured-api-key-and-url).
* An Anthropic API key, or another model provider that the AI SDK supports, for the agent's underlying chat model.

## Install the packages

In your terminal, install the AI SDK, the Anthropic provider, the AI SDK MCP client, and `zod` for the local tool
schema. `tsx` runs the TypeScript example directly:

```bash theme={null}
npm install ai @ai-sdk/anthropic @ai-sdk/mcp zod
npm install --save-dev tsx
```

This guide was verified against `ai@7`, `@ai-sdk/anthropic@4`, and `@ai-sdk/mcp@2`. Newer versions typically work, but
check the [AI SDK release notes](https://github.com/vercel/ai/releases) if an API has changed.

## Connect to the Transform MCP server

The Transform MCP server speaks the streamable HTTP transport and authenticates with your Unstructured API key sent as
a bearer token. Store your key in an environment variable rather than hard-coding it:

```bash theme={null}
export UNSTRUCTURED_API_KEY="<your-unstructured-api-key>"
```

Create an MCP client with the `http` transport, then load the server's tools with `mcpClient.tools()`:

```ts theme={null}
import { createMCPClient } from '@ai-sdk/mcp';

const mcpClient = await createMCPClient({
  transport: {
    type: 'http',
    url: 'https://mcp.transform.unstructured.io',
    headers: { Authorization: `Bearer ${process.env.UNSTRUCTURED_API_KEY}` },
  },
});

// Loads the Transform tools: request_file_upload_url, transform_files,
// check_transform_status, and get_transform_results.
const tools = await mcpClient.tools();
```

<Tip>
  Always close the client with `await mcpClient.close()` when the request finishes (use a `finally` block, or
  `streamText`'s `onFinish` callback) so the connection is released.
</Tip>

### Add local helper tools for waiting and downloading

Transform's job lifecycle needs two things the MCP tools do not cover on their own. First, transforms run as **async
jobs**, so the agent must pace its status polling; without a pause it will call `check_transform_status` many times in
a row and exhaust its step budget before the job finishes. Second, `get_transform_results` returns a pre-signed
`download_url` rather than the parsed text inline, so retrieving the content is a plain HTTP `GET`. The example below
registers two small local tools alongside the MCP tools:

* `wait`: Pauses between status checks so a multi-second transform has time to complete.
* `downloadText`: Fetches the finished Markdown from the pre-signed `download_url` with an HTTP `GET`.

Pre-signed URLs carry their own credentials in the URL itself. The `downloadText` tool must not send the
`Authorization` header, or the storage service rejects the request. It also restricts downloads to the
Transform host, so a crafted prompt cannot turn the tool into a server-side request forgery (SSRF) vector.

### Use the tools in an agent

The following example connects to the Transform MCP server, registers the two helper tools, and lets the model drive
the full job lifecycle: start the transform, poll for status with paced waits, fetch the results, and download the
Markdown output. Save it as `transform-agent.ts`:

```ts theme={null}
import { createMCPClient } from '@ai-sdk/mcp';
import { anthropic } from '@ai-sdk/anthropic';
import { generateText, stepCountIs, tool } from 'ai';
import { z } from 'zod';

const TRANSFORM_MCP_URL = 'https://mcp.transform.unstructured.io';

const SYSTEM_PROMPT = `You parse documents with Unstructured's Transform MCP tools.
Transforms are ASYNC jobs. Follow this exact sequence:
1. Call transform_files with the document URL, output format markdown.
2. Call the "wait" tool (a few seconds), THEN check_transform_status. Repeat
   wait -> check until the status is COMPLETED. Never poll without waiting first.
3. Call get_transform_results, then downloadText on the first file's download_url
   to read the Markdown. Summarize the content for the user.`;

const wait = tool({
  description:
    'Pause for a few seconds before polling an async job again. Use between check_transform_status calls.',
  inputSchema: z.object({ seconds: z.number().min(1).max(10) }),
  execute: async ({ seconds }) => {
    await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
    return { waited: seconds };
  },
});

// Restrict downloads to the Transform host (where the pre-signed download_url is
// served) so a crafted prompt cannot turn this tool into an SSRF vector.
const ALLOWED_DOWNLOAD_HOST = new URL(TRANSFORM_MCP_URL).host;

const downloadText = tool({
  description:
    'Download transformed output from a pre-signed download_url with an HTTP GET.',
  inputSchema: z.object({ url: z.string().url() }),
  execute: async ({ url }) => {
    const parsed = new URL(url);
    if (parsed.protocol !== 'https:' || parsed.host !== ALLOWED_DOWNLOAD_HOST) {
      throw new Error(`Refusing to download from "${parsed.host}".`);
    }
    // The URL is pre-signed: do not send an Authorization header here.
    const response = await fetch(parsed);
    if (!response.ok) throw new Error(`Download failed: ${response.status}`);
    return await response.text();
  },
});

async function main() {
  const mcpClient = await createMCPClient({
    transport: {
      type: 'http',
      url: TRANSFORM_MCP_URL,
      headers: { Authorization: `Bearer ${process.env.UNSTRUCTURED_API_KEY}` },
    },
  });

  try {
    const tools = { ...(await mcpClient.tools()), wait, downloadText };

    const { text } = await generateText({
      model: anthropic('claude-opus-4-8'),
      system: SYSTEM_PROMPT,
      tools,
      // Async jobs need several wait -> poll cycles; give the loop room.
      stopWhen: stepCountIs(25),
      prompt:
        'Parse this document into clean markdown and summarize its sections. ' +
        'URL: https://raw.githubusercontent.com/Unstructured-IO/unstructured/main/example-docs/pdf/layout-parser-paper.pdf',
    });

    console.log(text);
  } finally {
    await mcpClient.close();
  }
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
```

To use a different model provider, swap `anthropic('claude-opus-4-8')` for any
[provider the AI SDK supports](https://ai-sdk.dev/providers/ai-sdk-providers), and set that provider's API key.

## Parse your source files

Parsing requests have the following limits:

* Each file must be of a [supported file type](/transform/supported-file-types).
* Each file must be 50 MB or less in size.
* Each request must have 10 files or fewer.
* Only 5 requests can be running at a time.

The Transform MCP server is designed to report these limits back to the agent through its tool responses. Because of
this, your agent should notify you whenever it encounters a file that exceeds 50 MB in size, and it should formulate
strategies to send requests that are 10 files or fewer and not cause more than 5 requests to be running at a time. You
can reinforce this behavior in the system prompt, as the example above does for the polling sequence.

### Run the agent

Set your model provider's key, then run the example. Transforms take from about 10 seconds to several minutes,
depending on page count:

```bash theme={null}
export ANTHROPIC_API_KEY="<your-anthropic-api-key>"

npx tsx transform-agent.ts
```

The agent works through the tool sequence — `transform_files`, paced `wait` and `check_transform_status` cycles,
`get_transform_results`, and `downloadText` — then prints a summary of the parsed Markdown.

## Troubleshooting

* **`401 Unauthorized` or an `invalid_token` error on connect.** Confirm the `UNSTRUCTURED_API_KEY` environment
  variable is set and that the `Authorization` header is formatted as `Bearer <your-unstructured-api-key>`.
* **`404` or a not-found error when connecting.** Verify the server URL is exactly
  `https://mcp.transform.unstructured.io`, with no path such as `/mcp` appended.
* **The agent stops before the job finishes.** Each model step counts toward `stopWhen: stepCountIs(...)`, and each
  poll cycle uses two steps (`wait` plus `check_transform_status`). Raise the step count, or increase the seconds the
  `wait` tool pauses, for longer transforms.
* **The download request is rejected.** The `download_url` value is pre-signed. Send that request without the
  `Authorization` header.
* **A hanging or leaked connection.** Call `await mcpClient.close()` when the request finishes, in a `finally` block or
  `streamText`'s `onFinish` callback.

## Next steps

* [Control Transform file parsing output](/transform/output): Control how the Unstructured Transform MCP server instructs Transform to partition, enrich, chunk, and embed the data based on your files.
* [Control Transform generated sample code](/transform/code): Control how the Unstructured Transform MCP server generates sample curl or Python code that demonstrates how to use Transform to partition, enrich, chunk, and embed the data based on your files.

## Questions? Need help?

* For technical support, [request support](/support/request).
