Guide · CrewAI

Use Chart Library with CrewAI

CrewAI builds crews of role-playing agents. Give one of them the Chart Library tools and it answers “is this setup bullish?” with the cohort of historical analogs and their realized outcomes — base rates, never invented technical analysis.

Two ways in, both keyless for the core loop: the MCP adapter (full canonical toolset) or a native @tool (one tight endpoint). Add an API key only past 1,000 calls/day.

Step 1: Install

pip install crewai 'crewai-tools[mcp]'

crewai is the framework; the [mcp] extra on crewai-tools adds MCPServerAdapter, which turns any MCP server into CrewAI tools. (For the native @tool path you only need crewai and requests.)

Option A — connect the keyless MCP endpoint (full toolset)

Point MCPServerAdapter at the hosted Streamable-HTTP endpoint. No install of our server, no API key — the whole canonical surface (search, pull_comps, cohort_introspect, …) becomes CrewAI tools:

from crewai import Agent, Task, Crew
from crewai_tools import MCPServerAdapter

# Hosted, keyless. (For higher volume, add an Authorization header in the dict.)
server_params = {
    "url": "https://chartlibrary.io/mcp",
    "transport": "streamable-http",
}

with MCPServerAdapter(server_params) as tools:
    analyst = Agent(
        role="Chart Research Analyst",
        goal=(
            "Answer stock-setup questions with calibrated historical base "
            "rates from Chart Library — never a single price target."
        ),
        backstory=(
            "You ground every read in the cohort of historical analogs: the "
            "forward-return distribution, the calibrated 80% band, and the "
            "drivers. If a cohort is thin (n < 30), you say so."
        ),
        tools=tools,
        verbose=True,
    )

    task = Task(
        description=(
            "What did setups like NVDA on 2025-03-03 (1d) do next? Anchor the "
            "cohort with search, then pull_comps, and report the 5-day "
            "distribution (median, up-rate, calibrated 80% band) as fact."
        ),
        expected_output=(
            "A short report citing the cohort distribution and top drivers. "
            "No single price target."
        ),
        agent=analyst,
    )

    crew = Crew(agents=[analyst], tasks=[task], verbose=True)
    print(crew.kickoff())

MCPServerAdapter as a context manager opens the connection, exposes the tools for the crew, and cleans up on exit. Prefer a local subprocess? Swap the dict for StdioServerParameters(command="chartlibrary-mcp") after pip install chartlibrary-mcp.

Option B — a native keyless @tool (tight surface)

Want just the comp set, with a result shape you control? A CrewAI @tool wrapping one fetch is all it takes — no MCP, no extra dependency beyond requests:

import requests
from crewai.tools import tool


@tool("Pull historical comp set")
def pull_comps(symbol: str, date: str, timeframe: str = "1d") -> dict:
    """Pull the cohort of historical analogs for a setup (symbol, date,
    timeframe): forward-return distribution, calibrated 80% band, and the
    drivers that separated winners from losers. Historical base rates, never
    a price forecast. timeframe is one of 5m/15m/30m/1h/1d."""
    r = requests.post(
        "https://chartlibrary.io/api/v1/cohort_analyze",
        json={
            "anchor": {"symbol": symbol, "date": date, "timeframe": timeframe},
            "horizons": [1, 5, 10],
        },
        timeout=30,
    )
    r.raise_for_status()
    d = r.json()
    out = {"cohort_size": d.get("cohort_size_actual")}
    for h in (1, 5, 10):
        o = (d.get("outcome_distribution") or {}).get(h, {})
        c = (d.get("calibration_by_horizon") or {}).get(f"{h}d", {})
        out[f"{h}d"] = {
            "median_pct": o.get("median"),
            "up_rate": o.get("win_rate"),
            "cohort_p10_p90": [o.get("p10"), o.get("p90")],
            "calibrated_80_band": [c.get("calibrated_p10"), c.get("calibrated_p90")],
        }
    out["drivers"] = [
        {"feature": f["feature"], "direction": f["direction"]}
        for f in (d.get("feature_importance") or {}).get("5", [])[:3]
    ]
    out["note"] = "Historical distribution of analogs, not a forecast."
    return out


# ...then pass tools=[pull_comps] to your Agent(...) as in Option A.

Why ground the agent this way

A model asked “what happens after a setup like this?” produces a confident, plausible, unverifiable answer. We measured it: even a frontier model only stays calibrated by hedging to a ~2×-wider interval. Chart Library returns the actual cohort and its realized outcomes with a calibrated band whose coverage is audited — so your crew cites history instead of inventing it.

Frequently asked questions

Do I need an API key?
No. The core loop — search, cohort_analyze / pull_comps, cohort_introspect — is free and keyless up to 1,000 calls/day, on both the MCP endpoint and the REST endpoint. Add a key only for higher volume or paid surfaces.
MCP adapter vs native @tool — which?
Use MCPServerAdapter when you want the full canonical toolset and the agent to range across it. Use a native @tool when you want one tight endpoint and full control over the result shape you hand the model. Both are keyless.
Can multiple agents in the crew share the tools?
Yes — pass the same tools list (or the MCPServerAdapter context) to each Agent that needs cohort intelligence. Keep the adapter context open for the crew's run.
Does it forecast prices?
Never. It returns the historical distribution of what analogous setups did next — median, up-rate, calibrated p10/p90 — as a base rate with a coverage receipt. It does not predict a single outcome.
Try it

Give your crew calibrated history.

The core loop is free and keyless — connect it, run a task, no signup. Add a key only when you scale.

Related