AI agents & LLMs

How do I add a fund lookup tool to a Vercel AI SDK chatbot?

Define a tool with zod that calls the fund API by ISIN inside a Next.js route using the Vercel AI SDK, so your chatbot answers with current TER, holdings, exposures and returns.Updated 12 September 2026 · by FundFacts API

Short answer

In your route handler, pass a tool defined with zod ({ isin: z.string().length(12) }) whose execute function calls GET https://fundfactsapi.com/api/v1/funds/{isin} with the server-side key (or the @fundfactsapi/sdk client) and returns the fields the model should see. streamText or generateText then lets the model call it for any ISIN a user mentions, and you render the result in the chat. The key stays on the server.

Route handler

typescript
// app/api/chat/route.ts — npm install ai @ai-sdk/openai @fundfactsapi/sdk zod
import { openai } from "@ai-sdk/openai";
import { streamText, tool } from "ai";
import { FundFacts } from "@fundfactsapi/sdk";
import { z } from "zod";
const ff = new FundFacts({ apiKey: process.env.FUNDFACTS_API_KEY! }); // server only
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4.1"),
system: "You answer questions about investment funds using the tools. Always mention asOf with figures; say 'not disclosed' for empty fields.",
messages,
tools: {
fundFacts: tool({
description: "Current factsheet of a fund or ETF by 12-character ISIN.",
inputSchema: z.object({ isin: z.string().length(12) }),
execute: async ({ isin }) => {
const f = await ff.getFund(isin); // cached in memory until expiresAt
const d = f.data;
return {
name: f.name, category: d.profile.category, risk: d.riskRating, ter: d.headlineMetrics.ter, aum: d.keyFacts.aum,
topHoldings: d.topHoldings.slice(0, 10), sectors: d.sector.slice(0, 8), countries: d.geography.slice(0, 8),
calendarReturns: d.calendarReturns, asOf: d.dataAsOf,
};
},
}),
searchFunds: tool({
description: "Find ISINs by fund name or issuer. Free.",
inputSchema: z.object({ query: z.string().min(2) }),
execute: async ({ query }) => (await ff.search(query)).results.slice(0, 10).map((r) => ({ isin: r.isin, name: r.name, issuer: r.issuer })),
}),
},
});
return result.toUIMessageStreamResponse();
}

The tool result can be rendered as a card on the client (holdings table, sector donut) with @fundfactsapi/widgets, the same React components that render this site's factsheets.

Or connect the MCP server

The AI SDK can consume MCP tools directly:

typescript
import { experimental_createMCPClient as createMCPClient } from "ai";
const mcp = await createMCPClient({
transport: { type: "http", url: "https://fundfactsapi.com/api/mcp", headers: { Authorization: `Bearer ${process.env.FUNDFACTS_API_KEY}` } },
});
const tools = await mcp.tools(); // get_fund, search_funds, compare_funds, analyze_portfolio, fund_overlap, get_scpi

The MCP server exposes six tools, each counted like the REST endpoint it wraps: get_fund (ISIN → the full factsheet payload), search_funds (name → ISINs, free), compare_funds (side-by-side table for several ISINs), analyze_portfolio (weighted look-through of a list of positions, Pro and above), fund_overlap (shared holdings between funds, Pro and above) and get_scpi (French SCPI data by name or ISIN). Authentication is the same bearer API key as the REST API, sent as an Authorization header.

Notes

  • A cold ISIN can take minutes on first load; set maxDuration on the route accordingly or pre-warm the funds your users ask about.
  • One request per ISIN answered; the SDK client caches until expiresAt so repeated questions in a session cost nothing extra.
  • Never call the API from the browser; the key belongs in the route handler.

Verify it yourself

The demo endpoint returns the live payload for a fund that is already in the store, without a key. Everything on this page can be checked against it.
bash
curl -s https://fundfactsapi.com/api/v1/demo/funds/IE00B4L5Y983 | jq '{name, asOf: .data.dataAsOf, ter: .data.headlineMetrics.ter, risk: .data.riskRating, top: .data.topHoldings[:3]}'

Frequently asked questions

Do I need a credit card to try it?

No. The Free plan is 15 lookups per month with no card; sign up with an e-mail address or Google and the key is shown in the dashboard.

Which ISINs work?

Any share class of a UCITS fund, ETF, money-market fund or open-end fund whose management company publishes a product page, factsheet and KID. Each share class has its own ISIN and is looked up on its own. Stocks, bonds and indices are not funds and return 404 fund_not_found.

Try it on your own ISINs

One request returns key facts, holdings, risk and performance as JSON. Free plan, no card.