Skip to content
Scalekit Docs
Talk to an Engineer Dashboard

Connect AI agents to the Bitquery MCP server

Vendor MCP
Open markdown

The Bitquery MCP connector routes your AI agent's tool calls to Bitquery's own MCP server through Scalekit. Each user signs in to Bitquery once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 323 tools.

Tools
323
What they doRead · write · destructive
126 · 197 · 0126 read197 write0 destructive
Users sign in with
OAuth app
Scalekit's or your own

Setup

  1. Install the SDK

    Terminal window
    npm install @scalekit-sdk/node dotenv
  2. Set your credentials

    Add your Scalekit credentials to your .env file. Find values in app.scalekit.com > Developers > API Credentials.

    .env
    SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
    SCALEKIT_CLIENT_ID=<your-client-id>
    SCALEKIT_CLIENT_SECRET=<your-client-secret>
  3. Create the Bitquery MCP connection

    In AgentKit > Connections, create a Bitquery MCP connection. The name you give it is the connection_name your code passes. See Configure connections.

    Scalekit credentials are available for Bitquery MCP server, so you don't need to register an OAuth app.

  4. Authorize a user and make your first call

    quickstart.mts
    import { ScalekitClient } from '@scalekit-sdk/node'
    import 'dotenv/config'
    import { createInterface } from 'node:readline/promises'
    const scalekit = new ScalekitClient(
    process.env.SCALEKIT_ENVIRONMENT_URL,
    process.env.SCALEKIT_CLIENT_ID,
    process.env.SCALEKIT_CLIENT_SECRET,
    )
    const actions = scalekit.actions
    const connector = 'bitquerymcp'
    const identifier = 'user_123'
    // Generate an authorization link for the user
    const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
    console.log('Authorize Bitquery MCP:', link)
    const rl = createInterface({ input: process.stdin, output: process.stdout })
    await rl.question('Press Enter after authorizing...')
    rl.close()
    // Make your first call
    const result = await actions.executeTool({
    connector,
    identifier,
    toolName: 'bitquerymcp_chain_capabilities',
    toolInput: {},
    })
    console.log(result)
    Terminal window
    npx tsx quickstart.mts

    Each user signs in once. See Authorize a user for the full flow and statuses.

Tools

Pass the exact name to execute_tool
Try in PlaygroundRequest a tool
  • bitquerymcp_accumulating_traders_by_tokenFind wallets with the highest net buy volume for a token over a given time window.Read-only

    Accumulating Traders By Token

    Find wallets with the highest net buy volume for a token over a given time window.

    Inputs

    addressstringrequired
    Token contract address. Lowercase 0x-hex for EVM; base58 for Solana/Tron.
    blockchainstringrequired
    Chain the token trades on — Ethereum (eth), Arbitrum (arb, arbitrum one), Base, Matic (polygon, polygon pos), Optimism (op, op mainnet), Binance Smart Chain (bsc, bnb, bnb chain, bnb smart chain), Tron (trx), Solana (sol), Robinhood (rh) or Arc (arc network, circle arc). Case-insensitive; any other network is refused before the query runs.
    after_timestring
    Window start (from), UTC, inclusive — "2026-09-15 08:00:00", a date, or unix seconds. Overrides window_hours. Empty (default) = window_hours before the end. A value that is not a date-time is rejected. Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    before_timestring
    Window end (to), UTC, exclusive — same formats as after_time; never later than now. Empty (default) = now. Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    limitinteger
    Max traders to return.default 50
    min_net_buy_usdinteger
    Filter out traders whose net accumulation is below this USD threshold.default 0
    window_hoursinteger
    Look-back window in hours before the window end, used when after_time is empty — 1 to 744 (31 days); trade history reaches back about 30 days.default 24
  • bitquerymcp_address_labelsLook up all known LABELS for a blockchain ADDRESS — entity, category, CEX deposit/hot wallet, mixer, gambling, scam, token-clone, contract type, NFT collection, ENS, … Works for both wallets and token/contract addresses.Read-only

    Address Labels

    Look up all known LABELS for a blockchain ADDRESS — entity, category, CEX deposit/hot wallet, mixer, gambling, scam, token-clone, contract type, NFT collection, ENS, … Works for both wallets and token/contract addresses. Use for "what / who is this address", "is this token a scam or a clone", "is this wallet a CEX or mixer". To rank a token's traders by label use `labeled_traders_of_token`; to list every address carrying a label use `addresses_by_label`; to resolve a human name to a stored value use `find_label_values`. Backed by the Bitquery address-label directory (directory.labels). Coverage: token/contract labels are dense on EVM (Ethereum, BSC, Polygon); wallet (EOA) labels are densest on Tron and Bitcoin. Pass chain='' to search every chain. Tracing note: call this ONLY when the inline label already on a *_flow_edges / *_transfers row is empty, or to refine the type. An empty result while find_label_values shows rich coverage (e.g. 'binance') is a meaningful NEGATIVE signal, not missing data.

    Inputs

    addressstringrequired
    Address to look up. Lowercase 0x-hex for EVM (case is normalized); base58 as-is for Solana/Tron/Bitcoin.
    chainstring
    Restrict to one chain — network name or slug (Ethereum/ethereum, Matic/polygon, Binance Smart Chain/bsc, Tron, Solana, bitcoin, …). Empty string = all chains.default
  • bitquerymcp_addresses_by_labelList blockchain ADDRESSES that carry a specific label — e.g.Read-only

    Addresses By Label

    List blockchain ADDRESSES that carry a specific label — e.g. every `cex-deposit-address` = 'binance-deposit', every `category` = 'DEX', every `scam` / `mixer` / `sanctioned` address. Use for "give me every address tagged X" or to build an address set to cross-reference with trading via `execute_sql` (join `trading_rt.*` on the returned addresses). Discover valid label_type / label_value pairs first with `find_label_values`. Backed by directory.labels (indexed by label_type + label_value, so this is fast). label_value is matched exactly. Pass chain='' for all chains.

    Inputs

    label_typestringrequired
    Exact label key — e.g. cex-deposit-address, cex-hot-wallet, mixer, gambling, scam, token-clone, token-contract, darknet-market, category, entity. No backslashes.
    label_valuestringrequired
    Exact (case-sensitive) label value to match, e.g. "binance-deposit", "DEX". Use find_label_values to discover valid values. No backslashes.
    chainstring
    Restrict to one chain (network name or slug, e.g. ethereum, polygon/matic, bsc, tron, solana, bitcoin, ripple/xrp, cardano/ada, litecoin/ltc, dogecoin/doge, bitcoin-cash/bch, stellar/xlm). Empty string = all chains. Letters, digits, spaces, dots, underscores and hyphens only.default
    cursorstring
    Paging cursor - the Cursor value of the LAST row of the previous page, passed back unchanged. Empty (default) = first page.default
    limitinteger
    Max addresses to return. At most 10000.default 100
  • bitquerymcp_arbitrum_address_flow_summaryONE-CALL triage of an Arbitrum (arb, ARB, Arbitrum One, L2) address — profile (sent/received transfer counts, distinct receivers/senders) + TOP receivers AND TOP senders.Read-only

    Arbitrum Address Flow Summary

    ONE-CALL triage of an Arbitrum (arb, ARB, Arbitrum One, L2) address — profile (sent/received transfer counts, distinct receivers/senders) + TOP receivers AND TOP senders. Collapses address_profile + trace_next_hop(out) + an incoming convergence into a single call — call this FIRST when triaging a hop. Returns a computed Role: consolidator (senders ≫ receivers) / distributor (receivers ≫ senders) / hub (thousands of both — don't trace deeper) / relay. Profile counts are all-currency; the top arrays honor the currency filter. Pass the returned counterparties to labels_for_addresses to identify them. For raw rows use arbitrum_transfers_in/out; for one direction's full ranking use arbitrum_trace_next_hop. READING THE TOP ARRAYS: one entry per (counterparty, TOKEN) — the same address repeats once per token it moved. Symbols are not unique; identify a token by `contract`.

    Inputs

    addressstringrequired
    Wallet/contract address, 0x-hex (case-insensitive).
    after_timestring
    Only activity at/after this UTC time - bounds the profile counts AND the top arrays. Empty = whole history. Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    before_timestring
    Only activity strictly before this UTC time - bounds the profile counts AND the top arrays. Empty = up to now. Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    contractstring
    Restrict the top arrays to ONE exact token by its contract address (0x-hex, case-insensitive) — the reliable way to pin a token, since a currency symbol also matches clone tokens. The profile counts and Role still cover all tokens. Empty = no token filter.default
    currencystring
    Restrict the top receiver/sender arrays to one currency symbol (e.g. "USDT"). Empty = all. A symbol is NOT unique — clone/scam tokens reuse "USDC"/"USDT" and their broken decimals can make a fake token outrank the real one, so check the returned contract before trusting the ranking. No backslashes.default
    include_internalinteger
    0 (default) = top-level transfers only - the transfer a transaction makes itself, plus token transfers. 1 = also count ETH moved by contract code deeper inside a transaction (an internal transfer, for example through a batch sender or a withdrawal contract). Only 0 or 1.default 0
    max_amountinteger
    Maximum total amount for a counterparty to appear in the top arrays. 0 (default) = no upper bound. Amounts are in each token's own units and are NOT comparable across tokens, so a token with inflated or wrong decimals can total billions of units and push the real counterparties out of the top arrays - cap it here, or pin one token with contract.default 0
    min_amountinteger
    Minimum total amount for a counterparty to appear in the top arrays, in that token's own units. 0 (default) = no lower bound. Set it above 0 to drop dust and zero-value address-poisoning transfers (look-alike addresses that send 0 of a token so they show up next to the real counterparties).default 0
    top_ninteger
    How many top receivers and top senders to return (each).default 5
  • bitquerymcp_arbitrum_address_profileArbitrum (arb, ARB, Arbitrum One, L2) address STATISTICS — successful transfer counts out/in and distinct counterparties (receivers/senders), across all tokens.Read-only

    Arbitrum Address Profile

    Arbitrum (arb, ARB, Arbitrum One, L2) address STATISTICS — successful transfer counts out/in and distinct counterparties (receivers/senders), across all tokens. Fast triage of an address during tracing. For one-call triage that ALSO returns the top counterparties, prefer arbitrum_address_flow_summary. Role from the ratio (cheap triage before flow_edges): senders ≫ receivers = consolidator / sweep; receivers ≫ senders = distributor; ~1↔1 = relay (layering); thousands of both = mega-hub (exchange / treasury — don't trace deeper).

    Inputs

    addressstringrequired
    Address, 0x-hex (case-insensitive).
    after_timestring
    Only transfers at/after this UTC time - every count, time and amount covers that period. Empty = whole history. Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    before_timestring
    Only transfers strictly before this UTC time. Empty = up to now. Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    contractstring
    Restrict every figure to ONE exact token by its contract address (0x-hex (case-insensitive)) and return Sent_Amount / Received_Amount in that token's units. Empty = all tokens.default
    currencystring
    Restrict every figure to one currency symbol (e.g. "USDT") and return Sent_Amount / Received_Amount. A symbol is NOT unique - clone tokens reuse it; pin the real token with contract. Empty = all tokens. No backslashes.default
    include_internalinteger
    0 (default) = top-level transfers only - the transfer a transaction makes itself, plus token transfers. 1 = also count ETH moved by contract code deeper inside a transaction (an internal transfer, for example through a batch sender or a withdrawal contract). Only 0 or 1.default 0
  • bitquerymcp_arbitrum_find_callsFIND SMART-CONTRACT CALLS of a specific (possibly rare) method on ONE Arbitrum (arb, ARB, Arbitrum One, L2) contract — "who called method X on contract Y, when, did it succeed" in a single filtered query.Read-only

    Arbitrum Find Calls

    FIND SMART-CONTRACT CALLS of a specific (possibly rare) method on ONE Arbitrum (arb, ARB, Arbitrum One, L2) contract — "who called method X on contract Y, when, did it succeed" in a single filtered query. Match by method NAME (e.g. "transfer"), full SIGNATURE (e.g. "transfer(address,uint256)"), or raw 4-byte SELECTOR (e.g. "a9059cbb"). Includes internal calls, reverts and error text. Searches the LAST 7 DAYS by default — set after_time to widen or shift the window; page back by passing the oldest Time of the previous page as before_time (the 7-day window follows it). Wide windows on very busy contracts can be slow — narrow the window or retry. For value movements use arbitrum_transfers_out / arbitrum_transfers_in; for event logs use arbitrum_find_events.

    Inputs

    contractstringrequired
    Contract address being called, 0x-hex (case-insensitive).
    after_timestring
    Only calls at/after this UTC time. Empty = the last 7 days (measured back from the cursor position, or from before_time, when set). Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    before_timestring
    Coarse upper bound: only calls strictly before this UTC time. Do NOT page with it — it silently skips every row that shares the last row's timestamp; page with cursor instead. Ignored when cursor is set. Empty = no upper bound. Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    callerstring
    Only calls made by this address, 0x-hex (case-insensitive). Empty = any caller.default
    cursorstring
    Exact paging cursor: pass the Cursor value of the LAST row of the previous page to get the next page. Prefer this over before_time, which skips any rows sharing the last row's timestamp (busy contracts often have hundreds in one second). When set, before_time is ignored and the scan window ends at the cursor. Empty = first page.default
    daysinteger
    Size of the default scan window in DAYS, ending at the cursor position (or before_time, or now). Ignored when after_time is set. Use days=1 on a very high-traffic contract and page back with cursor.default 7
    limitinteger
    Max calls to return (newest first). At most 10000.default 20
    methodstring
    Method name (e.g. "transfer") or full signature (e.g. "transfer(address,uint256)"), case-insensitive. Empty = all methods.default
    only_successfulinteger
    1 = only successful calls in successful transactions; 0 = include reverted/failed calls.default 1
    selectorstring
    Raw 4-byte method selector, hex with or without 0x (e.g. "a9059cbb"). Use when the method name is unknown or unparsed. Empty = ignore.default
  • bitquerymcp_arbitrum_find_eventsFIND EVENT LOGS of a specific event on ONE Arbitrum (arb, ARB, Arbitrum One, L2) contract — "which X events involved contract Y, when, in which tx" in a single filtered query.Read-only

    Arbitrum Find Events

    FIND EVENT LOGS of a specific event on ONE Arbitrum (arb, ARB, Arbitrum One, L2) contract — "which X events involved contract Y, when, in which tx" in a single filtered query. Match by event NAME (e.g. "Transfer") or full SIGNATURE (e.g. "Transfer(address,address,uint256)"), case-insensitive. The contract matches both the called contract and the log emitter, so proxy tokens are found by their public address. Searches the LAST 7 DAYS by default — set after_time to widen or shift the window; page back by passing the oldest Time of the previous page as before_time (the 7-day window follows it). Wide windows on very busy contracts can be slow — narrow the window or retry. For decoded asset movements use arbitrum_transfers_out / arbitrum_transfers_in; for the calls themselves use arbitrum_find_calls.

    Inputs

    contractstringrequired
    Contract address, 0x-hex (case-insensitive) — matches logs this contract emitted, or that its code emitted as a proxy's implementation. Does NOT match logs of contracts it calls (e.g. a router's pair Swap events) — pass the pair/pool address instead, or find the router's transactions with arbitrum_find_calls and read their logs with arbitrum_tx_events.
    after_timestring
    Only events at/after this UTC time. Empty = the last 7 days (measured back from the cursor position, or from before_time, when set). Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    before_timestring
    Coarse upper bound: only events strictly before this UTC time. Do NOT page with it — it silently skips every row that shares the last row's timestamp; page with cursor instead. Ignored when cursor is set. Empty = no upper bound. Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    cursorstring
    Exact paging cursor: pass the Cursor value of the LAST row of the previous page to get the next page. Prefer this over before_time, which skips any rows sharing the last row's timestamp (busy contracts often have hundreds in one second). When set, before_time is ignored and the scan window ends at the cursor. Empty = first page.default
    daysinteger
    Size of the default scan window in DAYS, ending at the cursor position (or before_time, or now). Ignored when after_time is set. Use days=1 on a very high-traffic contract and page back with cursor.default 7
    emitterstring
    Narrow already matched rows to logs emitted by this contract address, 0x-hex. Empty = any emitter.default
    eventstring
    Event name (e.g. "Transfer") or full signature (e.g. "Transfer(address,address,uint256)"), case-insensitive. Empty = all events.default
    limitinteger
    Max events to return (newest first). At most 10000.default 20
    only_successfulinteger
    1 (default) = only events from successful transactions and non-reverted call frames; 0 = include failed/reverted traces.default 1
    topic0string
    Raw 32-byte event signature hash (topic0), hex with or without 0x. Use for unrecognized events whose Event and Signature are empty. Empty = any topic0.default
  • bitquerymcp_arbitrum_flow_edgesMONEYFLOW GRAPH EDGES out of an Arbitrum (arb, ARB, Arbitrum One, L2) address: one row per counterparty — Source → Target, total Amount, Currency.Read-only

    Arbitrum Flow Edges

    MONEYFLOW GRAPH EDGES out of an Arbitrum (arb, ARB, Arbitrum One, L2) address: one row per counterparty — Source → Target, total Amount, Currency. Building block for a MoneyFlow DIAGRAM. HOW TO DRAW: call this per address/hop, collect the edges, and emit a Mermaid `graph LR` (one node per address; each edge labeled with Amount+Currency). Pass the Target addresses to labels_for_addresses to flag CEX / mixer / bridge nodes and STOP expanding those branches. Pass a currency to avoid spam-token noise; call WITHOUT a currency filter to spot a token → USDT off-ramp at the edge. For raw per-transfer rows use arbitrum_transfers_out. Each edge carries the token Contract — pin one exact token with the contract param.

    Inputs

    addressstringrequired
    Source address, 0x-hex (case-insensitive).
    after_timestring
    Only transfers at/after this UTC time (e.g. "2026-06-01"). Empty = no lower bound.default
    before_timestring
    Only transfers strictly before this UTC time. Empty = up to now. Format: YYYY-MM-DD, YYYY-MM-DD HH:MM[:SS], ISO-8601 like 2026-09-20T00:00:00Z (fraction or +02:00 offset allowed) or 10-digit unix seconds.default
    contractstring
    Restrict to ONE exact token by its contract address (0x-hex, case-insensitive) — the reliable way to pin a token, since a currency symbol also matches clone tokens. Empty = no token filter.default
    currencystring
    Restrict to one currency symbol (e.g. "ETH", "USDT") — recommended. Empty = all. A symbol is NOT unique — look-alike clones share it, so confirm the token via the returned Contract or pin one with contract. No backslashes.default
    include_internalinteger
    0 (default) = top-level transfers only - the transfer a transaction makes itself, plus token transfers. 1 = also count ETH moved by contract code deeper inside a transaction (an internal transfer, for example through a batch sender or a withdrawal contract). Only 0 or 1.default 0
    limitinteger
    Max edges (largest amount first). At most 10000.default 20
    max_amountinteger
    Maximum total amount for an edge to be returned. 0 (default) = no upper bound. Amounts are in each token's own units and are NOT comparable across tokens, so a token with inflated or wrong decimals can total billions of units and push the real edges off the ranking - cap it here, or pin one token with contract.default 0
    min_amountinteger
    Minimum total amount for an edge to be returned. 0 = all.default 0