Connection and Architecture Internals¶
This page documents the internal architecture of tradingview-mcp: how it connects to TradingView Desktop, manages retries, validates input, waits for chart readiness, and formats MCP responses.
CDP Connection Management¶
File: src/connection.js
The connection layer manages a lazy singleton CDP (Chrome DevTools Protocol) client that connects to TradingView Desktop on localhost:9222.
Connection Lifecycle¶
getClient()
|
v
client exists? --yes--> liveness check (evaluate '1')
| |
no passes --> return client
| |
v fails --> set client=null, reconnect
connect()
|
v
findChartTarget() --> scan /json/list for TradingView chart pages
|
v
CDP({ host, port, target }) --> enable Runtime, Page, DOM
|
v
return client
Key Behaviors¶
- Lazy singleton: The client is created on first use and reused across all tool calls. No upfront connection is required.
- Liveness check: Before reusing an existing client,
getClient()evaluates'1'viaRuntime.evaluate. If this fails (connection dropped, TradingView restarted), the client is discarded and a fresh connection is established. - Target discovery:
findChartTarget()fetcheshttp://localhost:9222/json/listand finds the first page target whose URL matches/tradingview\.com\/chart/i. Falls back to any target matching/tradingview/i. - Domain enablement: After connecting, the client enables
Runtime,Page, andDOMdomains.
Retry Logic¶
Connection attempts use exponential backoff:
| Attempt | Delay |
|---|---|
| 0 | 500ms |
| 1 | 1,000ms |
| 2 | 2,000ms |
| 3 | 4,000ms |
| 4 | 8,000ms |
- Max retries: 5
- Base delay: 500ms
- Max delay cap: 30,000ms (30 seconds)
- Formula:
min(500 * 2^attempt, 30000)
After 5 failed attempts, a CDP connection failed error is thrown.
KNOWN_PATHS -- API Surface Map¶
The KNOWN_PATHS object documents the discovered internal API paths within TradingView's page context. These paths were found via live probing and are used throughout the codebase.
| Path Key | Expression | Purpose |
|---|---|---|
chartApi |
window.TradingViewApi._activeChartWidgetWV.value() |
Active chart widget -- primary entry point for chart operations |
chartWidgetCollection |
window.TradingViewApi._chartWidgetCollection |
Collection of all chart widgets (multi-pane layouts) |
bottomWidgetBar |
window.TradingView.bottomWidgetBar |
Bottom panel bar (Pine editor, strategy tester) |
replayApi |
window.TradingViewApi._replayApi |
Replay mode controls |
alertService |
window.TradingViewApi._alertService |
Alert management |
chartApiInstance |
window.ChartApiInstance |
Alternative chart API reference |
mainSeriesBars |
...value()._chartWidget.model().mainSeries().bars() |
Direct access to OHLCV bar data |
strategyStudy |
chart._chartWidget.model().model().dataSources() |
Strategy data sources (performance, orders, reports) |
layoutManager |
window.TradingViewApi.getSavedCharts |
Saved chart layout management |
symbolSearchApi |
window.TradingViewApi.searchSymbols |
Symbol search (returns Promise) |
pineFacadeApi |
https://pine-facade.tradingview.com/pine-facade |
Pine Script REST API for saved scripts |
Helper functions verify path availability before use:
getChartApi() // verifies chartApi exists, returns the path string
getChartCollection() // verifies chartWidgetCollection
getBottomBar() // verifies bottomWidgetBar
getReplayApi() // verifies replayApi
getMainSeriesBars() // verifies mainSeriesBars
Each helper calls verifyAndReturn(path, name) which evaluates typeof (path) !== 'undefined' && (path) !== null before returning the path string.
Safety Functions¶
safeString(str)¶
Sanitizes a string for safe interpolation into JavaScript code evaluated via CDP. Uses JSON.stringify() to produce a properly escaped JS string literal (including quotes). This prevents injection via quotes, backticks, template literals, or control characters.
Every tool that interpolates user-provided strings into CDP expressions uses safeString().
requireFinite(value, name)¶
Validates that a value is a finite number. Throws if NaN, Infinity, or non-numeric. This prevents corrupt values from reaching TradingView APIs that persist state to the cloud.
requireFinite(42, "price") // returns 42
requireFinite("abc", "price") // throws: "price must be a finite number, got: abc"
requireFinite(Infinity, "price") // throws
JavaScript Execution¶
evaluate(expression, opts)¶
Executes a JavaScript expression in TradingView's page context via Runtime.evaluate. Returns the result value. If the evaluation throws, the error is extracted from exceptionDetails and re-thrown as a JavaScript Error.
Options:
returnByValue: true(default) -- serialize the result to JSONawaitPromise: false(default) -- do not await Promises
evaluateAsync(expression)¶
Convenience wrapper that calls evaluate() with awaitPromise: true. Used for expressions that return a Promise (e.g., setSymbol(), searchSymbols()).
Chart Readiness Polling¶
File: src/wait.js
The waitForChartReady() function polls TradingView's DOM to determine when a chart has finished loading after a symbol or timeframe change.
Detection Strategy¶
The function polls at 200ms intervals (configurable timeout, default 10 seconds) and checks three conditions:
-
Spinner detection: Looks for loading indicators via selectors
[class*="loader"],[class*="loading"], and[data-name="loading"]. If a spinner is visible (offsetParent !== null), the chart is not ready. -
Symbol matching: If an
expectedSymbolis provided, the function reads the current symbol from the chart legend ([data-name="legend-source-title"]) and checks for a case-insensitive match. Mismatches reset the stability counter. -
Bar count stability: The function counts chart bar elements and tracks whether the count remains stable across consecutive polls. The chart is considered ready when the bar count has been stable for 2 consecutive checks (approximately 400ms of stability).
Return Values¶
- Returns
truewhen all conditions pass (spinner gone, symbol matches, bars stable) - Returns
falseon timeout -- the caller should still proceed but may want to verify
Parameters¶
| Parameter | Default | Description |
|---|---|---|
expectedSymbol |
null |
Expected symbol to match (case-insensitive substring) |
expectedTf |
null |
Reserved for future timeframe matching |
timeout |
10000 |
Maximum wait time in milliseconds |
MCP Response Formatting¶
File: src/tools/_format.js
The jsonResult(obj, isError) function standardizes MCP tool responses into the format expected by the MCP protocol.
jsonResult({ symbol: "ES1!", price: 5200 })
// {
// content: [{ type: "text", text: '{\n "symbol": "ES1!",\n "price": 5200\n}' }]
// }
jsonResult({ error: "Not connected" }, true)
// {
// content: [{ type: "text", text: '{\n "error": "Not connected"\n}' }],
// isError: true
// }
All tool handler files import jsonResult from _format.js to ensure consistent response structure. The MCP protocol expects responses as content arrays with typed entries. The isError flag signals tool failure to the MCP client.
Architecture Diagram¶
Claude Code / MCP Client
|
| stdio (JSON-RPC)
v
MCP Server (src/server.js)
|
| imports tool handlers
v
Tool Layer (src/tools/*.js)
|
| calls core logic
v
Core Layer (src/core/*.js)
|
| evaluate() / evaluateAsync()
v
Connection Layer (src/connection.js)
|
| CDP over HTTP/WebSocket
v
TradingView Desktop (Electron)
|
| localhost:9222
v
Chrome DevTools Protocol
The CLI (src/cli/) provides an alternative entry point that bypasses the MCP server and calls the core layer directly, outputting JSON to stdout instead of MCP-formatted responses.