Self-custodial Hyperliquid perps trading for AI agents, with limits you set and a kill switch.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Bearer <your vd_agt_ API key>. Docs, markets and registration work without it; the register_agent tool returns the key.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Authorize the key that signs your LIVE orders. Generate a NEW secp256k1 key (no Hyperliquid history — Hyperliquid refuses keys that already have an account). Call with no arguments to get the message that key must sign (EIP-191) as proof. Fund the trading wallet first. Then call with trading_key_address + trading_key_proof: you get payload_b64 (the exact Privy request as canonical JSON) and a `verify` list — decode it and check every field against YOUR records (your saved privy_wallet_id, your trading key) BEFORE signing it with your OWNER P-256 key (ECDSA P-256 over SHA-256, DER, base64). Call a third time adding time + owner_signature. Valid 180 days; repeat to renew or rotate.
Last call only: the `time` returned by the previous call
Last call only: your OWNER key's signature over payload_b64
EIP-191 signature of trading_key_message by the trading key
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http terminal https://terminal.vectorialdata.com/api/mcp
Headers
Authorization (optional, not in the config above): Bearer <your vd_agt_ API key>. Docs, markets and registration work without it; the register_agent tool returns the key.Authorize the key that signs your LIVE orders. Generate a NEW secp256k1 key (no Hyperliquid history — Hyperliquid refuses keys that already have an account). Call with no arguments to get the message that key must sign (EIP-191) as proof. Fund the trading wallet first. Then call with trading_key_address + trading_key_proof: you get payload_b64 (the exact Privy request as canonical JSON) and a `verify` list — decode it and check every field against YOUR records (your saved privy_wallet_id, your trading key) BEFORE signing it with your OWNER P-256 key (ECDSA P-256 over SHA-256, DER, base64). Call a third time adding time + owner_signature. Valid 180 days; repeat to renew or rotate.
| Argument | Type | Description |
|---|---|---|
| time | integer | Last call only: the `time` returned by the previous call |
| owner_signature | string | Last call only: your OWNER key's signature over payload_b64 |
| trading_key_proof | string | EIP-191 signature of trading_key_message by the trading key |
| trading_key_address | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"time": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Last call only: the `time` returned by the previous call"
},
"owner_signature": {
"type": "string",
"description": "Last call only: your OWNER key's signature over payload_b64"
},
"trading_key_proof": {
"type": "string",
"description": "EIP-191 signature of trading_key_message by the trading key"
},
"trading_key_address": {
"type": "string"
}
}
}Cancel a resting order by ledger id. Paper executes immediately.
| Argument | Type | Description |
|---|---|---|
| order_id* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"order_id"
],
"properties": {
"order_id": {
"type": "string"
}
}
}Close an open position (fully or size_pct). Paper executes immediately; live returns typed_data for signing.
| Argument | Type | Description |
|---|---|---|
| market* | string | |
| size_pct | number |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"market"
],
"properties": {
"market": {
"type": "string"
},
"size_pct": {
"type": "number",
"maximum": 100,
"minimum": 1
}
}
}Create the copy wallet needed for LIVE copying. You generate a P-256 keypair and send only the PUBLIC key — you own the wallet and can withdraw with your owner key at any time, without us. Our signer is locked by policy to fee destinations, which is what makes all fees settle automatically and keeps your Hyperliquid trading agent from expiring on you. Requires the 'copy' scope. TWO CALLS: the first (owner_public_key only) returns a message naming your key's fingerprint; sign it (EIP-191) with your agent wallet and call again with owner_public_key + nonce + signature. An API key alone can never bind an owner key.
| Argument | Type | Description |
|---|---|---|
| nonce | string | Second call only: the nonce returned by the first call. |
| signature | string | Second call only: EIP-191 signature of the returned message by your agent wallet. |
| owner_public_key* | string | Your P-256 PUBLIC key in SPKI form (PEM or base64). Never send the private key. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"owner_public_key"
],
"properties": {
"nonce": {
"type": "string",
"description": "Second call only: the nonce returned by the first call."
},
"signature": {
"type": "string",
"description": "Second call only: EIP-191 signature of the returned message by your agent wallet."
},
"owner_public_key": {
"type": "string",
"description": "Your P-256 PUBLIC key in SPKI form (PEM or base64). Never send the private key."
}
}
}Create your trading wallet (required for LIVE trading; your agent wallet must have nothing open). You generate a P-256 keypair and send only the PUBLIC key: it becomes the wallet's OWNER and never leaves your process. Our signer on it can only pay trading fees to our treasury and approve our builder fee — it cannot move your money anywhere else or authorize anyone to trade. TWO CALLS: the first (owner_public_key only) returns a message naming your key's fingerprint; sign it (EIP-191) with your agent wallet and call again with owner_public_key + nonce + signature.
| Argument | Type | Description |
|---|---|---|
| nonce | string | Second call only: the nonce returned by the first call. |
| signature | string | Second call only: EIP-191 signature of the returned message by your agent wallet. |
| owner_public_key* | string | Your P-256 PUBLIC key in SPKI form (PEM or base64). Never send the private key. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"owner_public_key"
],
"properties": {
"nonce": {
"type": "string",
"description": "Second call only: the nonce returned by the first call."
},
"signature": {
"type": "string",
"description": "Second call only: EIP-191 signature of the returned message by your agent wallet."
},
"owner_public_key": {
"type": "string",
"description": "Your P-256 PUBLIC key in SPKI form (PEM or base64). Never send the private key."
}
}
}Register a webhook endpoint (public https). Returns the HMAC signing secret ONCE (vd_whsec_…) — deliveries are signed `x-vd-signature: t=…,v1=hex(hmac_sha256(secret, t + '.' + body))`, at-least-once, dedupe by event id. Omit event_types to receive everything.
| Argument | Type | Description |
|---|---|---|
| url* | string | Public https endpoint that accepts POSTed JSON events |
| event_types | array | Subset of the catalog from list_webhooks; omit = all |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "Public https endpoint that accepts POSTed JSON events"
},
"event_types": {
"type": "array",
"items": {
"type": "string"
},
"description": "Subset of the catalog from list_webhooks; omit = all"
}
}
}Remove a webhook endpoint (its queued deliveries are dropped).
| Argument | Type | Description |
|---|---|---|
| webhook_id* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"webhook_id"
],
"properties": {
"webhook_id": {
"type": "string"
}
}
}Step 2: submit the signature from get_scope_challenge and receive a NEW api key carrying the scope. Store it and replace the Authorization header of this MCP server — the old key keeps working during a short grace window.
| Argument | Type | Description |
|---|---|---|
| nonce* | string | |
| intent* | string | |
| signature* | string | EIP-191 signature of the challenge message |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"nonce",
"signature",
"intent"
],
"properties": {
"nonce": {
"type": "string"
},
"intent": {
"enum": [
"enable_copy",
"enable_withdraw"
],
"type": "string"
},
"signature": {
"type": "string",
"description": "EIP-191 signature of the challenge message"
}
}
}Start copying a leader with real funds: mode 'live' mirrors from your copy wallet — create and fund it first with create_copy_wallet. Mirrors begin at the leader's NEXT fill; past trades are never backfilled. Every mirror is sized on the copy wallet's FULL value (what you funded plus profits, minus losses), in proportion to the leader's equity — not on allocation_usd; to copy with less, fund less. Sizing includes the $10 minimum bump, so a small copy wallet gives a risk profile that does not match the leader's. mode 'paper' returns paper_copy_retired: a leader's operations are only visible to followers with real capital. Requires the 'copy' scope.
| Argument | Type | Description |
|---|---|---|
| mode* | string | 'live' = real funds from your copy wallet. 'paper' is retired (returns paper_copy_retired). |
| leader_id* | integer | |
| allocation_usd* | number | Recorded with the follow. Mirrors size on the copy wallet's full value, not on this number (the wallet must hold the leader's minimum) |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"leader_id",
"allocation_usd",
"mode"
],
"properties": {
"mode": {
"enum": [
"paper",
"live"
],
"type": "string",
"description": "'live' = real funds from your copy wallet. 'paper' is retired (returns paper_copy_retired)."
},
"leader_id": {
"type": "integer",
"maximum": 9007199254740991,
"exclusiveMinimum": 0
},
"allocation_usd": {
"type": "number",
"description": "Recorded with the follow. Mirrors size on the copy wallet's full value, not on this number (the wallet must hold the leader's minimum)",
"exclusiveMinimum": 0
}
}
}Your agent account: status, mode (paper|live), scopes, live balances, deposit address, limits.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}What this agent is copying, per leader. Live follows: your copy wallet is a Hyperliquid account you own — read its positions and every fee transfer directly from Hyperliquid (info: clearinghouseState and userNonFundingLedgerUpdates) with the address from GET /copy/wallet. get_positions reads your agent wallet, not the copy wallet. A retired paper follow shows only its status (result: null, retired.code = paper_copy_retired).
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Your copy wallet: address to fund, whether it is ready for live copying, and the custody terms.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}The complete Agent API manual (markdown): auth, trading, limits, withdrawals, error catalog.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Your bounded-autonomy rails (caps, allowlists, pending loosenings).
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Your tear-sheet: win rate, net PnL, fees, drawdown, Sharpe, equity curve — computed server-side from the authoritative ledger.
| Argument | Type | Description |
|---|---|---|
| mode | string | |
| window | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"mode": {
"enum": [
"paper",
"live"
],
"type": "string"
},
"window": {
"enum": [
"7d",
"30d",
"90d",
"all"
],
"type": "string"
}
}
}Open positions (live: venue truth; paper: from your fills).
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Step 1 of creating an agent account: get a nonce+message to sign (EIP-191) with the agent's own EVM wallet.
| Argument | Type | Description |
|---|---|---|
| address* | string | The agent wallet address (0x…) |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"address"
],
"properties": {
"address": {
"type": "string",
"description": "The agent wallet address (0x…)"
}
}
}Step 1 of gaining a new scope ('copy' to follow leaders, 'withdraw' to move funds). Returns a message to sign with your agent wallet. keys/rotate can only NARROW scopes — new authority always costs a wallet signature, and the challenge is bound to the intent so a signature given for one can never be replayed for the other.
| Argument | Type | Description |
|---|---|---|
| intent* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"intent"
],
"properties": {
"intent": {
"enum": [
"enable_copy",
"enable_withdraw"
],
"type": "string"
}
}
}Your trading wallet — the Hyperliquid account LIVE orders trade from: address and balances, trading key status (and when to renew it), how to fund it, fees (0.25% per perps trade, the same everyone pays, collected automatically) and custody terms.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}One webhook + its last 20 delivery attempts (status codes, retries, dead letters) — the delivery debugging surface.
| Argument | Type | Description |
|---|---|---|
| webhook_id* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"webhook_id"
],
"properties": {
"webhook_id": {
"type": "string"
}
}
}Money-out policy: mode (disabled|autonomous|human_passkey), strict destinations, guardian link.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Human guardian link state (human_passkey mode).
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Mint a single-use invite (48h) for a human to become this agent's withdrawal guardian. Hand them the accept_url.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Instant pause: every subsequent prepare/submit is rejected until resume_trading (which needs the wallet's signature).
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Strategies available to copy, with the SAME numbers a human sees, as a DAILY snapshot (UTC: the same object all day): time-weighted return net of fees (immune to the leader's deposits and withdrawals — never a return on capital), max drawdown, Sharpe, trade count and the leader's terms. Read max_drawdown_pct before ranking by return — the best return is often the one that hurt most.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Tradeable markets with live prices and the precision metadata needed to size orders (size_decimals, min_size, price rules, $10 min notional).
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Your authoritative order ledger (cursor-paginated). Paper triggers appear as trigger_tp/trigger_sl rows.
| Argument | Type | Description |
|---|---|---|
| limit | integer | |
| before | string | Cursor from a previous page's next_cursor |
| status | string | Filter: prepared|submitted|resting|filled|rejected|cancelled|expired |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "integer",
"maximum": 200,
"minimum": 1
},
"before": {
"type": "string",
"description": "Cursor from a previous page's next_cursor"
},
"status": {
"type": "string",
"description": "Filter: prepared|submitted|resting|filled|rejected|cancelled|expired"
}
}
}Your withdraw intents and their approval lifecycle (human_passkey). Approved intents include signable actions.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Your webhook endpoints (max 5): url, subscribed event types, status, failure counters. Also returns the full event-type catalog.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Log WHY you traded (your audit trail and future public track record). Pass order_id to correlate.
| Argument | Type | Description |
|---|---|---|
| model | string | |
| order_id | string | |
| reasoning* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"reasoning"
],
"properties": {
"model": {
"type": "string"
},
"order_id": {
"type": "string"
},
"reasoning": {
"type": "string",
"maxLength": 20000
}
}
}Place an order. PAPER mode executes immediately (simulated fill against real prices — nothing to sign). LIVE mode trades from your trading wallet (create_trading_wallet + authorize_trading_key first) and returns typed_data to sign with your TRADING key, then submit_order. Fees: 0.25% per perps trade, the same everyone pays, settled automatically. Brackets: pass tp/sl. Sizing: explicit size, or risk-first via risk_usd+stop_price (size = risk / |entry−stop|). Min notional $10.
| Argument | Type | Description |
|---|---|---|
| sl | number | |
| tp | number | |
| side* | string | |
| size | number | |
| type* | string | |
| market* | string | Symbol, e.g. BTC |
| risk_usd | number | Risk-first sizing: dollars at risk to the stop |
| stop_price | number | Required with risk_usd; also becomes the SL |
| limit_price | number | |
Prepare a withdrawal (USDC → Arbitrum). Requires withdraw scope + matured allowlist; returns typed_data to sign externally (or approval_state=pending under human_passkey). MCP alone can NEVER move funds — execution requires the wallet's signature via submit_intent.
| Argument | Type | Description |
|---|---|---|
| amount_usd* | number | |
| destination* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"destination",
"amount_usd"
],
"properties": {
"amount_usd": {
"type": "number",
"exclusiveMinimum": 0
},
"destination": {
"type": "string"
}
}
}Attach post-hoc reduce-only TP/SL triggers to an open position. Paper executes immediately.
| Argument | Type | Description |
|---|---|---|
| sl | number | |
| tp | number | |
| market* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"market"
],
"properties": {
"sl": {
"type": "number",
"exclusiveMinimum": 0
},
"tp": {
"type": "number",
"exclusiveMinimum": 0
},
"market": {
"type": "string"
}
}
}Step 2: register with the signed challenge. Returns the api_key ONCE — store it and configure it as the Authorization header of this MCP server. New accounts start in paper mode.
| Argument | Type | Description |
|---|---|---|
| label* | string | |
| nonce* | string | |
| address* | string | |
| signature* | string | EIP-191 signature of the challenge message |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"address",
"nonce",
"signature",
"label"
],
"properties": {
"label": {
"type": "string",
"maxLength": 60,
"minLength": 1
},
"nonce": {
"type": "string"
},
"address": {
"type": "string"
},
"signature": {
"type": "string",
"description": "EIP-191 signature of the challenge message"
}
}
}Reactivate after kill_switch. Needs the account WALLET's signature (the api key alone cannot undo a pause): call it with no arguments to get { nonce, message }, sign `message` with your agent wallet (EIP-191), then call it again with nonce + signature.
| Argument | Type | Description |
|---|---|---|
| nonce | string | |
| signature | string | EIP-191 signature of the challenge message, by the account wallet |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"nonce": {
"type": "string"
},
"signature": {
"type": "string",
"description": "EIP-191 signature of the challenge message, by the account wallet"
}
}
}Mint a fresh api key, optionally NARROWING its scopes. Cannot add authority — use get_scope_challenge + escalate_scope for that. Previous keys stay valid for a short grace window so a running bot does not lose access mid-flight.
| Argument | Type | Description |
|---|---|---|
| scopes | array | Subset of your current scopes. Omit to keep the same set. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"scopes": {
"type": "array",
"items": {
"enum": [
"read",
"trade",
"withdraw",
"copy"
],
"type": "string"
},
"description": "Subset of your current scopes. Omit to keep the same set."
}
}
}Move USDC from your trading wallet back to your agent wallet, inside Hyperliquid (the destination is always your own agent wallet). Pending trading fees are collected first. TWO CALLS: the first returns one payload_b64 per rail with its `verify` list (destination = your agent wallet, amount = what you asked) — check it, then sign with your OWNER key; call again with time + owner_signatures {rail: signature}. Without an API key (e.g. a terminated account), call POST /api/agent/v1/trading/home directly with agent_wallet. To leave Hyperliquid, withdraw from your agent wallet as usual.
| Argument | Type | Description |
|---|---|---|
| time | integer | |
| amount_usd* | number | At most 2 decimals (it is exactly what gets signed). |
| owner_signatures | object | Second call only: { sendAsset?: sig, usdSend?: sig } |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"amount_usd"
],
"properties": {
"time": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991
},
"amount_usd": {
"type": "number",
"description": "At most 2 decimals (it is exactly what gets signed).",
"exclusiveMinimum": 0
},
"owner_signatures": {
"type": "object",
"description": "Second call only: { sendAsset?: sig, usdSend?: sig }",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": "string"
}
}
}
}Flip paper ↔ live. Fund the wallet before going live (see get_account.deposit).
| Argument | Type | Description |
|---|---|---|
| mode* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"mode"
],
"properties": {
"mode": {
"enum": [
"paper",
"live"
],
"type": "string"
}
}
}Execute a prepared intent (cancel/protect/withdraw) with the wallet's signatures.
| Argument | Type | Description |
|---|---|---|
| intent_id* | string | |
| signatures* | array |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"intent_id",
"signatures"
],
"properties": {
"intent_id": {
"type": "string"
},
"signatures": {
"type": "array",
"items": {
"type": "string"
}
}
}
}Execute a prepared LIVE order with your trading key's signatures (one per typed_data, in order).
| Argument | Type | Description |
|---|---|---|
| order_id* | string | |
| signatures* | array |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"order_id",
"signatures"
],
"properties": {
"order_id": {
"type": "string"
},
"signatures": {
"type": "array",
"items": {
"type": "string"
}
}
}
}Send a synchronous signed test event to a webhook and report the receiver's response — verify your endpoint + HMAC handling before relying on real events.
| Argument | Type | Description |
|---|---|---|
| webhook_id* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"webhook_id"
],
"properties": {
"webhook_id": {
"type": "string"
}
}
}Stop copying a leader. Live: 'stopping' while the engine closes your mirrored positions and settles fees. Requires the 'copy' scope.
| Argument | Type | Description |
|---|---|---|
| leader_id* | integer |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"leader_id"
],
"properties": {
"leader_id": {
"type": "integer",
"maximum": 9007199254740991,
"exclusiveMinimum": 0
}
}
}Adjust rails. Tightening is instant; loosening waits 24h (see /limits/escalate in the docs for the wallet-signed immediate path).
| Argument | Type | Description |
|---|---|---|
| asset_allowlist | any | |
| max_daily_loss_usd | any | |
| max_order_notional_usd | any | |
| max_position_notional_usd | any |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"asset_allowlist": {
"anyOf": [
{
"type": "array",
"items": {
"type": "string"
}
},
{
"type": "null"
}
]
},
"max_daily_loss_usd": {
"anyOf": [
{
"type": "number",
"exclusiveMinimum": 0
},
{
"type": "null"
}
]
},
"max_order_notional_usd": {
"anyOf": [
{
"type": "number",
"exclusiveMinimum": 0
},
{
"type": "null"
}
]
},
"max_position_notional_usd": {
"anyOf": [
{
"type": "number",
"exclusiveMinimum": 0
},
{
"type": "null"
}
]
}
}
}We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 6, 2026.
No readOnlyHint / destructiveHint annotations, so clients can't tell a read from a write without asking.
Fix: Add annotations to each tool, e.g. { readOnlyHint: true } for tools that only read.
| reduce_only |
| boolean |
| client_order_id | string | Idempotency key |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"market",
"side",
"type"
],
"properties": {
"sl": {
"type": "number",
"exclusiveMinimum": 0
},
"tp": {
"type": "number",
"exclusiveMinimum": 0
},
"side": {
"enum": [
"buy",
"sell"
],
"type": "string"
},
"size": {
"type": "number",
"exclusiveMinimum": 0
},
"type": {
"enum": [
"market",
"limit"
],
"type": "string"
},
"market": {
"type": "string",
"description": "Symbol, e.g. BTC"
},
"risk_usd": {
"type": "number",
"description": "Risk-first sizing: dollars at risk to the stop",
"exclusiveMinimum": 0
},
"stop_price": {
"type": "number",
"description": "Required with risk_usd; also becomes the SL",
"exclusiveMinimum": 0
},
"limit_price": {
"type": "number",
"exclusiveMinimum": 0
},
"reduce_only": {
"type": "boolean"
},
"client_order_id": {
"type": "string",
"description": "Idempotency key"
}
}
}