Skip to content

Tool Design Principles

1. Compact Output by Default

Every tool returns the smallest useful response. Large payloads are opt-in.

Pattern Example
Summary mode default data_get_ohlcv returns stats (~500B) not bars (~8KB)
Label caps data_get_pine_labels returns max 50 per study
Verbose flag data_get_pine_lines hides IDs/colors unless verbose=true
Path-only returns capture_screenshot returns file path (~300B) not image data

Why: LLM context is expensive. A typical "analyze chart" workflow should be 5-10KB, not 80KB.

2. Zod Schema Validation

Every tool parameter has a Zod schema with .describe() annotations. This gives Claude clear guidance on what each parameter expects.

server.tool('chart_set_symbol', 'Change the chart symbol', {
  symbol: z.string().describe('Symbol (e.g., BTCUSD, AAPL, ES1!, NYMEX:CL1!)'),
}, async ({ symbol }) => { ... });

3. Input Sanitization

  • safeString() — JSON.stringify escaping prevents code injection via CDP
  • requireFinite() — Blocks NaN/Infinity from reaching TradingView API (prevents corrupting cloud state)

4. Idempotent Where Possible

Tools like chart_set_symbol or chart_set_timeframe are idempotent — calling them with the current value is a no-op. This makes retry-on-failure safe.

5. CLI Parity

Every MCP tool has a corresponding CLI command. Output is always JSON for piping with jq.

tv chart_get_state                    # Same as MCP chart_get_state
tv data_get_ohlcv --summary           # Same as MCP data_get_ohlcv {summary: true}

6. Error Messages Are Actionable

Errors include what went wrong AND what to do about it: - "No CDP connection — is TradingView running with --remote-debugging-port=9222?" - "Indicator not found — use chart_get_state to see available indicator IDs"

7. One Tool, One Job

Tools do one thing well. Complex workflows are composed by calling multiple tools: - "Scan watchlist" = watchlist_get + loop(chart_set_symbol + data_get_pine_labels) - "Backtest setup" = replay_start + loop(replay_step + data_get_study_values)