Summary
The get_all_balances and get_spendable_balances MCP tools return empty balances + total=0 for addresses that provably hold multiple denominations on-chain. The denom-specific get_balance tool works correctly for the same addresses. The aggregate queries return a valid-shaped JSON response ({"balances":[], "pagination":{"total":"0"}}) rather than an error, making the failure silent.
Surfaced via the regen-python-mcp submodule inside glandua/regen-mcps during a 2026-04-17/18 claims-engine dogfooding session while querying two different mainnet addresses.
Reproduction
Address (primary wallet, mainnet regen-1): regen1tumh45fsagtstswtfwhm0fypka0p7s5x6n2fct
A. MCP calls (this repo, via Claude Code)
get_all_balances(address="regen1tumh45fsagtstswtfwhm0fypka0p7s5x6n2fct")
→ {"result":{"balances":[],"pagination":{"next_key":null,"total":"0"}}}
get_spendable_balances(address="regen1tumh45fsagtstswtfwhm0fypka0p7s5x6n2fct")
→ {"result":{"balances":[],"pagination":{"next_key":null,"total":"0"}}}
get_balance(address="regen1tumh45fsagtstswtfwhm0fypka0p7s5x6n2fct", denom="uregen")
→ {"result":{"balance":{"denom":"uregen","amount":"31632387289"}}}
B. Ground-truth CLI (same RPC endpoint)
$ regen query bank balances regen1tumh45fsagtstswtfwhm0fypka0p7s5x6n2fct \
--node https://regen-rpc.polkachu.com:443 --output json
{
"balances": [
{"denom": "ibc/334740505537E9894A64E8561030695016481830D7B36E6A9B6D13C608B55653", "amount": "7400000"},
{"denom": "uregen", "amount": "31632387289"}
],
"pagination": {"total": "2"}
}
Spendable balances via CLI returns the same two denoms (nothing is locked/staked for this address at query time).
C. Direct REST call to an endpoint the MCP uses
$ curl -s "https://regen-rest.publicnode.com/cosmos/bank/v1beta1/balances/regen1tumh45fsagtstswtfwhm0fypka0p7s5x6n2fct"
{"balances":[{"denom":"ibc/334740505537E9894A64E8561030695016481830D7B36E6A9B6D13C608B55653","amount":"7400000"},{"denom":"uregen","amount":"31632387289"}],"pagination":{"next_key":null,"total":"2"}}
So the on-chain state is populated, the CLI sees it, the REST endpoint this MCP lists in its fallback chain (regen-rest.publicnode.com) returns the populated response, and the per-denom MCP tool surfaces the right value. Only the MCP aggregate wrappers (get_all_balances, get_spendable_balances) return empty.
Suspected location
src/mcp_server/tools/bank_tools.py — get_all_balances (≈L58), get_spendable_balances (≈L119)
- Both delegate to
RegenClient.query_all_balances / query_spendable_balances in src/mcp_server/client/regen_client.py (≈L1103, L1132), which hit /cosmos/bank/v1beta1/balances/{address} and /cosmos/bank/v1beta1/spendable_balances/{address}.
Suspected root causes worth checking (in order of likelihood):
- The
regen_rest_endpoints default list in src/mcp_server/config/settings.py has https://api.regen.network first. That host returns a 404 HTML body (Cannot GET /cosmos/bank/v1beta1/balances/...) for both aggregate endpoints and for the /by_denom endpoint — yet get_balance clearly succeeds in the same session. So the two code paths appear to resolve different endpoints. Worth confirming whether the settings list is actually read (the RegenClient.__init__ hardcodes a different list starting with public-rpc.regen.vitwit.com:1317).
- If
api.regen.network IS being hit first and returning HTML for the aggregate path specifically, and the fallback logic silently swallows a parse error and returns {"balances": []}, that would match the observed symptom exactly.
- Less likely, but —
Pagination.to_query_params() emits pagination.limit=100&pagination.offset=0&pagination.count_total=true&pagination.reverse=false. Those are the correct Cosmos SDK v1beta1 param names; I verified the upstream REST accepts them and returns the populated response. So the param encoding itself is not the bug.
Workaround (current)
Use get_balance(address=..., denom="uregen") for each denom of interest. Not ergonomic when you don't know the denom list in advance, but it works.
Impact
Silently-wrong aggregate balance queries are a footgun for:
- Portfolio-style tools that iterate balances
- Pre-broadcast affordability checks (balance-of-0 → "insufficient funds" on a funded address)
- Any LLM-driven flow that branches on "is this address empty?"
Not a critical blocker (denom-specific query works), but it was observed during two separate sessions and produced misleading prompts during an on-chain anchoring run. Filing so it doesn't rot.
Context
- Session journal where this was first noticed: 2026-04-17 claims-engine dogfood + first mainnet
MsgAnchor run.
- Second confirmation: 2026-04-18, new session, same two addresses, same behavior.
- Filed as an issue (not a PR) — I'm not the maintainer of this code.
Summary
The
get_all_balancesandget_spendable_balancesMCP tools return emptybalances+total=0for addresses that provably hold multiple denominations on-chain. The denom-specificget_balancetool works correctly for the same addresses. The aggregate queries return a valid-shaped JSON response ({"balances":[], "pagination":{"total":"0"}}) rather than an error, making the failure silent.Surfaced via the
regen-python-mcpsubmodule insideglandua/regen-mcpsduring a 2026-04-17/18 claims-engine dogfooding session while querying two different mainnet addresses.Reproduction
Address (primary wallet, mainnet
regen-1):regen1tumh45fsagtstswtfwhm0fypka0p7s5x6n2fctA. MCP calls (this repo, via Claude Code)
B. Ground-truth CLI (same RPC endpoint)
Spendable balances via CLI returns the same two denoms (nothing is locked/staked for this address at query time).
C. Direct REST call to an endpoint the MCP uses
So the on-chain state is populated, the CLI sees it, the REST endpoint this MCP lists in its fallback chain (
regen-rest.publicnode.com) returns the populated response, and the per-denom MCP tool surfaces the right value. Only the MCP aggregate wrappers (get_all_balances,get_spendable_balances) return empty.Suspected location
src/mcp_server/tools/bank_tools.py—get_all_balances(≈L58),get_spendable_balances(≈L119)RegenClient.query_all_balances/query_spendable_balancesinsrc/mcp_server/client/regen_client.py(≈L1103, L1132), which hit/cosmos/bank/v1beta1/balances/{address}and/cosmos/bank/v1beta1/spendable_balances/{address}.Suspected root causes worth checking (in order of likelihood):
regen_rest_endpointsdefault list insrc/mcp_server/config/settings.pyhashttps://api.regen.networkfirst. That host returns a 404 HTML body (Cannot GET /cosmos/bank/v1beta1/balances/...) for both aggregate endpoints and for the/by_denomendpoint — yetget_balanceclearly succeeds in the same session. So the two code paths appear to resolve different endpoints. Worth confirming whether the settings list is actually read (theRegenClient.__init__hardcodes a different list starting withpublic-rpc.regen.vitwit.com:1317).api.regen.networkIS being hit first and returning HTML for the aggregate path specifically, and the fallback logic silently swallows a parse error and returns{"balances": []}, that would match the observed symptom exactly.Pagination.to_query_params()emitspagination.limit=100&pagination.offset=0&pagination.count_total=true&pagination.reverse=false. Those are the correct Cosmos SDK v1beta1 param names; I verified the upstream REST accepts them and returns the populated response. So the param encoding itself is not the bug.Workaround (current)
Use
get_balance(address=..., denom="uregen")for each denom of interest. Not ergonomic when you don't know the denom list in advance, but it works.Impact
Silently-wrong aggregate balance queries are a footgun for:
Not a critical blocker (denom-specific query works), but it was observed during two separate sessions and produced misleading prompts during an on-chain anchoring run. Filing so it doesn't rot.
Context
MsgAnchorrun.