| title | For agents |
|---|---|
| description | How an agent discovers, previews, and safely executes any Bittensor operation. |
The SDK and CLI are built to be driven by agents. Every operation is
discoverable at runtime with a JSON schema, every mutation can be previewed
before it spends anything, every failure returns a machine-readable code with a
remediation hint, and a Policy can hard-bound what a session is allowed to
do. Nothing here requires parsing human prose.
Start with one fetch: /llms.txt. Every docs link in it is
already raw markdown (content.md), so you can open a page without rewriting
URLs or scraping HTML.
# Curated index (start here)
curl -s https://www.bittensor.com/llms.txt
# Full docs corpus (for rg, not for stuffing into context)
curl -s https://www.bittensor.com/llms-full.txt | rg -n '<pattern>'
# One page (same URLs as the links inside llms.txt)
curl -s https://www.bittensor.com/llms.mdx/docs/quickstart/content.md
# Chain source: search by symbol, or fetch a known path
curl -s 'https://www.bittensor.com/code/search.json?q=do_move_stake'
curl -s https://www.bittensor.com/code/raw/pallets/subtensor/src/staking/move_stake.rs
# Heavy exploration: clone the repo (docs live in docs/)
git clone --depth 1 https://github.com/RaoFoundation/subtensorPrefer that index plus the JSON catalogs below over dumping
/llms-full.txt into context.
btcli tools # JSON: every transaction op + summary, description, signer, input schemaimport bittensor as bt
bt.intents.list_tools() # same catalog, as Python dicts
bt.reads.list_reads() # every read: name, params, docs, categoryThe same catalogs are published statically by these docs:
/catalog/intents.json,
/catalog/reads.json,
/catalog/errors.json. Each entry includes
docs_url (HTML), markdown_url (raw markdown to fetch), and often
sources / hits with a raw_url into /code. Errors live under
.codes (semantic) and .chain_errors (exact pallet names); see .note.
An agent never needs to import intent classes — build and execute by op name with a plain dict, validated against the schema:
async with bt.Subtensor() as client:
result = await client.execute_tool(
"transfer", {"dest_ss58": "5F...", "amount_tao": 1.0}, wallet
)On the CLI, every op in the catalog is btcli tx <op-name> (underscores
become dashes) and every read is btcli query <name>.
plan (SDK) and --dry-run (CLI) run the full pipeline — fee estimation,
predicted effects, warnings, policy check — without submitting:
plan = await client.plan(intent, wallet)
plan.fee # estimated fee
plan.effects # list[str]: what this will do
plan.warnings # non-fatal cautions (e.g. dust amounts)
plan.ok # would policy allow it?Attach a Policy to the client and every execution — by class, by name, or
raw — passes through one choke point:
policy = bt.Policy(max_spend_tao=5.0, allowed_netuids=[1, 2])
async with bt.Subtensor("finney", policy=policy) as client:
... # anything exceeding the caps raises PolicyError at execute timeOperations whose cost cannot be bounded ahead of time (e.g. subnet
registration, whose price floats) are blocked by a spend cap until it is
raised — the safe default. Raw calls are refused unless the policy sets
allow_raw_calls=True.
Every write returns an ExtrinsicResult; failures carry a semantic
ErrorCode and a remediation hint:
result = await client.execute(intent, wallet)
if not result.success:
match result.error.code:
case bt.ErrorCode.RATE_LIMITED: ... # wait and retry
case bt.ErrorCode.INSUFFICIENT_BALANCE: ... # reduce amount
case _:
log(result.error.remediation) # actionable next step, always present--jsonon any command produces machine-readable output.--yesskips confirmation prompts. Without it, a non-interactive session is declined, not blocked — the CLI never hangs waiting for input it can't get.--dry-runpreviews anytxcommand.- Configuration comes from flags,
BT_*environment variables, orbtcli config— highest to lowest precedence.
Anything on chain that no intent wraps (deprecated, root/admin-only, or off-chain-signed calls) is still reachable:
call = bt.calls.Commitments.set_commitment(netuid=1, info={...})
await client.submit_call(call, wallet, signer="hotkey")And generic accessors cover any storage item, constant, or runtime API in the chain metadata:
tempo = await client.query(bt.storage.SubtensorModule.Tempo, [1])
ed = await client.constant(bt.constants.Balances.ExistentialDeposit)