Complete guide for developing, testing, and extending the TradingView MCP Server locally.
- Local Setup
- Using Local MCP Server with Claude
- Development Workflow
- Testing
- Project Structure
- Adding New Features
- Environment Variables
- Debugging
- Node.js 18 or higher
- npm or yarn
- Git
# Clone the repository
git clone https://github.com/fiale-plus/tradingview-mcp-server.git
cd tradingview-mcp-server
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Watch tests (development mode)
npm run test:watchAfter npm run build, compiled files are in /dist:
dist/
├── index.js # Main entry point
├── api/ # TradingView API client
├── tools/ # MCP tools (screen, fields)
├── resources/ # Preset configurations
└── utils/ # Cache, rate limiting
For rapid development in this repository, use the included example configuration files:
# 1. Copy example MCP config for development (uses local source with tsx)
cp .mcp.json.example .mcp.json
# For development, modify .mcp.json to use local source:
# Change: "args": ["-y", "tradingview-mcp-server"]
# To: "args": ["tsx", "src/index.ts"]
# 2. Copy example Claude settings
cp .claude/settings.json.example .claude/settings.local.json
# 3. Restart Claude Code to load the MCP serverImportant distinctions:
- Cloned repo config (
.mcp.json.example): Usesnpx -y tradingview-mcp-server→ pulls published npm package - Development config (
.mcp.jsonwith tsx): Usesnpx tsx src/index.ts→ runs local source code directly - Both
.mcp.jsonand.claude/settings.local.jsonare gitignored to avoid committing your local setup
Development advantages with tsx:
- ✅ No build step required - edit and restart
- ✅ Faster iteration cycle
- ✅ TypeScript runs directly from
src/ - ✅ Easier debugging with source maps
To test your local development build with Claude Desktop or Claude Code, you have two options.
Project-level MCP servers are isolated to specific directories and won't affect your global Claude configuration.
- Create
.mcp.jsonin your project root:
{
"mcpServers": {
"tradingview-local": {
"command": "node",
"args": ["/absolute/path/to/tradingview-mcp-server/dist/index.js"],
"env": {
"CACHE_TTL_SECONDS": "300",
"RATE_LIMIT_RPM": "10"
}
}
}
}Important: Replace /absolute/path/to/tradingview-mcp-server with your actual repository path.
- Enable in
.claude/settings.local.json:
{
"enableAllProjectMcpServers": true
}- Restart Claude Code to load the MCP server
- ✅ Isolated from global config
- ✅ Project-specific settings
- ✅ Easy to switch between versions
- ✅ No impact on other projects
Global configuration makes the MCP server available in all Claude Desktop sessions.
-
Locate your Claude Desktop config file:
- Mac:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
- Mac:
-
Add the local MCP server:
{
"mcpServers": {
"tradingview-local": {
"command": "node",
"args": ["/absolute/path/to/tradingview-mcp-server/dist/index.js"],
"env": {
"CACHE_TTL_SECONDS": "300",
"RATE_LIMIT_RPM": "10"
}
}
}
}- Restart Claude Desktop
- ✅ Available in all Claude Desktop sessions
- ✅ Easier for testing across multiple projects
⚠️ Affects global Claude configuration⚠️ May conflict with published npm version
-
Make changes to source files in
src/# Edit files in src/ vim src/tools/fields.ts -
Build the project
npm run build
-
Restart Claude (Desktop or Code) to load new build
- The MCP server runs as a separate process
- Changes require a restart to take effect
- No hot-reload available
-
Test via Claude's MCP integration
List all available fields for stock screening -
Iterate - repeat steps 1-4 as needed
# Build and signal to restart
npm run build && echo "✓ Build complete - restart Claude to load changes"# Run all tests once
npm test
# Watch mode (re-runs on file changes)
npm run test:watch
# Run specific test file
npm test -- fields.test.ts
# Run with coverage
npm test -- --coveragesrc/
├── tools/
│ ├── fields.ts
│ └── fields.test.ts # Field tests
├── api/
│ ├── client.ts
│ └── client.test.ts # API client tests
└── utils/
├── cache.ts
└── cache.test.ts # Cache tests
Example test for a new field:
import { FieldsTool } from './fields';
describe('FieldsTool', () => {
it('should include new comprehensive fields', () => {
const tool = new FieldsTool();
const fields = tool.listFields({ asset_type: 'stock' });
expect(fields.some(f => f.name === 'enterprise_value_current')).toBe(true);
expect(fields.some(f => f.name === 'gross_margin_ttm')).toBe(true);
});
});tradingview-mcp-server/
├── src/ # Source code
│ ├── index.ts # Main entry point & MCP server setup
│ ├── api/ # TradingView API integration
│ │ ├── client.ts # API client
│ │ └── types.ts # Type definitions
│ ├── tools/ # MCP tools
│ │ ├── screen.ts # Stock screening tool
│ │ └── fields.ts # Field listing tool
│ ├── resources/ # MCP resources
│ │ └── presets.ts # Preset configurations
│ └── utils/ # Utilities
│ ├── cache.ts # Response caching
│ └── rateLimit.ts # Rate limiting
├── docs/ # Documentation
│ ├── presets.md # Preset strategies guide
│ ├── fields.md # Field reference
│ └── local/ # Development docs
│ ├── MCP_SERVER_DESIGN.md # Architecture
│ └── SCREENER_FILTERS_GUIDE.md
├── dist/ # Compiled output (gitignored)
├── package.json
├── tsconfig.json
└── README.md
Fields are defined in src/tools/fields.ts.
Step 1: Find the TradingView field name
- Open TradingView screener in browser
- Open DevTools → Network tab
- Add your field to the screener
- Find the
/scanrequest - Check the
columnsarray for the field name
Step 2: Add to fields.ts
// src/tools/fields.ts
const STOCK_FIELDS: FieldMetadata[] = [
// ... existing fields
{
name: "quick_ratio", // TradingView field name
label: "Quick Ratio", // Display name
category: "fundamental", // fundamental | technical | performance
type: "number", // number | percent | currency | string | boolean
description: "Current assets minus inventory divided by current liabilities"
},
];Step 3: Add to EXTENDED_COLUMNS (if applicable)
If the field should be included in comprehensive presets:
// src/tools/screen.ts
export const EXTENDED_COLUMNS = [
...DEFAULT_COLUMNS,
// ... existing fields
"quick_ratio", // Add your field
];Step 4: Test
npm run build
# Restart Claude
# Test: "List all available fields"
# Test: "Screen stocks with quick ratio > 1.5"Presets are defined in src/resources/presets.ts.
Example: Creating a "Tech Growth" preset
// src/resources/presets.ts
export const PRESETS: Record<string, Preset> = {
// ... existing presets
tech_growth: {
name: "Tech Growth Stocks",
description: "High-growth technology stocks with strong margins and R&D investment",
filters: [
// Size and liquidity
{ field: "market_cap_basic", operator: "greater", value: 5000000000 },
{ field: "average_volume_90d_calc", operator: "greater", value: 500000 },
// Growth
{ field: "total_revenue_yoy_growth_ttm", operator: "greater", value: 15 },
{ field: "return_on_equity", operator: "greater", value: 18 },
// Margins (tech typically has high gross margins)
{ field: "gross_margin_ttm", operator: "greater", value: 60 },
{ field: "operating_margin_ttm", operator: "greater", value: 15 },
// R&D intensity (innovation)
{ field: "research_and_dev_ratio_ttm", operator: "greater", value: 10 },
// Sector filter
{ field: "sector", operator: "match", value: "Electronic Technology|Technology Services" },
],
markets: ["america"],
sort_by: "total_revenue_yoy_growth_ttm",
sort_order: "desc",
columns: EXTENDED_COLUMNS, // Use extended columns for deep analysis
},
};Best Practices:
- ✅ Use descriptive names and descriptions
- ✅ Balance filter count (3-16 filters optimal)
- ✅ Choose appropriate sort field
- ✅ Consider if extended columns are needed
- ✅ Test on real data to validate results
- ✅ Document typical result count
Tools are MCP endpoints that Claude can call.
Step 1: Create tool implementation
// src/tools/compare.ts
export class CompareTool {
constructor(private client: TradingViewClient) {}
async compareStocks(symbols: string[], fields: string[]): Promise<any> {
// Implementation
const results = await Promise.all(
symbols.map(symbol => this.client.getStockData(symbol, fields))
);
return {
comparison: results,
symbols,
fields
};
}
}Step 2: Register in index.ts
// src/index.ts
// Import
import { CompareTool } from "./tools/compare.js";
// Initialize
const compareTool = new CompareTool(client);
// Register tool
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
// ... existing tools
{
name: "compare_stocks",
description: "Compare multiple stocks across specified fields",
inputSchema: {
type: "object",
properties: {
symbols: {
type: "array",
items: { type: "string" },
description: "Stock symbols to compare (e.g., ['AAPL', 'MSFT'])"
},
fields: {
type: "array",
items: { type: "string" },
description: "Fields to compare"
}
},
required: ["symbols"]
}
}
]
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
// ... existing cases
case "compare_stocks": {
const result = await compareTool.compareStocks(
args.symbols,
args.fields || ["close", "market_cap_basic", "return_on_equity"]
);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
}
}
});Step 3: Test
npm run build
# Restart Claude
# Test: "Compare AAPL and MSFT on ROE and margins"Configure server behavior via environment variables:
| Variable | Description | Default | Valid Range |
|---|---|---|---|
CACHE_TTL_SECONDS |
Cache time-to-live | 300 (5 min) |
0 (disabled) to 3600 |
RATE_LIMIT_RPM |
API requests per minute | 10 |
1 to 60 |
In .mcp.json:
{
"mcpServers": {
"tradingview-local": {
"command": "node",
"args": ["/path/to/dist/index.js"],
"env": {
"CACHE_TTL_SECONDS": "600", // 10 minute cache
"RATE_LIMIT_RPM": "20" // 20 requests/min
}
}
}
}In shell (for testing):
export CACHE_TTL_SECONDS=0 # Disable cache
export RATE_LIMIT_RPM=5 # Very conservative rate limit
npm run build && node dist/index.jsThe MCP server writes to stderr (visible in Claude's logs):
// Add debug output
console.error("Debug: Filters received:", filters);
console.error("Debug: API response:", response);# Mac/Linux
tail -f ~/.claude/logs/mcp-server-tradingview-local.log
# Or check Claude's output panelIssue: Changes not appearing
Solution: You must restart Claude after rebuilding. The MCP server doesn't hot-reload.
npm run build
# Restart Claude Desktop or Claude CodeIssue: "Unknown operator" error
Solution: Check that filter operators match the allowed list in OPERATOR_MAP (src/tools/screen.ts).
Valid operators: greater, less, greater_or_equal, less_or_equal, equal, not_equal, in_range, match
Issue: Field not found
Solution: Verify field name exactly matches TradingView's API field name (case-sensitive).
# Check field exists
npm run build
# Ask Claude: "List all fields with 'margin' in the name"Issue: Cache not clearing
Solution: Restart the MCP server or set CACHE_TTL_SECONDS=0 to disable caching during development.
{
"env": {
"CACHE_TTL_SECONDS": "0"
}
}- Preset Strategies - Creating effective presets
- Field Reference - All available fields
- Main README - Getting started
When contributing:
- Follow TypeScript best practices
- Add tests for new features
- Update documentation (this file, fields.md, presets.md)
- Test with Claude before submitting PR
- Use conventional commits (feat:, fix:, docs:, etc.)
Example workflow:
# Create feature branch
git checkout -b feat/add-quick-ratio-field
# Make changes
vim src/tools/fields.ts
# Build and test
npm run build
npm test
# Commit
git add .
git commit -m "feat: Add quick ratio field for liquidity analysis"
# Push and create PR
git push origin feat/add-quick-ratio-fieldBuilt with ❤️ using the Model Context Protocol