refactor(V2): get_instruments common-only on /mcp (enriched with Deribit filters)

Move get_instruments off the per-exchange Deribit router and onto the
unified interface, without losing the advanced Deribit filtering.

- /mcp/tools/get_instruments now accepts the Deribit-specific filters
  (currency, kind, expiry_from/to, strike_min/max, min_open_interest) and
  pagination (offset/limit). Venues that list everything (Hyperliquid)
  ignore them. Response adds `meta: {deribit: {total, offset, limit,
  has_more}}` surfacing per-source pagination.
- Remove /mcp-deribit/tools/get_instruments (endpoint + tool wrapper +
  GetInstrumentsReq schema). The DeribitClient.get_instruments METHOD
  stays — it's what the unified normalizer calls.

Tests: cover the enriched filters + meta passthrough; app-boot asserts
/mcp-deribit/tools/get_instruments is gone. Docs (API_REFERENCE/README/
CLAUDE) and smoke updated; also fixed the stale Hyperliquid tool count
(14 → 15) found during the doc check.

325 passed, ruff clean. Verified live: kind=option limit=5 → 5/940
has_more=true; /mcp-deribit/tools/get_instruments → 404.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano
2026-05-29 11:06:09 +00:00
parent 6faf21369d
commit 8d7e2ca30f
10 changed files with 126 additions and 75 deletions
+33 -5
View File
@@ -68,11 +68,18 @@ class _FakeDeribit:
self._raises = raises
async def get_instruments(self, **kwargs: Any) -> dict[str, Any]:
return {"instruments": [{
"name": "BTC-PERPETUAL", "kind": "future", "tick_size": 0.5,
"maker_commission": 0.0, "taker_commission": 0.0005,
"creation_timestamp": 1534377600000,
}]}
self.inst_call = kwargs
return {
"instruments": [{
"name": "BTC-PERPETUAL", "kind": "future", "tick_size": 0.5,
"maker_commission": 0.0, "taker_commission": 0.0005,
"creation_timestamp": 1534377600000,
}],
"total": 1,
"offset": kwargs.get("offset", 0),
"limit": kwargs.get("limit", 100),
"has_more": False,
}
async def get_historical(self, **kwargs: Any) -> dict[str, Any]:
if self._raises:
@@ -136,6 +143,27 @@ async def test_get_instruments_unsupported_exchange_raises_400():
assert exc.value.status_code == 400
@pytest.mark.asyncio
async def test_get_instruments_deribit_filters_and_meta():
fake = _FakeDeribit()
uc = UnifiedClient(_FakeRegistry({"deribit": fake}), env="mainnet")
out = await uc.get_instruments(
exchange="deribit", currency="ETH", kind="option",
strike_min=1000.0, expiry_to="2026-12-31", min_open_interest=5.0,
offset=5, limit=10,
)
# advanced Deribit filters reach the client
assert fake.inst_call["currency"] == "ETH"
assert fake.inst_call["kind"] == "option"
assert fake.inst_call["strike_min"] == 1000.0
assert fake.inst_call["expiry_to"] == "2026-12-31"
assert fake.inst_call["min_open_interest"] == 5.0
assert fake.inst_call["offset"] == 5 and fake.inst_call["limit"] == 10
# pagination metadata surfaced per source
assert out["meta"]["deribit"]["limit"] == 10
assert out["meta"]["deribit"]["has_more"] is False
@pytest.mark.asyncio
async def test_get_instruments_partial_failure_reports_failed():
uc = UnifiedClient(_FakeRegistry({"deribit": _FakeDeribit()}), env="mainnet")