Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,6 @@ server.run()
- `beacon_agent_status` — Get detailed status of a specific agent
- `beacon_send_message` — Send a message to another agent (costs RTC gas)
- `beacon_chat` — Chat with native Beacon agents (Sophia, Boris, etc.)
- `beacon_gas_balance` — Check RTC gas balance for messaging
- `beacon_gas_deposit` — Deposit RTC gas for messaging
- `beacon_contracts` — List bounties, agreements, and accords
- `beacon_network_stats` — Beacon network statistics

Expand Down Expand Up @@ -317,7 +315,7 @@ Solution: Check file size limits and format compatibility
MCP clients should treat failed RustChain, BoTTube, and Beacon calls as
verification failures, not as successful zero-value results. In particular,
`wallet_balance`, `rustchain_balance`, `rustchain_miners`,
`beacon_gas_balance`, and related balance/miner tools should return a
and related balance/miner tools should return a
predictable error object when the upstream service cannot be trusted.

Recommended shape:
Expand Down
63 changes: 3 additions & 60 deletions rustchain_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,7 @@ def rustchain_balance(wallet_id: str) -> dict:
"""
# Bypass any potential local caching by forcing a fresh network request
# and adding a timestamp to the query if the server supports it.
params = {"miner_id": wallet_id, "timestamp": int(time.time() * 1000)}
r = get_client().get(f"{RUSTCHAIN_NODE}/balance", params=params)
r = get_client().get(f"{RUSTCHAIN_NODE}/balance/{wallet_id}", params={"_ts": int(time.time() * 1000)})
r.raise_for_status()
return r.json()

Expand Down Expand Up @@ -220,7 +219,7 @@ def wallet_balance(wallet_id: str) -> dict:
# Try querying by address directly
pass

r = get_client().get(f"{RUSTCHAIN_NODE}/balance", params={"miner_id": wallet_id})
r = get_client().get(f"{RUSTCHAIN_NODE}/balance/{wallet_id}")
r.raise_for_status()
return r.json()

Expand Down Expand Up @@ -777,9 +776,6 @@ def beacon_send_message(
) -> dict:
"""Send a message to another agent via Beacon relay.

Costs RTC gas (0.0001 RTC per text message). Check your gas
balance with beacon_gas_balance first.

Args:
relay_token: Your relay token (from beacon_register)
from_agent: Your agent ID
Expand Down Expand Up @@ -830,56 +826,6 @@ def beacon_chat(agent_id: str, message: str) -> dict:
return r.json()


@mcp.tool()
def beacon_gas_balance(agent_id: str) -> dict:
"""Check RTC gas balance for Beacon messaging.

Sending messages through Beacon costs micro-fees in RTC:
- Text relay: 0.0001 RTC
- Attachment: 0.001 RTC
- Discovery: 0.00005 RTC

Args:
agent_id: Your agent ID to check gas balance for

Returns current gas balance in RTC.
"""
r = get_client().get(f"{BEACON_URL}/relay/gas/balance/{agent_id}")
r.raise_for_status()
return r.json()


@mcp.tool()
def beacon_gas_deposit(
agent_id: str,
amount_rtc: float,
admin_key: str = "",
) -> dict:
"""Deposit RTC gas for Beacon messaging.

Gas powers agent-to-agent communication. Deposit RTC to your
agent's gas balance to send messages through the relay.

Args:
agent_id: Agent ID to deposit gas for
amount_rtc: Amount of RTC to deposit
admin_key: Authorization key for deposit

Returns updated gas balance.
"""
headers = {}
if admin_key:
headers["X-Admin-Key"] = admin_key

r = get_client().post(
f"{BEACON_URL}/relay/gas/deposit",
json={"agent_id": agent_id, "amount_rtc": amount_rtc},
headers=headers,
)
r.raise_for_status()
return r.json()


@mcp.tool()
def beacon_contracts(agent_id: str = "") -> dict:
"""List Beacon contracts (bounties, agreements, accords).
Expand Down Expand Up @@ -1168,10 +1114,7 @@ def contributor_lookup(username: str) -> dict:
wallet_ids_to_try = [username, f"rtc-{username}", username.lower()]
for wallet_id in wallet_ids_to_try:
try:
r = client.get(
f"{RUSTCHAIN_NODE}/balance",
params={"miner_id": wallet_id},
)
r = client.get(f"{RUSTCHAIN_NODE}/balance/{wallet_id}")
if r.status_code == 200:
balance_data = r.json()
if balance_data.get("balance_rtc", 0) > 0 or balance_data.get("amount_i64", 0) > 0:
Expand Down
19 changes: 19 additions & 0 deletions tests/test_bottube_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,22 @@ def test_no_dead_v1_paths_anywhere(rec):
srv.bottube_trending()
srv.bottube_agent_profile("a")
assert all("/api/v1/" not in url for _, url, _, _ in rec.calls)


# --- RustChain balance path-lock (was /balance?miner_id=, dead; now /balance/{id}) ---
def test_rustchain_balance_uses_path_param(rec):
srv.rustchain_balance("sophia-elya")
m, url, params, _ = _last(rec)
assert m == "GET" and url.endswith("/balance/sophia-elya")
assert "miner_id" not in params and "?" not in url


def test_wallet_balance_uses_path_param(rec):
srv.wallet_balance("dual-g4-125")
_, url, params, _ = _last(rec)
assert url.split("?")[0].endswith("/balance/dual-g4-125") and "miner_id" not in params


def test_gas_tools_removed():
# the relay has no gas routes — these dead tools must not exist
assert not hasattr(srv, "beacon_gas_balance") and not hasattr(srv, "beacon_gas_deposit")
Loading