AI fund assistant

Build an AI fund assistant with tool calling over structured data

A language model can explain a fund, compare two funds or summarise a portfolio, but only if the facts it works from are current and structured. This page shows how to expose FundFacts API as a tool the model calls, how to keep it inside guardrails, and what the AI companion kit adds when the assistant is built with Cursor, Claude Code or Codex.

The problem

Ask a general-purpose model about a fund by ISIN and you get one of two things: a refusal, or a confident paragraph assembled from training data that may be years old and may describe a different share class. Neither is acceptable in a product that clients read.

The fix is well understood: give the model a tool that returns the current facts, and instruct it to answer only from tool output. The remaining questions are practical. What does the tool return, how large is the payload, and how do you stop the model from drifting into advice?

What you need from the data

A fund-assistant tool should return a compact, structured document the model can quote from:

  • Name, ISIN, share class, currency, distribution policy
  • Investment objective in the fund house's words
  • Risk rating 1–7 and profile labels (kind, category, region focus, sector tilt)
  • TER, AUM, volatility and other headline metrics
  • Top holdings and sector or country exposure
  • Calendar and cumulative returns
  • dataAsOf, so the model can state how current the facts are

Structured is the point. A model asked to compare fees does better with headlineMetrics.ter as a number than with a factsheet PDF.

How it works with FundFacts API

Define one tool, get_fund, whose implementation calls the API and returns a trimmed data object. The example uses a generic tool-calling shape that maps onto any major LLM SDK.

typescript
export const getFundTool = {
name: "get_fund",
description:
"Return current structured facts for a fund or ETF share class by ISIN. " +
"Use it before answering any question about a specific fund.",
parameters: {
type: "object",
properties: { isin: { type: "string", pattern: "^[A-Z]{2}[A-Z0-9]{9}[0-9]$" } },
required: ["isin"],
},
async execute({ isin }: { isin: string }) {
const res = await fetch(`https://fundfactsapi.com/api/v1/funds/${isin}`, {
headers: { Authorization: `Bearer ${process.env.FUNDFACTS_API_KEY}` },
});
if (res.status === 404) return { error: "fund_not_found", isin };
if (res.status === 429) return { error: "rate_limited", resetAt: res.headers.get("X-RateLimit-Reset") };
if (!res.ok) return { error: `http_${res.status}` };
const { name, data, generatedAt } = await res.json();
return {
isin,
name,
dataAsOf: data.dataAsOf,
generatedAt,
objective: data.investmentObjective,
riskRating: data.riskRating,
profile: data.profile,
keyFacts: data.keyFacts,
headlineMetrics: data.headlineMetrics,
topHoldings: (data.topHoldings ?? []).slice(0, 10),
sector: data.sector,
geography: data.geography,
cumulativePerformance: data.cumulativePerformance,
};
},
};

Pair the tool with a system prompt along these lines: answer only from get_fund output; always state dataAsOf; never recommend buying, selling or holding; if the tool returns an error, say so rather than guessing. Returning errors as data instead of throwing lets the model explain the situation to the user.

Design notes

Guardrails. Three layers work well. First, the system prompt rules above. Second, an output check that blocks phrases such as "you should buy" or "this fund is better". Third, a footer appended by your code rather than the model: "Facts read from documents published by the fund house, as of {dataAsOf}. Not investment advice."

Payload size. The full data object can be several kilobytes. Trim what the model sees, ten holdings rather than fifty and no indexed NAV series unless the user asks for a chart, to keep context small and answers focused.

Caching. The API refreshes every 24 hours; cache tool results by ISIN until expiresAt so a conversation that mentions the same fund five times costs one request. A cold ISIN can take one to three minutes on first load, so tell the user the fund is being loaded rather than timing out silently.

Quota. Tool calls are user-driven and bursty. Read X-RateLimit-Remaining and, when low, have the tool return rate_limited so the model can say the service is busy. Do not let a chat loop retry on its own; a 502 upstream_error should likewise be reported once, not retried in a loop.

The AI companion kit. Pro and Scale plans include an AGENTS.md, a Cursor rule, a Claude Code skill, the OpenAPI spec and TypeScript types. Drop them into a repository and a coding agent can scaffold this assistant, from tool definition and types to caching and the disclaimer footer, from a single prompt. The walkthrough is in build a fund app with an AI coding agent.

Disclaimers. The assistant explains facts; it does not advise. Keep "not investment advice" in the UI, not only in the model's output, and show dataAsOf for every fund mentioned. For the identifier the tool expects, see ISIN; for the data fields, fund data from an ISIN.

Frequently asked questions

Why not let the model read the factsheet PDF directly?

PDF layouts vary by fund house, tables lose structure when converted to text, and the model has to guess which of several similar numbers is the TER. Structured JSON removes that ambiguity: headlineMetrics.ter is one number with one meaning. It is also far smaller than a document, which keeps the context window free for the conversation itself.

How do I stop the assistant from giving investment advice?

Use several layers. Instruct the model in the system prompt to describe facts and decline recommendations. Check its output for advice phrasing before displaying it. Append a fixed disclaimer with dataAsOf from your own code so it cannot be omitted. Finally, design the interface around explanation and comparison rather than around questions like which fund should I buy.

What is in the AI companion kit?

Pro and Scale plans include an AGENTS.md file describing the API for coding agents, a Cursor rule, a Claude Code skill, the OpenAPI specification and generated TypeScript types for the response. Together they let Cursor, Claude Code or Codex scaffold a working fund tool, including this tool-calling pattern, from a single prompt, without the agent guessing at field names.

Should the tool return the whole response or a subset?

A subset. The full payload includes long holdings lists and an indexed NAV series that add tokens without helping most questions. Return the objective, risk rating, key facts, headline metrics, top ten holdings, exposure arrays and dataAsOf by default, and offer a second tool or a parameter for the performance series when a user asks about returns or wants a chart.

Build your ai fund assistant on FundFacts API

Free plan with 10 requests a day to prototype; paid plans add the AI companion kit that scaffolds the integration for you.