# How to Add Documentation Search to a Vercel AI SDK Agent

URL: https://context7.com/vercel-ai-sdk-documentation-search
Updated: 2026-10-12

Add a documentation search tool to a Vercel AI SDK agent with the Context7 tools package or a custom tool() that calls the Context7 Search API. Code included.

To add documentation search to a Vercel AI SDK agent, give it a tool that returns current library docs and let the model call it in a multi-step loop. With Context7 there are two ways to do that: install `@upstash/context7-tools-ai-sdk` and pass its `resolveLibraryId` and `queryDocs` tools (or use its ready-made Context7Agent), or write your own tool() whose execute function calls the Context7 Search API at https://context7.com/api/v3/search.

General web search tools such as Exa and Tavily also plug into the AI SDK, and they are the better pick when the answer lives on the open web. For questions about a library's API, a documentation index returns a bounded set of snippets from that library's docs and lets you pin the version your project uses. The code below targets AI SDK 7, where the stop condition helper is called `isStepCount` (it was `stepCountIs` in AI SDK 6).

## Option 1: Use the Context7 Tools Package

The official package is `@upstash/context7-tools-ai-sdk`. It gives you two AI SDK tools: `resolveLibraryId`, which turns a name like next.js into a Context7 library ID, and `queryDocs`, which fetches documentation for that ID. Install it with `@upstash/context7-sdk`, ai (version 6 or later) and zod (version 4 or later).

Both tools read your API key from the `CONTEXT7_API_KEY` environment variable, or you can pass it as `resolveLibraryId({ apiKey })` and `queryDocs({ apiKey })`. The package needs a key, so create one in the dashboard first. Keys start with ctx7sk-. The examples use AI Gateway model strings like anthropic/claude-sonnet-5.5, which authenticate with `AI_GATEWAY_API_KEY` or Vercel OIDC; any AI SDK provider model works in their place.

Give the model room for at least three steps: one to resolve the library, one to query the docs, and one to write the answer. `generateText` stops after a single step by default, so without `stopWhen` you get the tool call back and no final text.

If you do not want to wire the tools yourself, Context7Agent is a ToolLoopAgent subclass that comes with both tools, a system prompt that tells the model to resolve the library first, and a default stop condition of five steps. You can pass your own instructions, `stopWhen`, extra tools or `apiKey`, and any other ToolLoopAgent setting goes straight through.

```bash
npm install @upstash/context7-tools-ai-sdk @upstash/context7-sdk ai zod
export CONTEXT7_API_KEY=ctx7sk-...
```

```ts
import { resolveLibraryId, queryDocs } from "@upstash/context7-tools-ai-sdk";
import { generateText, isStepCount } from "ai";

const { text } = await generateText({
  model: "anthropic/claude-sonnet-5.5",
  prompt: "How do I add middleware in Next.js 15?",
  tools: {
    resolveLibraryId: resolveLibraryId(),
    queryDocs: queryDocs(),
  },
  stopWhen: isStepCount(5),
});

console.log(text);
```

```ts
import { Context7Agent } from "@upstash/context7-tools-ai-sdk";

const agent = new Context7Agent({
  model: "anthropic/claude-sonnet-5.5",
});

const { text } = await agent.generate({
  prompt: "How do I set up routing in Next.js?",
});

console.log(text);
```

## Option 2: Write Your Own Tool With the Search API

Write the tool yourself when you want control over its name, description, input schema and output. The Context7 Search API answers a documentation question in one GET request: it picks up to four relevant libraries, searches them in parallel, reranks the results and returns up to eight. That is one tool call per lookup instead of two.

The tool below takes a query, plus an optional library and version. It sends your API key from process.env when there is one. Without a key the API still answers, but anonymous requests are rate-limited. It returns plain text, which is the API's default format and goes straight into the model's context.

Errors come back as text too. A 404 means no documentation matched, and telling the model that lets it retry with a different library name instead of failing the run.

Then hand the tool to a ToolLoopAgent, or pass it to `generateText` or `streamText` with the same tools and `stopWhen` options. ToolLoopAgent stops after 20 steps by default; a docs lookup should not need more than five, so set a lower limit.

If you already use `@upstash/context7-sdk`, its search() method calls the same endpoint and takes libraries, version, language and type options. The SDK needs an API key.

```ts
// searchDocs.ts
import { tool } from "ai";
import { z } from "zod";

type SearchInput = { query: string; library?: string; version?: string };

export async function searchContext7({ query, library, version }: SearchInput) {
  const params = new URLSearchParams({ query });
  if (library) {
    params.append("library", library);
    if (version) params.append("version", version);
  }

  const apiKey = process.env.CONTEXT7_API_KEY;
  const res = await fetch(`https://context7.com/api/v3/search?${params}`, {
    headers: apiKey ? { Authorization: `Bearer ${apiKey}` } : {},
  });

  if (res.status === 404) {
    return "No documentation matched. Try another library name or a shorter query.";
  }
  if (!res.ok) {
    return `Documentation search failed with status ${res.status}.`;
  }
  return res.text();
}

export const searchDocs = tool({
  description:
    "Search up-to-date documentation for programming libraries and frameworks. " +
    "Call this before writing code that uses a third-party library API, " +
    "and whenever the user names a library. Returns doc snippets and code examples.",
  inputSchema: z.object({
    query: z
      .string()
      .describe("What to look up, as a short phrase, e.g. 'middleware matcher config'"),
    library: z
      .string()
      .optional()
      .describe("Library name or Context7 ID, e.g. 'next.js' or '/vercel/next.js'"),
    version: z
      .string()
      .optional()
      .describe("Library version, e.g. '15.2'. Only set it together with library."),
  }),
  execute: (input) => searchContext7(input),
});
```

```ts
import { ToolLoopAgent, isStepCount } from "ai";
import { searchDocs } from "./searchDocs";

const docsAgent = new ToolLoopAgent({
  model: "anthropic/claude-sonnet-5.5",
  instructions:
    "You write TypeScript. Before you use a third-party library API, call searchDocs " +
    "and follow what it returns. If the docs disagree with what you remember, trust the docs.",
  tools: { searchDocs },
  stopWhen: isStepCount(5),
});

const { text } = await docsAgent.generate({
  prompt: "Add rate limiting to a Next.js route handler with @upstash/ratelimit.",
});

console.log(text);
```

## How Do I Pin a Library Version?

Version pinning is where a docs index pays off. A model that learned Next.js 14 will write Next.js 14 code into a Next.js 15 project, and the code will often compile. Passing the version stops that.

With the Search API, send library and version together. The version parameter accepts forms like 15, 15.1 or v15.1.0. With one library, documentation for that version is preferred. With several libraries, the version is only a preference, and without a library it applies only when the query names a single product. When the version matters, pass one library by its exact ID, such as /vercel/next.js.

Check what you got. In a text response, the Library line names the version that answered, for example /vercel/next.js/v15.1.11. If that version is not indexed, the response starts with a version note and serves current documentation instead of failing.

Do not leave the version to the model if you can avoid it. Read it from `package.json` in your own code and bake it into a narrower tool, like `searchNextDocs` below. The model then only chooses the query.

With the tools package, `queryDocs` accepts a versioned library ID in the org/project/version form, such as /vercel/next.js/v15.1.11, so you can name the version in the prompt or the agent instructions.

```bash
curl -G "https://context7.com/api/v3/search" \
  --data-urlencode "query=middleware matcher config" \
  --data-urlencode "library=/vercel/next.js" \
  --data-urlencode "version=15.1" \
  -H "Authorization: Bearer $CONTEXT7_API_KEY"
```

```ts
import { tool } from "ai";
import { z } from "zod";
import { searchContext7 } from "./searchDocs";

// Read this from package.json in your own setup.
const NEXT_VERSION = "15.1";

export const searchNextDocs = tool({
  description: `Search the Next.js ${NEXT_VERSION} documentation. Use it for routing, data fetching, caching and config questions.`,
  inputSchema: z.object({
    query: z.string().describe("What to look up, in a few words"),
  }),
  execute: ({ query }) =>
    searchContext7({ query, library: "/vercel/next.js", version: NEXT_VERSION }),
});
```

## Context7 vs Exa and Tavily Tools in an AI SDK Agent

Exa and Tavily both publish AI SDK tool packages, listed in the AI SDK tools registry: `webSearch` from `@exalabs/ai-sdk` and `tavilySearch` from `@tavily/ai-sdk`. They search the live web. Context7 searches an index of documentation sources that were parsed ahead of time, and it does not fetch arbitrary pages at query time.

| Feature          | Context7                                                                       | Exa                                                                     | Tavily                                                 |
| ---------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------- | ------------------------------------------------------ |
| What it searches | A pre-built index of library and framework documentation                       | The live web                                                            | The live web                                           |
| AI SDK tool      | `@upstash/context7-tools-ai-sdk`, or your own tool() on the Search API         | `webSearch()` from `@exalabs/ai-sdk`                                    | `tavilySearch()` from `@tavily/ai-sdk`                 |
| Live web content | No; returns snippets from indexed docs                                         | Yes; can live-crawl result pages for full text, highlights or summaries | Yes; extract, crawl and map tools read specific sites  |
| Version pinning  | library and version parameters, for indexed versions                           | No version concept; filter by domain                                    | No version concept; filter by domain                   |
| API key          | `CONTEXT7_API_KEY`; optional for the Search API, required by the tools package | `EXA_API_KEY`                                                           | `TAVILY_API_KEY`                                       |
| Best for         | Library APIs, setup steps and code examples for a given version                | Open web research and reading specific pages                            | Web research and extracting or crawling specific sites |

## When Should an AI SDK Agent Use Web Search Instead?

Use a web search tool when the answer is not in any library's docs: a bug report from last week, a blog post comparing two approaches, a vendor status page, or a site Context7 has not indexed. Exa and Tavily both let you restrict results to chosen domains, which narrows them toward a docs site. You still get web pages back, though, not snippets tied to a library and version.

Many agents end up with both: Context7 for library APIs and a web search tool for everything else. Give each tool a description that says when to use it, so the model does not send an API question to the web or a news question to the docs index.

## Tips for Tool Descriptions and Token Budgets

Write the description for the model. The model picks tools from their descriptions, so say when to call the tool (before using a third-party library API, or when the user names a library) and what it returns. A vague description gets the tool called at random or not at all.

Describe every input field with .describe() and include an example. next.js or /vercel/next.js for the library and 15.2 for the version tell the model more than a type does.

Keep queries short. One concept per call retrieves better than a paragraph with three questions in it. If a task spans several libraries, the Search API takes up to four library values in one request; repeat the library parameter.

Set a stop condition you can afford. Every step is a full model call with the earlier tool results in the context, so a long loop over doc snippets adds up quickly. `isStepCount(5)` covers search, a retry and the answer.

Return text unless your code needs fields. The default txt format is already laid out for a model to read. Ask for type=json when you want to filter `codeSnippets` and `infoSnippets`, log library IDs, or trim the result before it reaches the model.

Pass language when you know it, such as TypeScript. It is a soft preference: examples in that language rank first, and language-neutral docs are still included.

Count your calls. Each Search API request is one API call, even when it searches four libraries. The tools package makes two calls per lookup, one to resolve the library and one to query it. There is a free monthly allowance. Pro is $10 per seat per month with 2,000 API calls per seat, then $5 per 1,000 calls; extra calls are billed, not blocked, unless you set a spending limit.

## Frequently asked questions

### How do I add a documentation search tool to a Vercel AI SDK agent?

Define a tool with tool() from the ai package, give it a zod `inputSchema` and an execute function that fetches docs, and pass it in the tools option of ToolLoopAgent, `generateText` or `streamText` with a `stopWhen` condition such as `isStepCount(5)`. With Context7 you can use the ready-made `resolveLibraryId` and `queryDocs` tools from `@upstash/context7-tools-ai-sdk`, or write one tool that calls https://context7.com/api/v3/search.

### What is the best documentation search tool for a Vercel AI SDK agent?

For library and framework APIs, use a documentation index such as Context7, because it returns doc snippets and code examples and can pin a library version. For open-web research, recent news or pages outside any library's docs, use a web search tool such as Exa or Tavily. Many agents use one of each.

### Why does my AI SDK agent call the tool but return no answer?

`generateText` and `streamText` stop after one step by default, so the run ends right after the tool call. Set `stopWhen` to a step limit such as `isStepCount(5)` so the model can read the tool result and write a final answer. ToolLoopAgent defaults to 20 steps, so this mostly affects direct `generateText` calls.

### Do I need an API key to use Context7 with the AI SDK?

The `@upstash/context7-tools-ai-sdk` package needs one, read from `CONTEXT7_API_KEY` or passed as `apiKey`. The Search API also answers without a key, but anonymous requests are rate-limited. A free key from the Context7 dashboard comes with a monthly allowance.

### Should I use `stepCountIs` or `isStepCount` in the AI SDK?

Use `isStepCount`. AI SDK 7 renamed `stepCountIs` to `isStepCount`, and the old name is still exported as an alias in AI SDK 7.0. If you are on AI SDK 6, `stepCountIs` is the only name available.

## Related guides

- [Search APIs for coding agents](https://context7.com/search-api-for-coding-agents.md): Compare Exa, Tavily, Brave Search, Firecrawl, and Context7 for coding agents.
- [Safer search APIs for coding agents](https://context7.com/prompt-injection-safe-search-api.md): Limit prompt injection by narrowing what your coding agent can read.
- [Versioned library docs for AI tools](https://context7.com/versioned-library-documentation.md): Pin documentation to the exact library version your project uses.
- [Up-to-date docs for coding agents](https://context7.com/up-to-date-documentation-for-coding-agents.md): Understand why coding agents need current, version-specific documentation.