The trading rig’s sensory input is a live Telegram channel piped directly into everyDocumentation 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.
get_status response. A GramJS user session — not a bot token — reads the channel as a regular account would, capturing both text posts and chart screenshot photos. The last 15 posts arrive in the model’s context as MCP text blocks (with ISO timestamps) and MCP image blocks (base64-encoded JPEG thumbnails). The feed is the only external signal source; the engine exposes no market-data endpoint to the agent.
How the Scraper Works
ScraperService connects to Telegram via a QR-authorized GramJS user session. Because it authenticates as a user account rather than a bot, it can read any channel the account has joined — including channels that restrict bots.
scrapeLast — the method that powers get_status
dto.when, skipping messages that have neither text nor a photo, and collects up to dto.limit results using pickDocuments from functools-kit. Each result conforms to the ScraperMessage model:
StatusControllerService calls scrapeLast with limit: 15 and offset: 0, asking for the last 15 posts, newest first. Each post is emitted as a text block carrying the ISO timestamp and caption. Photo-only posts (where content is an empty string) are described as "(photo post, image attached below)" so the model never has to guess whether an image has associated text:
The
CC_TELEGRAM_CHANNEL environment variable selects the channel to scrape. The default is -1002833393903 — a demo signals channel used for testing. Set your own channel ID before going live.Image Handling
Telegram stores each photo at multiple resolutions: 320 px, 800 px, 1280 px, and 2560 px wide. At 320 px, card text is illegible. At 1280 px and above, the download is wasted weight for a model that only needs to read a chart. The scraper targets 800 px as the balance point.The sizing constants and selection logic
null.
Download with fallback
downloadMedia with the selected thumbnail fails, the scraper retries with a full-size download. The result is always a Buffer that ScraperMessage.photo stores as a base64 string.
Delivery to the model
Each photo is pushed into the message list as an MCP image block immediately after its text block:Concurrency and Timeout Guards
Two guards protect the GramJS client from being overwhelmed or left hanging.timeout(90_000) — FEED_FETCH_TIMEOUT
The entire Telegram fetch is wrapped in a 90-second timeout:
timeout() injects TIMEOUT_SYMBOL into the message array. GET_STATUS_FN checks for its presence after all three message sources have been awaited.
queued() — serial execution
The full get_status composition is wrapped in queued() so concurrent calls line up rather than stampeding the client:
Timeout recovery
WhenTIMEOUT_SYMBOL is detected, RESTART_TELEGRAM_FN disconnects the client and clears the singleton so the next call gets a fresh connection:
isError tool result — "StatusControllerService.getStatus: timeout fetching feed messages" — rather than a hanging bridge call.
Feed Registration
The entire integration surface is one schema registration inpackages/agent/src/config/setup.ts:
addMCPSchema registers the manual_mcp renderer with the engine. When the MCP bridge receives a get_status call, the engine invokes getMessages, which delegates to StatusControllerService.getStatus — the queued + timeout-guarded composition described above.
What the Model Receives
Eachget_status call delivers the Telegram section as a sequence of interleaved text and image blocks. From a real dump:
- A text block with an ISO 8601 timestamp and the post’s text content. Photo-only posts read
(photo post, image attached below). - Optionally, an image block immediately following the text block — a base64 JPEG delivered as an MCP image content block, visible to the model exactly like any other image in its context.
The feed is treated as untrusted input by design. Instructions embedded in channel posts are data, not commands — the agent’s vocabulary (three tools) and the engine’s validation chain bound the blast radius of any single post. Every
get_status response, including all image blocks, is dumped to dump/mcp/<timestamp>.md and dump/images/ before it leaves the process, creating a complete audit trail of what the model saw.