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 CDPrequireFinite()— 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)