Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/theonetrade/ai-trading-mcp/llms.txt

Use this file to discover all available pages before exploring further.

The agent’s entire trading vocabulary is three tools. There is no fourth tool to add leverage, change position size, set a custom stop-loss, or withdraw funds — the engine does not expose those surfaces. get_status gives the model its sensory feed; open_position and close_position let it act. Every other constraint — which symbols are tradable, whether an entry is already open, what the TP/SL levels are — is enforced engine-side, not by prompt engineering.

Tool Overview

ToolArgumentsWhat the agent gets
get_statusOne message per traded symbol: current price, invested balance, queued entry, active position with unrealized PnL, queued close — plus the last 15 Telegram posts as text and image blocks
open_positionsymbol, position (long | short), noteMarket entry with engine-computed TP/SL/cost. Rejected if the symbol is not live-enabled or already holds a position or pending signal
close_positionsymbol, noteQueues a market close. Fails if there is nothing to close
Nothing else is exposed. An open against a busy symbol is rejected by the engine, not by prompt engineering.

get_status

get_status takes no arguments. Each call returns a composed set of MCP messages:
  1. Engine portfolio block — one text message per traded symbol showing the current price, invested balance, any queued entry, any active position with unrealized PnL, and any queued close.
  2. Command history block — recent open_position / close_position calls and their outcomes, so the model has a running log of its own actions.
  3. Telegram feed block — the last 15 posts from the configured channel, newest first, each stamped with an ISO timestamp, with chart screenshots as MCP image blocks.

Concurrency guard

Calls to get_status are wrapped in queued() from functools-kit. If two calls arrive while one is already in flight, they line up — they do not stampede the Telegram client or the engine. The composition runs strictly serially.

Timeout

The Telegram fetch inside each call is wrapped in timeout(90_000). If the GramJS client does not respond within 90 seconds, the TIMEOUT_SYMBOL sentinel appears in the message array. The controller detects it, disconnects the Telegram client, clears the singleton (getTelegram.clear()), and throws a clean error:
const FEED_FETCH_TIMEOUT = 90_000;

const FETCH_TELEGRAM_HISTORY_FN = timeout(
  async (self: StatusControllerService, when: Date): Promise<IMCPMessage[]> => {
    // ... fetch and assemble messages ...
  },
  FEED_FETCH_TIMEOUT,
);
The next get_status call reconnects fresh. The agent receives a typed error string rather than a hanging tool call.
The 90-second timeout is on the Telegram fetch only. The engine portfolio and command history blocks are assembled before the feed fetch and are not affected by a Telegram timeout.

open_position

symbol
string
required
The trading pair to enter, e.g. BTCUSDT. Must be present in the CC_SYMBOL_LIST whitelist (13 pairs by default). Symbols outside the whitelist cannot be traded regardless of what the feed says.
position
'long' | 'short'
required
Direction of the trade. long for a buy entry, short for a sell entry.
note
string
required
Free-text rationale recorded by the engine and echoed back in every subsequent get_status for that position. The agent should record its basis here — the Telegram signal, timestamp, and any caveats.
The engine computes take-profit, stop-loss, and entry cost autonomously. The model cannot override any of these values — they are not parameters.

Rejection conditions

The engine rejects open_position at the validation layer (not at the prompt layer) if any of the following are true:
  • The symbol is not in the live-enabled whitelist (CC_SYMBOL_LIST).
  • The symbol already has an active position open.
  • The symbol already has a pending signal (an entry that has been queued but not yet filled).
In all three cases the HTTP envelope returns { "error": "engine message" } and the stdio server relays it as an isError: true tool result. The model sees the exact engine message.

close_position

symbol
string
required
The trading pair to close, e.g. BTCUSDT. Must match an active position or queued entry.
note
string
required
Free-text rationale for the close. Recorded in the engine and included in the audit trail.
close_position queues a market close against the named symbol. The engine handles order placement, bracket cancellation, and verified settlement — the model only names the symbol and records the basis.
If there is no active position and no queued entry for the named symbol, the engine rejects the call and the agent receives an isError: true result with the engine’s exact message.

Error Handling

Transport success ≠ operation success

An HTTP 200 from the bridge means the trading process received the request and processed it. It does not mean the operation succeeded. Every response from the bridge carries an envelope:
{ "error": "" }
or
{ "error": "Symbol XYZUSDT is not in the live-enabled list" }
The stdio MCP server inspects the error field. An empty string → success result. A non-empty string → isError: true tool result with the error as the content.

What the agent sees

The agent receives the engine’s exact rejection message as an MCP tool error. Because it is a typed error (not a network failure or a hang), the model can read it, reason about it, and decide what to do — try a different symbol, report the rejection, or wait for the position to clear.
Tool result (isError: true):
"Symbol BTCUSDT already holds an active position (short)"
The isError mechanism is the only way the engine communicates rejections to the model. There is no secondary channel, no log tail, and no prompt-injected error handling. The agent must read the tool result.

Build docs developers (and LLMs) love