Skip to content
Open
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
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<img src="https://img.shields.io/badge/strategies-14-C9A84C" alt="Strategies" />
<img src="https://img.shields.io/badge/tests-483%20passing-brightgreen" alt="Tests" />
<img src="https://img.shields.io/badge/license-MIT-blue" alt="License" />
<img src="https://img.shields.io/badge/MCP-16%20tools-8A2BE2" alt="MCP" />
<img src="https://img.shields.io/badge/MCP-20%20tools-8A2BE2" alt="MCP" />
</p>

<p align="center">
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions cli/commands/treadfi.py
Original file line number Diff line number Diff line change
@@ -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))
2 changes: 2 additions & 0 deletions cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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():
Expand Down
25 changes: 25 additions & 0 deletions cli/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)."""
Expand Down
42 changes: 41 additions & 1 deletion docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 |
Expand Down
93 changes: 93 additions & 0 deletions modules/treadfi_contract.py
Original file line number Diff line number Diff line change
@@ -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",
],
}
45 changes: 45 additions & 0 deletions tests/test_treadfi_contract.py
Original file line number Diff line number Diff line change
@@ -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
40 changes: 40 additions & 0 deletions tests/test_treadfi_mcp.py
Original file line number Diff line number Diff line change
@@ -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"