Announcing CIMD support for MCP Client registration
Learn more

Tavily MCP vs Tavily API for AI Agents (2026)

TL;DR

  • Tavily's hosted MCP server exposes five tools that map onto five REST endpoints, but not the full parameter surface. include_answer, auto_parameters, include_usage, and safe_search exist on /search and are absent from the MCP tavily_search schema.
  • MCP auth accepts a Tavily API key in a query parameter, a Tavily API key in an Authorization header, or an optional OAuth flow. The OAuth flow does not mint a scoped delegated token; it selects which API key from the authorized account gets used. Your blast radius is the key, either way.
  • Research is the sharpest gap. The /research endpoint supports Server-Sent Events streaming and async polling by request_id. The MCP tavily_research tool takes two parameters and blocks. A pro research call can consume up to 250 credits, so no progress signal is an operational problem, not a UX preference.
  • Tavily is a builder-credential connector, not a user-identity connector. There is usually no "user's Tavily account" to delegate to. The credential problem is key blast radius, per-tenant cost attribution, and rate limit contention rather than per-user consent.
  • Scalekit's Tavily MCP connector keeps that key out of agent runtime and LLM context, scopes the tool surface per user, and logs every call. The MCP versus API decision does not change what you need at the credential layer.

Your agent needs live web data. Tavily ships a hosted MCP server at mcp.tavily.com and a REST API at api.tavily.com, and both give your agent search, extract, map, crawl, and research. The tool names look identical, which is exactly why the choice looks trivial and isn't. The MCP server exposes a narrower parameter surface, no streaming, no usage reporting, and an auth model that resolves to a flat API key no matter which door you walk through. Here's how to pick.

What Tavily MCP and Tavily API actually are

Two objects, one backend. Both paths terminate at the same Tavily infrastructure and bill against the same credit pool, so the differences that matter are in surface area and operational control rather than data quality.

Tavily MCP

The Tavily MCP server is built and maintained by Tavily. It runs hosted at https://mcp.tavily.com/mcp/ over Streamable HTTP, with a local stdio variant available through the tavily-mcp npm package. The current server exposes five tools: tavily_search, tavily_extract, tavily_map, tavily_crawl, and tavily_research. Tavily's own overview tab and repository README still describe an earlier four-tool set, so treat a live tool listing as the source of truth rather than the prose.

Authentication accepts your Tavily API key as a tavilyApiKey query parameter or as an Authorization: Bearer header. OAuth with Dynamic Client Registration is supported and explicitly optional.

Tavily API

The Tavily REST API is based at https://api.tavily.com and exposes /search, /extract, /crawl, /map, /research, and /usage. Every endpoint authenticates with a bearer API key prefixed tvly-. There is no OAuth authorization server, no scope model, and no per-endpoint credential.

Two request headers matter for production agents. X-Project-ID attaches usage to a named project so a single key can be attributed across applications. X-Session-Id and X-Human-Id group calls into a logical session and an anonymized end user.

Comparing them where it matters for agents

The interesting comparison is not "which tools exist." It is which knobs you lose, what breaks silently, and who owns the fix. Four dimensions, in the order they tend to bite.

What your agent can actually do

Both paths cover the same five operations. The MCP tavily_search tool exposes 15 parameters against the 20 documented on POST /search, and the five it drops are not decorative.

Capability
Tavily MCP
Tavily API
Web search with domain, date, and country filters
Yes (tavily_search)
Yes (/search)
LLM-generated answer in the response (include_answer)
No
Yes
Query-intent parameter selection (auto_parameters)
No
Yes
Snippet count control (chunks_per_source)
No
Yes
Per-request credit usage in the response (include_usage)
No
Yes
Enterprise safe search (safe_search)
No
Yes
Content extraction from URLs
Yes (tavily_extract)
Yes (/extract)
Site mapping and crawling
Yes (tavily_map, tavily_crawl)
Yes (/map, /crawl)
Deep research
Yes (tavily_research, 2 params)
Yes (/research, full params)
Streaming research progress over SSE
No
Yes
Async research polling by request_id
No
Yes
Credit and usage reporting
No
Yes (/usage)

Why the research gap is the expensive one

include_answer is a convenience you can replicate with your own model call. Research is different. Tavily prices /research dynamically: a mini call costs between 4 and 110 credits, a pro call between 15 and 250, per the credits documentation. At pay-as-you-go rates, one pro call can approach two dollars.

The API lets you set stream: true and consume Server-Sent Events, or fire and poll GET /research/{request_id}. The MCP tavily_research tool accepts input and model and returns when it returns. For a user-facing research agent, that is a blank screen for a minute or more. For a background pipeline, it is an in-flight request you cannot observe or cancel.

The auth path each one puts you on

This is where Tavily diverges from the Slack and Notion comparisons in this series, and where most write-ups get it wrong. Tavily MCP supports OAuth, which looks like delegated per-user access. It is not.

When a user completes the Tavily OAuth flow, the server picks an API key from their account using a naming convention: a key named mcp_auth_default in the personal account wins, then a team key of the same name, then the account's default key, then the first available key. The result is a full-privilege Tavily API key selected on the user's behalf. There is no scope, no per-tool restriction, and no downgrade path.

What the direct API asks of you instead

The REST path is honest about the same constraint. One bearer token, full account access, revoked by deleting the key from the Tavily dashboard. Keyless mode exists for search and extract with no account at all, which is useful for prototypes and useless for anything with a cost model.

What that means for a multi-tenant agent

Most Tavily agents run on the builder's key, not the end user's. Your customer has a Salesforce account and a Notion workspace; they almost certainly do not have a Tavily account. So the per-user consent story that dominates other connectors mostly does not apply here.

What replaces it is a different set of problems. One key serves every tenant, so a runaway crawl loop in one customer's workflow consumes the credits and the rate limit budget of all of them. One key in the agent runtime means one leak compromises every tenant. And when a customer asks what their agent actually searched last quarter, a shared key gives you nothing to answer with. For a deeper look at credential ownership across agent tool-calling patterns, the tradeoffs apply directly here.

What you own in production

On the MCP path, Tavily maintains the tool schemas, the transport, and the mapping onto its own endpoints. That is real value: no request construction, no response parsing, no schema library to version.

What you still own: the API key and where it lives, retry logic against 429 responses with the retry-after header, and credit budgeting with no include_usage field and no /usage tool to query. The rate limits are per key and per endpoint class, and they do not move because you called through MCP.

Limit
Development key
Production key
Default endpoints
100 RPM
1,000 RPM
Crawl endpoint
100 RPM
100 RPM
Research task creation
20 RPM
20 RPM
Usage endpoint
10 per 10 min
10 per 10 min

The schema stability question

MCP tool schemas are unversioned. They change when Tavily ships a server update, and the divergences are already observable: exact_match is documented as a boolean on /search and surfaces as a string in the MCP tool schema. That is a small thing until a model emits true and the call rejects.

The REST API has no version header, but it has a published OpenAPI document and an additive change history. For a deterministic pipeline where an unexpected parameter type is an incident, the API is the more predictable dependency. This is one reason MCP can be significantly more expensive than CLI in production contexts — the operational overhead adds up.

When to use MCP, when to use the API

Both are legitimate. The split is cleaner here than for most connectors because the capability gap maps almost exactly onto interactive versus deterministic.

Use Tavily MCP when:

  • You are wiring web access into an interactive agent in Claude Code, Cursor, or a chat assistant, and the user tolerates a blocking research call
  • Your agent already speaks MCP to three other tools and you want one transport rather than one SDK per provider
  • Search, extract, map, and crawl at default parameters cover the job, and you are not tuning chunks_per_source or auto_parameters per query
  • You are validating a research agent concept and do not want to write request construction and retry code yet

Use the Tavily API when:

  • Your agent runs /research in front of a user and needs SSE progress events, or runs it in a background job and needs to poll request_id rather than hold a connection open
  • You bill customers for research and need include_usage on every response plus /usage for reconciliation
  • You attribute cost per tenant with X-Project-ID, which is a direct-API header with no documented MCP passthrough
  • You are running high-volume crawls against the 100 RPM crawl ceiling and need explicit backoff on retry-after
  • You need safe_search on an enterprise plan, or auto_parameters to let Tavily tune depth per query

Building a Tavily agent with Scalekit

Scalekit ships a single Tavily connector, and it is the MCP flavor. Unlike Notion or GitHub, where the connector catalog carries separate REST and MCP entries, Tavily MCP is the one path. Tool names are prefixed: tavilymcp_tavily_search, tavilymcp_tavily_extract, tavilymcp_tavily_map, tavilymcp_tavily_crawl, and tavilymcp_tavily_research.

One note on the connection before you write code

Scalekit's docs tag the connector OAuth 2.1/DCR; the connector page describes a per-user Tavily API key held in the vault. Both are consistent with what the Tavily remote server accepts. Create the connection in the dashboard first under AgentKit > Connections, confirm which mode it uses, and copy the exact Connection name. That string must match your code exactly, and it is not always the provider slug.

Setting up and authorizing a connected account

Install the SDK, then create the connected account that will hold the credential. Nothing is callable until the account status is ACTIVE.

pip install scalekit-sdk-python langchain-openai langchain-core
import os import scalekit.client from dotenv import load_dotenv load_dotenv() scalekit_client = scalekit.client.ScalekitClient( client_id=os.environ["SCALEKIT_CLIENT_ID"], client_secret=os.environ["SCALEKIT_CLIENT_SECRET"], env_url=os.environ["SCALEKIT_ENV_URL"], ) actions = scalekit_client.actions # Must match the Connection name in AgentKit > Connections exactly CONNECTION_NAME = os.environ["TAVILY_CONNECTION_NAME"] # e.g. "tavilymcp" IDENTIFIER = "user_123" response = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=IDENTIFIER, ) connected_account = response.connected_account if connected_account.status != "ACTIVE": link_response = actions.get_authorization_link( connection_name=CONNECTION_NAME, identifier=IDENTIFIER, ) print("Authorize Tavily:", link_response.link) input("Press Enter after authorizing...") # In production, redirect the user and resume after the OAuth callback connected_account = actions.get_or_create_connected_account( connection_name=CONNECTION_NAME, identifier=IDENTIFIER, ).connected_account if connected_account.status != "ACTIVE": raise RuntimeError("Tavily is not ACTIVE. Complete authorization and retry.")

Retrieving the authorized tool surface

Before the agent loop runs, retrieve the tools this connected account is authorized to call. This is not tool discovery across an unknown catalog. list_scoped_tools returns a deterministic surface derived from what this user actually authorized, which is what keeps a five-tool research agent from carrying a hundred-tool context.

from google.protobuf.json_format import MessageToDict scoped_response, _ = actions.tools.list_scoped_tools( identifier=IDENTIFIER, filter={"connection_names": [CONNECTION_NAME]}, page_size=100, ) for scoped_tool in scoped_response.tools: definition = MessageToDict(scoped_tool.tool).get("definition", {}) print(definition.get("name"), definition.get("input_schema"))

Running the agent loop with LangChain

Scalekit returns native LangChain StructuredTool objects, so there is no schema reshaping between the connector and the model. This pattern fits naturally into LangChain tool calling workflows without any additional translation layer.

from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, ToolMessage tools = actions.langchain.get_tools( identifier=IDENTIFIER, connection_names=[CONNECTION_NAME], page_size=100, ) tool_map = {t.name: t for t in tools} llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ HumanMessage( "Find pricing changes announced by competitor.com in the last 30 days, " "then extract the full content of the two most relevant pages." ) ] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: result = tool_map[tc["name"]].invoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

Calling a single tool directly in TypeScript

When you already know which tool to call, executeTool skips the model round trip. Tool output lives under data, not at the top level of the response.

import { ScalekitClient } from '@scalekit-sdk/node'; import 'dotenv/config'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENV_URL!, process.env.SCALEKIT_CLIENT_ID!, process.env.SCALEKIT_CLIENT_SECRET!, ); const result = await scalekit.actions.executeTool({ toolName: 'tavilymcp_tavily_search', identifier: 'user_123', connector: process.env.TAVILY_CONNECTION_NAME!, toolInput: { query: 'competitor.com pricing changes', search_depth: 'advanced', time_range: 'month', include_domains: ['competitor.com'], max_results: 5, }, }); console.log(result.data);

Wiring the scoped surface into the Claude SDK

The same scoped list feeds Anthropic's tool-use format without transformation. Retrieve, map, pass to messages.create, then execute what comes back.

import Anthropic from '@anthropic-ai/sdk'; const anthropic = new Anthropic(); const { tools } = await scalekit.tools.listScopedTools('user_123', { filter: { connectionNames: [process.env.TAVILY_CONNECTION_NAME!] }, pageSize: 100, }); const llmTools = tools.map((t) => ({ name: t.tool.definition.name, description: t.tool.definition.description, input_schema: t.tool.definition.input_schema, })); const messages: Anthropic.MessageParam[] = [ { role: 'user', content: 'Map competitor.com/docs and extract the pricing page.' }, ]; const response = await anthropic.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, tools: llmTools, messages, }); for (const block of response.content) { if (block.type === 'tool_use') { const toolResult = await scalekit.actions.executeTool({ toolName: block.name, identifier: 'user_123', toolInput: block.input as Record, }); messages.push({ role: 'assistant', content: response.content }); messages.push({ role: 'user', content: [ { type: 'tool_result', tool_use_id: block.id, content: JSON.stringify(toolResult.data), }, ], }); } }

Virtual MCP for multi-tool and multi-tenant research agents

A research agent rarely calls Tavily alone. It searches the web, then writes to Notion, posts to Slack, or updates a CRM record. Point it at four MCP servers and you have four endpoints, four credential lifecycles, and every tool from every server in context.

Why a raw Tavily MCP connection overexposes the agent

A competitor-monitoring agent needs tavilymcp_tavily_search and tavilymcp_tavily_extract. Handing it the full connector also hands it tavilymcp_tavily_crawl and tavilymcp_tavily_research, the two most expensive tools Tavily sells. A model that decides to crawl a documentation site instead of searching it turns a one-credit call into a hundred-credit one. That is not a hypothetical failure mode; it is what happens when a decision space includes options the task never needed. Understanding tool calling auth production problems and anti-patterns makes clear why scoping the surface is essential before you ship.

One server definition, per-user session tokens

Virtual MCP Servers let you declare exactly which connections and which tools an agent role can see. You create the server once per role and get a static mcp_server_url. Before each run, you mint a short-lived session token bound to one user.

from datetime import timedelta from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping vmcp = scalekit_client.actions.mcp.create_config( name="competitor-monitor-agent", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name=CONNECTION_NAME, tools=["tavilymcp_tavily_search", "tavilymcp_tavily_extract"], ), McpConfigConnectionToolMapping( connection_name="slack", tools=["slack_send_message"], ), ], ) config_id = vmcp.config.id mcp_server_url = vmcp.config.mcp_server_url

Minting the token before each run

Check that every connection is still active, then mint. Never reuse a token across runs.

accounts = scalekit_client.actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier=IDENTIFIER, include_auth_link=True, ) for account in accounts.connected_accounts: if account.connected_account_status != "ACTIVE": raise RuntimeError( f"{account.connection_name} needs auth: {account.authentication_link}" ) token_response = scalekit_client.actions.mcp.create_session_token( mcp_config_id=config_id, identifier=IDENTIFIER, expiry=timedelta(minutes=30), ) mcp_server = { "url": mcp_server_url, "headers": {"Authorization": f"Bearer {token_response.token}"}, }

The endpoint is static; the identity is not. That is the property that makes one agent definition safe to run for every tenant. Setup details are in the Virtual MCP setup guide.

Observability for downstream Tavily calls

Tavily's own attribution is thin on the MCP path. The remote server generates X-Session-Id per MCP session and forwards X-Human-Id only if your client supplies it. X-Project-ID has no documented MCP passthrough. So on a shared key, Tavily's dashboard shows you aggregate credit burn and very little else.

What the auth log actually answers

Scalekit logs the tool call on your side of the boundary. The Tavily connector page describes a 90-day audit trail with per-call attribution: which user triggered it, which tool ran, and what came back. That is the record that answers the three questions that arrive in production. Which tenant burned 40,000 credits last Tuesday. Which agent run crawled a competitor's entire site. Whether the search that produced a hallucinated citation actually returned that source. Audit trails for agent auth are non-negotiable in B2B SaaS, and a shared key makes them nearly impossible to build without an intermediary layer.

Credentials never reach the runtime

The Tavily key is resolved server-side at request time and injected into the outbound call. It does not sit in an environment variable next to your agent, does not enter the LLM context window, and does not appear in your application logs. Rotation and revocation are dashboard operations rather than a redeploy.

The credential problem that exists on both paths

Both paths hand you a Tavily API key and nothing else. No vault, no rotation, no per-tenant namespacing, no revocation workflow. Choosing MCP changes the transport and leaves the credential exactly where it was.

Why Tavily's version of this is different

For Slack or Notion, the N-credential problem is N users authorizing N accounts. For Tavily, the common shape is the inverse: one key serving N tenants. That concentration is the risk. A leaked key exposes every tenant's search history and every remaining credit. A single misbehaving agent run exhausts the 1,000 RPM production ceiling for everyone. And where enterprise customers do bring their own Tavily key, you are back to per-tenant credentials with no isolation primitive in either path. This is the same class of problem covered in how tool calling auth changes when you move from single-tenant to multi-tenant.

Where Scalekit fits

Scalekit's Tavily MCP connector holds the credential in a per-tenant namespaced vault, resolves it server-side on every call, and scopes the tool surface per connected account. The same infrastructure works whether you settled on the MCP path or a direct API integration through a custom tool, so the transport decision stops being an auth decision.

Which one to build against

If your agent is interactive, calls search and extract at default parameters, and already speaks MCP to other tools, use Tavily MCP. The tool schemas are maintained upstream and you will be running in an afternoon.

If your agent runs /research in front of a user or on a schedule, bills customers for credits, attributes cost per tenant, or needs auto_parameters and include_usage, use the API directly. Streaming, polling, and usage reporting are structural absences on the MCP surface, not roadmap items you can plan around.

The mix most teams land on

Most production research agents run both: MCP for interactive search inside the assistant, direct API for the expensive research jobs that need progress events and cost accounting. Whichever mix you land on, the key still needs a vault, per-tenant isolation, and an audit trail, and that is the part no transport gives you. The token vault is the critical piece of infrastructure that makes this model work at scale.

Build with us

Browse the Scalekit Tavily MCP connector docs or the Tavily connector page to see the full tool surface.

Building a Tavily research agent and hitting something specific? Join the Scalekit Slack community and ask, or talk to an engineer if you need help now.

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
zero upfront cost.

Every feature unlocked. No hidden fees.