How do I add fund and ETF data as a tool in LangChain or LangGraph?
Wrap one API call in a LangChain @tool so an agent can answer questions about any ISIN with current TER, holdings, exposures and returns. Python code, plus the MCP adapter route.Updated 12 September 2026 · by FundFacts APIShort answer
Define a @tool that calls GET https://fundfactsapi.com/api/v1/funds/{isin} with your API key and returns a compact dict (name, TER, risk indicator, category, top holdings, sector split, returns, dataAsOf), then bind it to your model or LangGraph agent. Alternatively load the MCP server at https://fundfactsapi.com/api/mcp through langchain-mcp-adapters and get all six tools without writing wrappers. Either way the agent answers with current published figures instead of training-data memory.
A tool in twenty lines
python# pip install langchain langchain-openai fundfactsimport osfrom langchain_core.tools import toolfrom fundfacts import FundFactsff = FundFacts() # reads FUNDFACTS_API_KEY@tooldef fund_facts(isin: str) -> dict:"""Current factsheet of a fund or ETF by 12-character ISIN: name, TER, 1-7 riskindicator, category, fund size, top holdings, sector and country split,calendar and annualised returns, as-of date. Returns {"error": ...} when theISIN is not a covered fund."""try:f = ff.get_fund(isin)except Exception as e: # FundFactsError carries status and codereturn {"error": str(e)}d = f["data"]return {"name": f["name"], "category": d["profile"]["category"], "risk_1_to_7": d["riskRating"],"ter": d["headlineMetrics"]["ter"], "aum": d["keyFacts"]["aum"],"top_holdings": d["topHoldings"][:10], "sectors": d["sector"][:8], "countries": d["geography"][:8],"calendar_returns": d["calendarReturns"], "annualised_returns": d["annualisedReturns"],"as_of": d["dataAsOf"],}@tooldef search_funds(query: str) -> list[dict]:"""Find fund ISINs by name or issuer, e.g. 'msci world' or 'vanguard all-world'. Free."""return [{"isin": r["isin"], "name": r["name"], "issuer": r["issuer"]} for r in ff.search(query)["results"][:10]]
Bind the tools to a model and run an agent:
pythonfrom langchain_openai import ChatOpenAIfrom langgraph.prebuilt import create_react_agentagent = create_react_agent(ChatOpenAI(model="gpt-4.1"), [fund_facts, search_funds],prompt="You answer questions about investment funds. Always quote as_of with figures. Say 'not disclosed' for empty fields; never estimate.")result = agent.invoke({"messages": [("user", "What does IE00B4L5Y983 hold and what does it cost?")]})print(result["messages"][-1].content)
Trimming the payload in the tool (the full one is a few kilobytes with the series) keeps the context small; return the whole data object if the agent needs the monthly series for calculations.
Or use the MCP server directly
python# pip install langchain-mcp-adaptersfrom langchain_mcp_adapters.client import MultiServerMCPClientclient = MultiServerMCPClient({"fundfacts": {"transport": "streamable_http","url": "https://fundfactsapi.com/api/mcp","headers": {"Authorization": f"Bearer {os.environ['FUNDFACTS_API_KEY']}"},}})tools = await client.get_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 for production agents
- The first call for a cold ISIN can take up to a few minutes; give the tool a 300-second timeout and tell the model a first lookup can be slow.
- One request per ISIN answered. Cache tool results by ISIN until
expiresAtif users repeat questions. - The fund data for AI agents guide covers prompt design and guardrails in more depth.
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.bashcurl -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]}'