From c896e15ca4b9e8cd652fb2da6524363e71742ffa Mon Sep 17 00:00:00 2001
From: JaeLeex
Date: Tue, 16 Jun 2026 12:46:02 -0400
Subject: [PATCH 1/2] feat: add TreadFi CLI status skeleton
Expose read-only TreadFi contract status through `hl treadfi` so agents can see what is blocked without calling invented endpoints.
Co-authored-by: Cursor
---
cli/commands/treadfi.py | 35 +++++++++++++
cli/main.py | 2 +
modules/treadfi_contract.py | 93 ++++++++++++++++++++++++++++++++++
tests/test_treadfi_contract.py | 45 ++++++++++++++++
4 files changed, 175 insertions(+)
create mode 100644 cli/commands/treadfi.py
create mode 100644 modules/treadfi_contract.py
create mode 100644 tests/test_treadfi_contract.py
diff --git a/cli/commands/treadfi.py b/cli/commands/treadfi.py
new file mode 100644
index 0000000..cb53ca3
--- /dev/null
+++ b/cli/commands/treadfi.py
@@ -0,0 +1,35 @@
+"""hl treadfi — read-only TreadFi integration status."""
+from __future__ import annotations
+
+import json
+from typing import Any
+
+import typer
+
+from modules import treadfi_contract
+
+treadfi_app = typer.Typer(no_args_is_help=True)
+
+
+def _echo_json(payload: dict[str, Any]) -> None:
+ typer.echo(json.dumps(payload, indent=2))
+
+
+@treadfi_app.command("spec-status")
+def treadfi_spec_status():
+ """Report whether the TreadFi endpoint/MCP contract is present."""
+ _echo_json(treadfi_contract.spec_status())
+
+
+@treadfi_app.command("capabilities")
+def treadfi_capabilities():
+ """List local TreadFi placeholders and blocked live capabilities."""
+ _echo_json(treadfi_contract.capabilities())
+
+
+@treadfi_app.command("market-params")
+def treadfi_market_params(
+ instrument: str = typer.Option("BTCSWP-USDYP", "--instrument", "-i"),
+):
+ """Show locally known BTCSWP identifiers and missing live params."""
+ _echo_json(treadfi_contract.market_params(instrument=instrument))
diff --git a/cli/main.py b/cli/main.py
index 6253f07..6794006 100644
--- a/cli/main.py
+++ b/cli/main.py
@@ -35,6 +35,7 @@
from cli.commands.skills import skills_app
from cli.commands.journal import journal_app
from cli.commands.keys import keys_app
+from cli.commands.treadfi import treadfi_app
app.command("run", help="Start autonomous trading with a strategy")(run_cmd)
app.command("status", help="Show positions, PnL, and risk state")(status_cmd)
@@ -53,6 +54,7 @@
app.add_typer(skills_app, name="skills", help="Skill discovery and registry")
app.add_typer(journal_app, name="journal", help="Trade journal — structured position records with reasoning")
app.add_typer(keys_app, name="keys", help="Unified key management across backends")
+app.add_typer(treadfi_app, name="treadfi", help="TreadFi integration status and read-only capability placeholders")
def main():
diff --git a/modules/treadfi_contract.py b/modules/treadfi_contract.py
new file mode 100644
index 0000000..160f748
--- /dev/null
+++ b/modules/treadfi_contract.py
@@ -0,0 +1,93 @@
+"""Read-only TreadFi contract status helpers.
+
+This module deliberately avoids network calls. It gives CLI and MCP surfaces a
+shared, machine-readable view of what is locally known and what remains blocked
+until TreadFi/Eng provides a real endpoint contract.
+"""
+from __future__ import annotations
+
+from typing import Any
+
+from common.models import asset_to_coin, asset_to_instrument
+
+
+REQUIRED_CONTRACT_FIELDS = (
+ "transport",
+ "environments",
+ "auth",
+ "discovery",
+ "market_data",
+ "campaign_reporting",
+ "execution",
+ "operations",
+ "fixtures",
+)
+
+
+def spec_status() -> dict[str, Any]:
+ """Return the current TreadFi integration contract status."""
+ return {
+ "status": "blocked",
+ "reason": "TreadFi endpoint and MCP tool specs are not present in agent-cli.",
+ "required_contract_fields": list(REQUIRED_CONTRACT_FIELDS),
+ "missing_contract_fields": list(REQUIRED_CONTRACT_FIELDS),
+ "known_local_context": {
+ "agent_cli_command": "hl",
+ "mcp_command": "hl mcp serve",
+ "mcp_server": "cli/mcp_server.py",
+ "btcswp_instrument": asset_to_instrument("BTCSWP"),
+ "btcswp_coin": asset_to_coin("BTCSWP"),
+ },
+ "next_step": "Provide the TreadFi contract with fixtures before adding live client or execution code.",
+ }
+
+
+def capabilities() -> dict[str, Any]:
+ """Return local placeholder capabilities without claiming live support."""
+ return {
+ "live_discovery_available": False,
+ "status": "contract_missing",
+ "read_only_placeholders": [
+ {
+ "name": "spec_status",
+ "description": "Report which TreadFi endpoint contract fields are still missing.",
+ },
+ {
+ "name": "market_params",
+ "description": "Return locally known BTCSWP identifiers and mark live params unavailable.",
+ },
+ ],
+ "blocked_until_contract": [
+ "list_treadfi_tools",
+ "read_treadfi_depth",
+ "read_treadfi_leaderboard",
+ "place_treadfi_quote",
+ "cancel_treadfi_quote",
+ ],
+ }
+
+
+def market_params(instrument: str = "BTCSWP-USDYP") -> dict[str, Any]:
+ """Return locally known BTCSWP identifiers and missing live fields."""
+ requested = instrument.upper()
+ local_instrument = asset_to_instrument("BTCSWP")
+ local_coin = asset_to_coin("BTCSWP")
+ known = requested in {"BTCSWP", local_instrument, local_coin.upper()}
+ return {
+ "requested": instrument,
+ "known_locally": known,
+ "instrument": local_instrument,
+ "coin": local_coin,
+ "source": "common.models HIP-3 instrument registry",
+ "live_params_available": False,
+ "missing_live_fields": [
+ "canonical_treadfi_market_id",
+ "oracle_reference_price",
+ "depth_bands",
+ "tick_size",
+ "size_decimals",
+ "collateral",
+ "campaign_window",
+ "attribution_rule",
+ ],
+ }
diff --git a/tests/test_treadfi_contract.py b/tests/test_treadfi_contract.py
new file mode 100644
index 0000000..e826955
--- /dev/null
+++ b/tests/test_treadfi_contract.py
@@ -0,0 +1,45 @@
+from __future__ import annotations
+
+import json
+
+from typer.testing import CliRunner
+
+from cli.main import app
+from modules import treadfi_contract
+
+
+runner = CliRunner()
+
+
+def test_spec_status_is_blocked_until_contract_exists():
+ status = treadfi_contract.spec_status()
+
+ assert status["status"] == "blocked"
+ assert "transport" in status["missing_contract_fields"]
+ assert status["known_local_context"]["mcp_command"] == "hl mcp serve"
+
+
+def test_market_params_use_local_btcswp_registry():
+ params = treadfi_contract.market_params()
+
+ assert params["known_locally"] is True
+ assert params["instrument"] == "BTCSWP-USDYP"
+ assert params["coin"] == "yex:BTCSWP"
+ assert params["live_params_available"] is False
+
+
+def test_treadfi_spec_status_cli_outputs_json():
+ result = runner.invoke(app, ["treadfi", "spec-status"])
+
+ assert result.exit_code == 0
+ payload = json.loads(result.stdout)
+ assert payload["status"] == "blocked"
+
+
+def test_treadfi_market_params_cli_accepts_instrument():
+ result = runner.invoke(app, ["treadfi", "market-params", "--instrument", "BTCSWP"])
+
+ assert result.exit_code == 0
+ payload = json.loads(result.stdout)
+ assert payload["requested"] == "BTCSWP"
+ assert payload["known_locally"] is True
From 7688b632c27016f12dc05b00be7bd12bfd9b9de1 Mon Sep 17 00:00:00 2001
From: JaeLeex
Date: Tue, 16 Jun 2026 12:47:28 -0400
Subject: [PATCH 2/2] feat: expose TreadFi status over MCP
Add explicit read-only FastMCP tools for TreadFi contract status so agents can discover the blocked integration state.
Co-authored-by: Cursor
---
README.md | 10 +++++-----
cli/mcp_server.py | 25 +++++++++++++++++++++++
docs/api-reference.md | 42 ++++++++++++++++++++++++++++++++++++++-
tests/test_treadfi_mcp.py | 40 +++++++++++++++++++++++++++++++++++++
4 files changed, 111 insertions(+), 6 deletions(-)
create mode 100644 tests/test_treadfi_mcp.py
diff --git a/README.md b/README.md
index f69ebcc..2e8af28 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@
-
+
@@ -481,9 +481,9 @@ hl mcp serve # stdio transport (default)
hl mcp serve --transport sse # SSE transport
```
-**16 tools exposed:** `account`, `status`, `trade`, `run_strategy`, `strategies`, `radar_run`, `apex_status`, `apex_run`, `reflect_run`, `setup_check`, `builder_status`, `wallet_list`, `wallet_auto`, `agent_memory`, `trade_journal`, `judge_report`
+**20 tools exposed:** `account`, `status`, `trade`, `run_strategy`, `strategies`, `radar_run`, `apex_status`, `apex_run`, `reflect_run`, `setup_check`, `builder_status`, `wallet_list`, `wallet_auto`, `agent_memory`, `trade_journal`, `judge_report`, `obsidian_context`, `treadfi_spec_status`, `treadfi_capabilities`, `treadfi_market_params`
-Fast tools (strategies, builder, wallet, setup, memory, journal, judge) call Python directly — zero subprocess overhead.
+Fast tools (strategies, builder, wallet, setup, memory, journal, judge, TreadFi status) call Python directly — zero subprocess overhead.
### HTTP API & SSE
@@ -533,7 +533,7 @@ One-click deploy of a full OpenClaw agent that uses our CLI as the tool backend.
**What you get:**
- OpenClaw gateway with web UI at `/openclaw`
- Telegram integration — chat with your bot to start/stop trading, run scans, check status
-- Our 13 MCP trading tools as the agent's primary capabilities
+- Our MCP trading tool catalog as the agent's primary capabilities
- Persistent state across redeploys via `/data` volume
- Auto-onboard: bot sends "Agent ready" to Telegram on first deploy
- REFLECT self-improvement: the agent analyzes its own trades and adjusts strategy parameters
@@ -572,7 +572,7 @@ hl run engine_mm -i BTCSWP-USDYP --tick 10
```
cli/ CLI commands and trading engine
commands/ Subcommand modules (run, apex, radar, pulse, guard, reflect, house, ...)
- mcp_server.py MCP server (16 tools via FastMCP)
+ mcp_server.py MCP server (20 tools via FastMCP)
hl_adapter.py Direct HL API adapter (live + mock)
builder_fee.py Builder fee config (HL native BuilderInfo)
keystore.py Encrypted keystore (geth-compatible)
diff --git a/cli/mcp_server.py b/cli/mcp_server.py
index 1544778..887b09e 100644
--- a/cli/mcp_server.py
+++ b/cli/mcp_server.py
@@ -149,6 +149,31 @@ def setup_check() -> str:
"passed": len(issues) == 0,
}, indent=2)
+ @mcp.tool()
+ def treadfi_spec_status() -> str:
+ """Report whether the TreadFi endpoint/MCP contract is present."""
+ from modules import treadfi_contract
+
+ return json.dumps(treadfi_contract.spec_status(), indent=2)
+
+ @mcp.tool()
+ def treadfi_capabilities() -> str:
+ """List local TreadFi placeholders and blocked live capabilities."""
+ from modules import treadfi_contract
+
+ return json.dumps(treadfi_contract.capabilities(), indent=2)
+
+ @mcp.tool()
+ def treadfi_market_params(instrument: str = "BTCSWP-USDYP") -> str:
+ """Show locally known BTCSWP identifiers and missing live params.
+
+ Args:
+ instrument: Requested market identifier (default: BTCSWP-USDYP)
+ """
+ from modules import treadfi_contract
+
+ return json.dumps(treadfi_contract.market_params(instrument=instrument), indent=2)
+
@mcp.tool()
def account(mainnet: bool = False) -> str:
"""Get Hyperliquid account state (balances, positions)."""
diff --git a/docs/api-reference.md b/docs/api-reference.md
index a746466..e5ad1d7 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -592,7 +592,7 @@ python leaderboard.py serve --port 8090
## MCP Server
-The MCP server exposes 16 tools for AI agent orchestration via the [Model Context Protocol](https://modelcontextprotocol.io). This is the access path for Claude Code, OpenClaw, or any MCP-compatible client.
+The MCP server exposes 20 tools for AI agent orchestration via the [Model Context Protocol](https://modelcontextprotocol.io). This is the access path for Claude Code, OpenClaw, or any MCP-compatible client.
### Starting the Server
@@ -712,6 +712,43 @@ Returns:
}
```
+#### `treadfi_spec_status()`
+
+Report whether the TreadFi endpoint/MCP contract is present. This is read-only
+and does not call TreadFi.
+
+```
+Tool: treadfi_spec_status
+Args: (none)
+```
+
+Returns JSON with `status`, missing contract fields, and known local `agent-cli`
+context.
+
+#### `treadfi_capabilities()`
+
+List local TreadFi placeholders and blocked live capabilities.
+
+```
+Tool: treadfi_capabilities
+Args: (none)
+```
+
+Returns JSON that marks live TreadFi discovery unavailable until Eng provides the
+real contract.
+
+#### `treadfi_market_params(instrument="BTCSWP-USDYP")`
+
+Show locally known BTCSWP identifiers and missing live market parameters.
+
+```
+Tool: treadfi_market_params
+Args: { "instrument": "BTCSWP-USDYP" }
+```
+
+Returns JSON with the local `BTCSWP-USDYP` / `yex:BTCSWP` identifiers and the
+TreadFi fields still missing from the live contract.
+
### Action Tools (Subprocess, seconds to minutes)
These shell out to the CLI and may take significant time.
@@ -1052,6 +1089,9 @@ else:
| `wallet_list` | Fast | <100ms | None |
| `wallet_auto` | Fast | <500ms | Creates keystore file |
| `setup_check` | Fast | <100ms | None |
+| `treadfi_spec_status` | Fast | <100ms | None |
+| `treadfi_capabilities` | Fast | <100ms | None |
+| `treadfi_market_params` | Fast | <100ms | None |
| `account` | Subprocess | 1-5s | None |
| `status` | Subprocess | <1s | None |
| `trade` | Subprocess | 1-5s | Places order on HL |
diff --git a/tests/test_treadfi_mcp.py b/tests/test_treadfi_mcp.py
new file mode 100644
index 0000000..676de8e
--- /dev/null
+++ b/tests/test_treadfi_mcp.py
@@ -0,0 +1,40 @@
+from __future__ import annotations
+
+import json
+import sys
+import types
+from typing import Callable
+
+
+class FakeFastMCP:
+ def __init__(self, *_args, **_kwargs):
+ self.tools: dict[str, Callable] = {}
+
+ def tool(self, name: str | None = None, **_kwargs):
+ def decorator(func: Callable):
+ self.tools[name or func.__name__] = func
+ return func
+
+ return decorator
+
+
+def test_treadfi_tools_register_with_fastmcp(monkeypatch):
+ fastmcp_module = types.ModuleType("mcp.server.fastmcp")
+ fastmcp_module.FastMCP = FakeFastMCP
+ server_module = types.ModuleType("mcp.server")
+ mcp_module = types.ModuleType("mcp")
+
+ monkeypatch.setitem(sys.modules, "mcp", mcp_module)
+ monkeypatch.setitem(sys.modules, "mcp.server", server_module)
+ monkeypatch.setitem(sys.modules, "mcp.server.fastmcp", fastmcp_module)
+
+ from cli.mcp_server import create_mcp_server
+
+ server = create_mcp_server()
+
+ assert "treadfi_spec_status" in server.tools
+ assert "treadfi_capabilities" in server.tools
+ assert "treadfi_market_params" in server.tools
+
+ payload = json.loads(server.tools["treadfi_spec_status"]())
+ assert payload["status"] == "blocked"