Agents
Eleven tools, three surfaces
The MCP server, the Pi extension and the OMP extension register the same eleven tools, and every one of them goes through withProvider() with the operation's capability, so selection, keys and the one retry behave as in the CLI. Cancelling works too. The MCP request's signal and the one Pi and OMP hand to execute reach ENS resolution and the explorer request, so an interrupted call stops right away instead of waiting out the request timeout.
| Tool | Reads | Input |
|---|---|---|
explorers_providers | every registered provider with its capabilities and endpoint | none |
explorers_balance | native balance for one address or a list of up to twenty | address or address[] |
explorers_tx_history | normalized transactions involving an address | address, paging options, tags and raw on MCP |
explorers_tx_detail | one transaction by hash, OP_RETURN and inscriptions included | hash, raw on MCP |
explorers_utxos | fifty unspent outputs unless limit says otherwise, Bitcoin family | address, limit |
explorers_pubkey | the public key an address has shown, and the txid that showed it | address |
explorers_contract | verification, compiler, creator, proxy and token metadata | address, abi and sourceCode on MCP |
explorers_tokens | fifty token holdings unless limit says otherwise, zeros dropped | address, nonZeroOnly, limit |
explorers_token_transfers | fungible transfers involving an address, one token or all | address, token, paging options |
explorers_gas | fee suggestions in the provider's unit | none |
explorers_block | one block by number | blockNumber |
Every tool takes an optional chain and provider. Addresses and ENS names get resolved before the read, so a model can pass vitalik.eth where it would pass hex. The answer is { provider, data } as JSON text, with provider naming the backend that actually answered, which after a retry isn't always the first candidate.
What stays out until asked
Over MCP the heavy fields don't travel by default. explorers_tx_history and explorers_tx_detail drop raw unless the call says raw: true, and explorers_contract drops abi and sourceCode unless the call says abi: true or sourceCode: true. A provider's own transaction record dwarfs the normalized fields, and an ABI runs to tens of kilobytes. A model that wants to know whether a contract is verified shouldn't pay for its source, and one that needs every input of a Bitcoin transaction can ask. The Pi and OMP extensions render a short text summary and keep the full object in the tool's details instead.
explorers_tokens lists fifty holdings unless limit says otherwise, at most a hundred, in the explorer's own order, which on Blockscout puts the priced ones first by USD value. total counts every holding the list was cut from, zeros excluded unless nonZeroOnly is false, so a model can tell a wallet with fifty tokens from one that has been airdropped at six thousand times without paying for the list; the Pi and OMP text says 6766 tokens … 50 listed for the same read. getTokenBalances() in the library still returns everything.
explorers_utxos cuts the same way, at fifty outputs unless limit says otherwise, with total counting all of them. An address that collects dust makes this matter. The genesis address on Bitcoin SV comes back from WhatsOnChain as ten thousand outputs, 1.8 MB of JSON, while fifty of them are 9 kB. The Pi and OMP header counts every output and sums the confirmed ones, so the balance you read off it doesn't shrink with the list.
MCP
explorers mcp
claude mcp add explorers --scope user -- npx -y @agntn/explorers mcp
Or in a client's config:
{
"mcpServers": {
"explorers": { "command": "npx", "args": ["-y", "@agntn/explorers", "mcp"] }
}
}
The server speaks MCP over stdio. Bringing your own transport? @agntn/explorers/mcp hands you the same server, no stdio attached:
import { createMcpServer } from "@agntn/explorers/mcp";
await createMcpServer().connect(transport);
It shares the registry with the main entry. A provider you register() there shows up in explorers_providers too. One registry, not two that politely ignore each other.
All eleven tools carry readOnlyHint: true, because that's what they are: nothing here signs, sends or holds a key on the model's behalf.
Keys stay in the process
Keys come from the agent's process environment, ETHERSCAN_API_KEY and the rest, never from a tool argument. A model can choose a provider and a chain, it can't supply a credential, and an error that reaches it has the key redacted already. A provider that needs a key the process doesn't have fails with an AuthError naming the provider, which a model can read and a session can survive.
Pi and OMP
omp install @agntn/explorers
pi install npm:@agntn/explorers
The package declares both extensions in package.json. Installing the OMP extension doesn't replace or reuse the Pi one; they're separate entrypoints that register the same eleven tools and render their calls and results in the terminal. From a source checkout, omp install . links the local package.
What the answer is
The tools return the normalized objects, not prose. The Explorer shows the same objects for any public address, a quick way to check a model's summary of a wallet against the source.
functionName or a symbol become an instruction.