MCP quickstart
The fastest path from zero to a cited answer. This guide assumes you already have an MCP-aware host (Claude Desktop, Claude Code, Cursor, LangChain, Eliza or a custom runtime) wired up; if not, start at Quickstart → Claude Desktop or jump to the Claude Code and Cursor sections below.
1. Zero-config demo (no key needed)
The package ships with a public demo key baked in. Run it as-is and 10 marquee tools start working immediately:
npx -y @blockchainacademics/mcp
# or: uvx bca-mcpYou get get_price, get_market_overview, get_trending, search_news, get_recent_stories, get_topic, get_entity, get_explainer, get_sentiment, get_fear_greed. Shared bucket: 100 calls a day globally, 20 a day per IP. See Demo tier for the full breakdown.
2. Upgrade to your own key (all 105 tools)
Get a free key at blockchainacademics.com/crypto-api/signup: the free tier is 2,000 calls a month with all 105 tools unlocked. Drop it in your env:
export BCA_API_KEY=bca_live_xxxxxxxxxxxxxxxx3. Call a tool
Every Crypto Brain tool returns the canonical envelope:
{
"data": { "...": "tool-specific payload" },
"attribution": {
"citations": [
{
"cite_url": "https://blockchainacademics.com/article/bitcoin-price-history?src=claude",
"as_of": "2026-04-22T10:12:00Z",
"source_hash": "sha256:abc123..."
}
]
},
"meta": {
"status": "complete",
"request_id": "req_01HABCDEF0123456789ABCDEFG",
"pageInfo": { "hasNextPage": false, "hasPreviousPage": false, "startCursor": null, "endCursor": null }
}
}Three top-level keys. Three, every time, every surface. Full field semantics: Response envelope.
4. Parse the envelope
The snippet that covers most callers: call search_news, pull the primary citation out of attribution.citations[0].
One detail that is specific to MCP: the server returns the envelope as the first text content block, and inside it data is a string wrapped in an <untrusted_content> fence, so the host model treats upstream text as data rather than instructions. attribution and meta stay structured. Strip the fence and parse data a second time:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const client = new Client({ name: "myagent", version: "0.1.0" }, { capabilities: {} });
await client.connect(new StdioClientTransport({
command: "npx",
args: ["-y", "@blockchainacademics/mcp"],
env: { BCA_API_KEY: process.env.BCA_API_KEY! },
}));
const result = await client.callTool({
name: "search_news",
arguments: { query: "circle mica", limit: 5 },
});
// The envelope is the first text content block; `data` arrives fenced.
const envelope = JSON.parse(result.content[0].text);
const unfence = (s: string) =>
s.replace(/^<untrusted_content[^>]*>[\s\S]*?\n\n/, "").replace(/\n<\/untrusted_content>\s*$/, "");
const data = JSON.parse(unfence(envelope.data));
// Happy-path guard
if (envelope.meta.status !== "complete") {
console.warn(
`[${envelope.meta.request_id}] status=${envelope.meta.status}`,
envelope.meta.diagnostic,
);
}
// Pull the primary citation: always citations[0]
const primary = envelope.attribution.citations[0];
console.log("Source:", primary.cite_url);
console.log("As of:", primary.as_of);
// Use the data
for (const item of data.items) {
console.log(`- ${item.title} (${item.slug})`);
}import json
import os
import re
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
server_params = StdioServerParameters(
command="uvx",
args=["bca-mcp"],
env={"BCA_API_KEY": os.environ["BCA_API_KEY"]},
)
def unfence(s: str) -> str:
s = re.sub(r"^<untrusted_content[^>]*>.*?\n\n", "", s, count=1, flags=re.S)
return re.sub(r"\n</untrusted_content>\s*$", "", s)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool(
"search_news",
arguments={"query": "circle mica", "limit": 5},
)
# The envelope is the first text content block; `data` arrives fenced.
envelope = json.loads(result.content[0].text)
data = json.loads(unfence(envelope["data"]))
# Happy-path guard
if envelope["meta"]["status"] != "complete":
print(
f"[{envelope['meta']['request_id']}] "
f"status={envelope['meta']['status']}",
envelope["meta"].get("diagnostic"),
)
# Pull the primary citation: always citations[0]
primary = envelope["attribution"]["citations"][0]
print("Source:", primary["cite_url"])
print("As of:", primary["as_of"])
# Use the data
for item in data["items"]:
print(f"- {item['title']} ({item['slug']})")Output (elided):
Source: https://blockchainacademics.com/article/circle-mica-license?src=custom&utm_medium=mcp&utm_campaign=search_news
As of: 2026-04-21T09:31:22Z
- Circle secures full MiCA license (circle-mica-license)Over plain HTTP there is no fence: data is the JSON object itself. See the REST API reference.
5. Surface the citation
Paste the cite_url into your agent's response: inline link, footnote or citation block, whichever your UI uses. On Free and Starter tiers this is required, not optional; see Attribution & licensing.
Circle received its MiCA license on 2026-04-18. [source]
6. Handle non-complete statuses
meta.status is one of four values. Map each to an honest user-facing response:
| Status | Agent should say (example) |
|---|---|
complete | The answer, with citations[0].cite_url surfaced. |
unseeded | "That data source is not live yet; ETA diagnostic.eta." |
partial | The answer, plus "Data is partial right now (diagnostic.vendor is degraded)." |
stale | The answer, plus "As of citations[0].as_of (cached; upstream unreachable)." |
Full decision tree with handling code: Envelope → Status decision tree.
7. Log the request_id
Every envelope includes meta.request_id (ULID, prefixed req_) and every HTTP response mirrors it as the X-Request-ID header. Persist it with your agent's turn log. Support tickets and audit traces start here.
CLI
The PyPI package also installs bca, a terminal CLI over the same REST API and the same key: handy for debugging, quick lookups and shell pipelines.
pipx install bca-mcp
bca login # stores the key in ~/.bca/config.toml (chmod 600)
bca news search "bitcoin etf" -n 5 # recent articles, as a table with cite_url
bca entity ethereum # dossier panel
bca price BTC,ETH,SOL # spot price and 24h change
bca market overview -n 10 # top N by market cap
bca indicator coverage-index bitcoin -w 30d
bca explainer what-is-a-blockchain # rendered markdown
bca agent summarize-whitepaper --url https://ethereum.org/whitepaper
bca version # CLI and live API versionEvery command accepts --json for the raw envelope, ready for jq. BCA_API_KEY and BCA_API_BASE in the environment take precedence over the config file; bca config show prints where the key came from, with the key masked.
Claude Code
Claude Code registers MCP servers from the terminal. One command per machine (user scope), or per repository (project scope, written to .mcp.json so teammates get it too):
claude mcp add blockchainacademics -e BCA_API_KEY=bca_live_xxxxxxxxxxxxxxxx -- npx -y @blockchainacademics/mcp
# shared with the repository instead:
claude mcp add --scope project blockchainacademics -- npx -y @blockchainacademics/mcpFor project scope, keep the key out of the committed file: .mcp.json expands ${BCA_API_KEY} from each developer's environment.
{
"mcpServers": {
"blockchainacademics": {
"command": "npx",
"args": ["-y", "@blockchainacademics/mcp"],
"env": { "BCA_API_KEY": "${BCA_API_KEY}" }
}
}
}Then ask: "Run due diligence on Hyperliquid: tokenomics, team, audits, risk." or "Which AI agent tokens have rising narrative strength this week?" Claude Code picks the tools itself. claude mcp list shows the server, and /mcp inside a session lists its tools.
Cursor
Cursor reads MCP servers from .cursor/mcp.json in the project, or ~/.cursor/mcp.json for every project. Create the file, then open Settings → MCP and confirm the server shows green:
{
"mcpServers": {
"blockchainacademics": {
"command": "npx",
"args": ["-y", "@blockchainacademics/mcp"],
"env": { "BCA_API_KEY": "bca_live_xxxxxxxxxxxxxxxx" }
}
}
}Windsurf uses the same shape in ~/.codeium/windsurf/mcp_config.json.
Environment variables
Both MCP servers and the CLI read the same variables. In the CLI they take precedence over ~/.bca/config.toml.
| Variable | Default | What it does |
|---|---|---|
BCA_API_KEY | the demo key | Your key. Unset, the server runs on the shared demo tier. |
BCA_API_BASE | https://api.blockchainacademics.com | The API host. Allowlisted: production, https://staging-api.blockchainacademics.com, and http://localhost or http://127.0.0.1 with any port. BCA_API_BASE_URL is accepted as a legacy alias. |
BCA_ALLOW_INSECURE_BASE | unset | 1 lets bca login --api-base store a non-HTTPS base for local development. |
The servers never follow redirects from the API host and never send the key anywhere but the configured base.
What's next
- Response envelope: all four status values, the full field breakdown, the 0.2 to 0.3 migration.
- Citations: why
citations[]is an array and what multi-source unlocks. - Attribution & licensing: tier rules for surfacing
cite_url. - First agent walkthrough: end to end, retrieval plus reasoning plus a cited answer.
- REST API reference: the same envelope over plain HTTP.