Docs navigation

LangChain (Python)

Call the Crypto Brain from a LangChain (Python) agent. Two paths:

  1. Native Python MCP (bca-mcp on PyPI): 8 read-only tools, same REST API, same envelope. Fastest path for pure-Python shops.
  2. TypeScript server via stdio: spawns @blockchainacademics/mcp as a subprocess through langchain-mcp-adapters. Gets you all 105 tools.

Both feed the same api.blockchainacademics.com backend. Pick whichever matches your deployment story.

Path A: native bca-mcp (8 tools, pure Python)

bash
pip install bca-mcp langchain langchain-openai langchain-mcp-adapters
bca_native_agent.py
python
import asyncio
import os
import sys
 
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
 
async def main() -> None:
    params = StdioServerParameters(
        command=sys.executable,
        args=["-m", "bca_mcp"],
        env={**os.environ, "BCA_API_KEY": os.environ["BCA_API_KEY"]},
    )
    async with stdio_client(params) as (r, w), ClientSession(r, w) as s:
        await s.initialize()
        tools = await load_mcp_tools(s)
        print(f"Loaded {len(tools)} BCA tools")
 
        agent = create_react_agent(ChatOpenAI(model="gpt-4o-mini"), tools)
        result = await agent.ainvoke({
            "messages": [("user",
                "Using BCA tools, list the top 3 stories about stablecoin "
                "regulation this month with citations.")]
        })
        print(result["messages"][-1].content)
 
if __name__ == "__main__":
    asyncio.run(main())

Wrapping specific tools as LangChain Tools directly

python
from langchain.tools import StructuredTool
 
# After load_mcp_tools(session) you get a list of StructuredTool objects;
# pick the ones you want rather than passing all 8 to the agent:
search_news = next(t for t in tools if t.name == "search_news")
get_entity  = next(t for t in tools if t.name == "get_entity")
get_price   = next(t for t in tools if t.name == "get_price")
 
agent = create_react_agent(ChatOpenAI(model="gpt-4o-mini"),
                           [search_news, get_entity, get_price])

Narrow tool surfaces keep reasoning loops tight and predictable: recommended for production agents.


Path B: TypeScript server via stdio (every tool)

1. Install

bash
pip install langchain langchain-openai langchain-mcp-adapters
# Node 18+ required for the stdio subprocess
node --version

2. Set your API key

bash
export BCA_API_KEY="bca_live_xxxxxxxxxxxxxxxx"

3. Spin up the agent

bca_agent.py
python
import asyncio
import os
 
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
 
async def main() -> None:
    async with MultiServerMCPClient(
        {
            "blockchainacademics": {
                "command": "npx",
                "args": ["-y", "@blockchainacademics/mcp"],
                "transport": "stdio",
                "env": {"BCA_API_KEY": os.environ["BCA_API_KEY"]},
            }
        }
    ) as client:
        tools = await client.get_tools()
        print(f"Loaded {len(tools)} BCA tools")
 
        agent = create_react_agent(
            ChatOpenAI(model="gpt-4o-mini"),
            tools,
        )
 
        result = await agent.ainvoke(
            {
                "messages": [
                    (
                        "user",
                        "Using BCA tools, list the top 3 news stories about "
                        "stablecoin regulation this month with citations.",
                    )
                ]
            }
        )
        print(result["messages"][-1].content)
 
if __name__ == "__main__":
    asyncio.run(main())

Run it:

bash
python bca_agent.py

What you get back

Every tool response is wrapped in the canonical envelope (over MCP, data arrives as a fenced string; see Parse the envelope):

python
{
    "data": [ ... ],                                # tool payload
    "attribution": {
        "citations": [
            {
                "cite_url": "https://blockchainacademics.com/article/...?src=langchain",
                "as_of": "2026-04-20T09:31:22Z",
                "source_hash": "sha256:a9f1...",
            }
        ]
    },
    "meta": {
        "status": "complete",
        "request_id": "req_01HABCDEF0123456789ABCDEFG",
        "pageInfo": {
            "hasNextPage": False,
            "hasPreviousPage": False,
            "startCursor": None,
            "endCursor": None,
        },
    },
}

Parse attribution.citations[0].cite_url into your agent's final answer so downstream users can verify the claim.

Tips

  • Rate limits: every plan has a per-second rate limit and a monthly quota (see Pricing). Agent loops with tool retries burn through them quickly: cache aggressively or upgrade.
  • Long-running jobs: generate_due_diligence and generate_tokenomics_model enqueue agent jobs that return a job id; poll get_agent_job until the job is done. See the Tool reference.
  • Attribution: every cite_url carries UTM parameters that identify the integration. Nothing to add on your side; just surface the link.

Next up