Skip to content
Merged
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@arcadeai:registry=https://registry.npmjs.org/
42 changes: 42 additions & 0 deletions app/_data/partner-toolkits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Toolkit } from "@arcadeai/design-system";

/**
* Docs-local partner toolkits (remote MCP Servers offered by our partners).
*
* These entries are merged into the live integrations catalog alongside the
* TOOLKITS exported from @arcadeai/design-system. Each partner toolkit uses
* a standard ToolkitType (typically "verified") plus an `isPartner: true`
* flag that renders a Partner badge next to BYOC/Pro on catalog cards.
*
* Once DS adds an explicit `isPartner` field to its Toolkit shape, migrate
* these entries into the DS TOOLKITS array and delete this file.
*/

export type PartnerToolkit = Toolkit & {
isPartner: true;
/**
* Remote MCP Server URL displayed on the partner detail page. Use a
* placeholder like `YOUR_API_KEY` for any per-user secrets so the URL can
* be copied without leaking credentials. Updating this field updates the
* detail page automatically.
*/
mcpUrl: string;
};

export const PARTNER_TOOLKITS: PartnerToolkit[] = [
{
id: "Tavily",
label: "Tavily",
category: "search",
publicIconUrl: "/images/partners/tavily.svg",
isBYOC: true,
isPro: false,
isPartner: true,
type: "verified",
mcpUrl: "https://mcp.tavily.com/mcp/?tavilyApiKey=YOUR_API_KEY",
docsLink: "https://docs.arcade.dev/en/resources/integrations/search/tavily",
relativeDocsLink: "/en/resources/integrations/search/tavily",
isComingSoon: false,
isHidden: false,
},
];
14 changes: 9 additions & 5 deletions app/_lib/toolkit-slug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ export type ToolkitSlugSource = {
};

/**
* Toolkit with an optional docsLink property.
* The design-system `Toolkit` type doesn't include `docsLink` in its
* type definitions, but some entries carry it at runtime. This type
* makes the property explicit so both server and client code can share it.
* Toolkit with optional `docsLink` and `isPartner` properties.
* The design-system `Toolkit` type doesn't include either field, but some
* docs-local entries carry them at runtime (e.g. partner toolkits that
* render a Partner badge on cards). This type makes the properties explicit
* so both server and client code can share it.
*/
export type ToolkitWithDocsLink = Toolkit & { docsLink?: string | null };
export type ToolkitWithDocsLink = Toolkit & {
docsLink?: string | null;
isPartner?: boolean;
};

/**
* Strip all non-alphanumeric characters and lowercase.
Expand Down
16 changes: 14 additions & 2 deletions app/en/resources/integrations/components/tool-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
type ToolkitType,
} from "@arcadeai/design-system";
import { cn } from "@arcadeai/design-system/lib/utils";
import { Package } from "lucide-react";
import { Handshake, Package } from "lucide-react";
import Link from "next/link";
import posthog from "posthog-js";
import type React from "react";
Expand All @@ -26,6 +26,7 @@ type ToolCardProps = {
isComingSoon?: boolean;
isByoc?: boolean;
isPro?: boolean;
isPartner?: boolean;
};

export const ToolCard: React.FC<ToolCardProps> = ({
Expand All @@ -37,6 +38,7 @@ export const ToolCard: React.FC<ToolCardProps> = ({
isComingSoon = false,
isByoc = false,
isPro = false,
isPartner = false,
}) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const {
Expand All @@ -45,7 +47,7 @@ export const ToolCard: React.FC<ToolCardProps> = ({
icon: IconComponent,
color,
} = TOOL_CARD_TYPE_CONFIG[type];
const showHeaderBadges = isByoc || isPro || isComingSoon;
const showHeaderBadges = isByoc || isPro || isPartner || isComingSoon;

const trackToolCardClick = () => {
posthog.capture("Tool card clicked", {
Expand All @@ -55,6 +57,7 @@ export const ToolCard: React.FC<ToolCardProps> = ({
is_coming_soon: isComingSoon,
is_byoc: isByoc,
is_pro: isPro,
is_partner: isPartner,
});
};

Expand Down Expand Up @@ -131,6 +134,15 @@ export const ToolCard: React.FC<ToolCardProps> = ({
)}
{isByoc && <ByocBadge className="gap-1.5 rounded-md" />}
{isPro && <ProBadge className="gap-1.5 rounded-md" />}
{isPartner && (
<Badge
className="shrink-0 gap-1.5 whitespace-nowrap rounded-md border-pink-400/50 bg-pink-500/10 text-pink-700 dark:border-pink-400/50 dark:bg-pink-400/10 dark:text-pink-300"
variant="outline"
>
<Handshake className="h-3 w-3" />
Partner
</Badge>
)}
</div>
)}
</div>
Expand Down
20 changes: 12 additions & 8 deletions app/en/resources/integrations/components/toolkits-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getToolkitIcon,
Separator,
} from "@arcadeai/design-system";
import { Generic as GenericIcon } from "@arcadeai/design-system/components/ui/atoms/icons";
import { cn } from "@arcadeai/design-system/lib/utils";
import { Plus, Search } from "lucide-react";
import Link from "next/link";
Expand Down Expand Up @@ -36,25 +37,27 @@ function mapToToolkitPage(

/**
* Get toolkit icon with fallback for API toolkits.
* If "GithubApi" has no icon, falls back to "Github" icon.
*
* `getToolkitIcon` from @arcadeai/design-system returns the Generic placeholder
* (not null) when a toolkit id isn't in its icon map. For toolkits that ship
* their own `publicIconUrl` (e.g. partner toolkits not yet in the DS),
* we treat Generic as "no match" so the caller can fall through to `iconUrl`.
*/
function getToolkitIconWithFallback(
toolkitId: string
): React.ComponentType<React.SVGProps<SVGSVGElement>> | null {
const apiSuffix = "api";

// Try direct match first
const directIcon = getToolkitIcon(toolkitId);
if (directIcon) {
return directIcon;
const resolved = getToolkitIcon(toolkitId);
if (resolved && resolved !== GenericIcon) {
return resolved;
}

// For API toolkits, try the base provider ID
const normalizedId = toolkitId.toLowerCase();
if (normalizedId.endsWith(apiSuffix)) {
const baseProviderId = toolkitId.slice(0, -apiSuffix.length); // Remove "Api" suffix
const baseProviderId = toolkitId.slice(0, -apiSuffix.length);
const baseIcon = getToolkitIcon(baseProviderId);
if (baseIcon) {
if (baseIcon && baseIcon !== GenericIcon) {
return baseIcon;
}
}
Expand Down Expand Up @@ -175,6 +178,7 @@ export default function ToolkitsClient({ toolkits }: ToolkitsClientProps) {
iconUrl={iconUrl}
isByoc={toolkit.isBYOC}
isComingSoon={toolkit.isComingSoon}
isPartner={toolkit.isPartner}
isPro={toolkit.isPro}
key={toolkit.id}
link={mapToToolkitPage(
Expand Down
12 changes: 8 additions & 4 deletions app/en/resources/integrations/components/toolkits.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { TOOLKITS, type Toolkit } from "@arcadeai/design-system";
import { PARTNER_TOOLKITS } from "@/app/_data/partner-toolkits";
import { readToolkitData } from "@/app/_lib/toolkit-data";
import { normalizeToolkitId } from "@/app/_lib/toolkit-slug";
import {
normalizeToolkitId,
type ToolkitWithDocsLink,
} from "@/app/_lib/toolkit-slug";
import ToolkitsClient from "./toolkits-client";

type ToolkitWithDocsLink = Toolkit & { docsLink?: string | null };

const getToolkitDocsLink = (toolkit: Toolkit): string | undefined => {
if ("docsLink" in toolkit) {
const value = (toolkit as ToolkitWithDocsLink).docsLink;
Expand Down Expand Up @@ -33,13 +35,15 @@ const getToolkitsWithDocsLinks = async (): Promise<ToolkitWithDocsLink[]> => {
})
);

return TOOLKITS.map((toolkit) => {
const dsToolkits: ToolkitWithDocsLink[] = TOOLKITS.map((toolkit) => {
const existing = getToolkitDocsLink(toolkit);
const docsLink =
existing ?? docsLinkById.get(normalizeToolkitId(toolkit.id));

return docsLink ? { ...toolkit, docsLink } : toolkit;
});

return [...dsToolkits, ...PARTNER_TOOLKITS];
};

export default async function Toolkits() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const TYPE_LABELS: Record<ToolkitType, string> = {
const getTypePriority = (type: string): number =>
TYPE_PRIORITY[type as ToolkitType] ?? DEFAULT_PRIORITY;

const compareToolkits = (a: Toolkit, b: Toolkit): number => {
const compareToolkits = <T extends Toolkit>(a: T, b: T): number => {
// First prioritize available toolkits over coming soon toolkits
if (a.isComingSoon !== b.isComingSoon) {
return a.isComingSoon ? 1 : -1;
Expand Down Expand Up @@ -80,7 +80,7 @@ export const useFilterStore = create<FilterState>((set) => ({
}),
}));

export function useToolkitFilters(toolkits: Toolkit[]) {
export function useToolkitFilters<T extends Toolkit>(toolkits: T[]) {
const {
selectedCategory,
selectedType,
Expand All @@ -91,7 +91,7 @@ export function useToolkitFilters(toolkits: Toolkit[]) {

const debouncedSearchQuery = useDebounce(searchQuery, DEBOUNCE_TIME);

const filteredToolkits = useMemo(() => {
const filteredToolkits = useMemo<T[]>(() => {
const searchLower = debouncedSearchQuery.toLowerCase();

return toolkits
Expand Down
8 changes: 8 additions & 0 deletions app/en/resources/integrations/search/_meta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ const meta: MetaRecord = {
title: "Exa API",
href: "/en/resources/integrations/search/exa-api",
},
"-- Partner": {
type: "separator",
title: "Partner",
},
tavily: {
title: "Tavily",
href: "/en/resources/integrations/search/tavily",
},
};

export default meta;
80 changes: 80 additions & 0 deletions app/en/resources/integrations/search/tavily/page.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
---
title: "Tavily"
description: "Enable agents to search the web in real time and extract structured content. Available directly in Arcade as a Partner MCP Server."
---

import { Callout, Steps } from "nextra/components";

# Tavily

This integration is a remote MCP Server offered by [Tavily](https://tavily.com), an Arcade Partner. Add it to an [MCP Gateway](/guides/mcp-gateways/add-remote-servers) for central governance, authorization, and access control alongside Arcade's native servers.

## MCP Server URL

Paste the following URL into Arcade: **Servers → Add Server → Remote MCP**. Replace `YOUR_API_KEY` with the key you generate at [tavily.com](https://tavily.com).

```text
https://mcp.tavily.com/mcp/?tavilyApiKey=YOUR_API_KEY
```

## What you can do

Once you register Tavily in your Arcade project, Arcade discovers these tools automatically:

| Tool | What it does |
| --- | --- |
| `Tavily.Search` | Real-time web search with agent-optimized ranking. |
| `Tavily.Extract` | Extract structured content from specific URLs. |
| `Tavily.Crawl` | Crawl a site and return content across pages. |
| `Tavily.Map` | Map the structure of a site or domain. |
| `Tavily.Research` | Multi-source deep research across the web. |
| `Tavily.Skill` | High-level research skills built on the tools above. |

Compose these tools with Google Docs, Slack, Salesforce, GitHub, or any of Arcade's native servers in a single MCP Gateway, so an agent can research, draft, and act in one flow, with full authorization and audit from Arcade's runtime.

## Add Tavily to your Arcade project

<Steps>

### Get your Tavily API key

Go to [tavily.com](https://tavily.com) → **Overview** → **Generate MCP Link**. Copy the generated URL (it contains your API key).

### Add Tavily as a Remote MCP Server in Arcade

Open the [Arcade Dashboard](https://api.arcade.dev/dashboard) → **Servers** → **Add Server** → **Remote MCP**. Paste the URL from the [MCP Server URL](#mcp-server-url) section above (with your API key in place of `YOUR_API_KEY`) and save.

<Callout type="info">
See the full walkthrough in <a href="/guides/mcp-gateways/add-remote-servers">Add remote MCP servers</a> for advanced settings like connection retries, OAuth, and custom headers.
</Callout>

### Verify Tavily tools

Arcade discovers six Tavily tools: `Tavily.Search`, `Tavily.Extract`, `Tavily.Crawl`, `Tavily.Map`, `Tavily.Research`, and `Tavily.Skill`. They appear in the Playground for this project and in the MCP Gateway tool picker.

### Create an MCP Gateway

Go to **MCP Gateways** → **Create Gateway**. Select Tavily plus any other MCP Servers you want to compose with (for example, Google Docs and Slack). Set the auth mode to **Arcade Auth** so users authenticate with their Arcade account. Copy the gateway URL. This is what your agent connects to.

</Steps>

## Call Tavily tools from your agent

Once you create your gateway, any MCP client that supports Streamable HTTP can use it: Cursor, Claude Desktop, VS Code, or a custom application built with the Vercel AI SDK, LangChain, or OpenAI Agents.

```text
https://api.arcade.dev/mcp/<YOUR-GATEWAY-SLUG>
```

Arcade handles authorization, credential handling, and audit logging at runtime.

## Example

See the open-source [Financial Intelligence Agent](https://github.com/arcadeai-labs/financial-intelligence), a web-based research assistant that composes Tavily, Google Docs, and Slack through a single MCP Gateway in under 200 lines of code.

## Resources

- [Tavily documentation](https://docs.tavily.com)
- [Add remote MCP servers to Arcade](/guides/mcp-gateways/add-remote-servers)
- [Create an MCP Gateway](/guides/mcp-gateways/create-via-dashboard)
- [Connect to MCP clients](/get-started/mcp-clients)
1 change: 1 addition & 0 deletions public/images/partners/tavily.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions public/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ Arcade delivers three core capabilities: Deploy agents even your security team w
- [Setup Arcade with LangChain](https://docs.arcade.dev/en/get-started/agent-frameworks/langchain/use-arcade-with-langchain-ts): This documentation page provides a comprehensive guide on integrating Arcade tools within LangChain agents, enabling users to build and manage AI agents effectively. It covers essential concepts, prerequisites, and step-by-step instructions for transforming Arcade tools into LangChain-compatible tools, configuring agents
- [Setup Arcade with OpenAI Agents (TypeScript)](https://docs.arcade.dev/en/get-started/agent-frameworks/openai-agents/setup-typescript): This documentation page provides a comprehensive guide for setting up and building AI agents using the OpenAI Agents SDK with Arcade tools in TypeScript. It covers the integration process, including converting Arcade tools to the required format, handling authorization, and executing agent functions.
- [Setup Arcade with OpenAI Agents SDK](https://docs.arcade.dev/en/get-started/agent-frameworks/openai-agents/setup-python): This documentation page guides users on how to set up and integrate Arcade tools within OpenAI Agents applications using the OpenAI Agents SDK. It covers the necessary prerequisites, provides step-by-step instructions for creating a CLI agent, and explains how to implement tool authorization
- [Tavily](https://docs.arcade.dev/en/resources/integrations/search/tavily): Documentation page
- [The Arcade Registry](https://docs.arcade.dev/en/resources/registry-early-access): The Arcade Registry documentation provides an overview of a platform where developers can share and monetize their tools for agentic applications, similar to HuggingFace or Pypi. It explains how the registry integrates runtime metrics and user feedback to enhance tool development and usage
- [Tool error handling](https://docs.arcade.dev/en/guides/tool-calling/error-handling): This documentation page provides guidance on effectively handling errors when using tools with Arcade's Tool Development Kit (TDK). It explains the error handling philosophy, outlines best practices, and offers code examples for managing output errors in various programming languages. Users will learn how
- [Tool feedback](https://docs.arcade.dev/en/resources/integrations/tool-feedback): Documentation page
Expand Down
Loading