tradingview-mcp-proof
Provides integration with TradingView webhook alerts, exposing market data to an MCP server with freshness checks, staleness detection, gap reporting, and duplicate alert protection so stale prices are never presented as current.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tradingview-mcp-proofIs the latest price for EUR/USD still usable for analysis?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
tradingview-mcp-proof
An MCP server for the failure that costs money when TradingView data reaches a language model: the feed stops, the tool keeps answering, and Claude analyses forty-minute-old prices as if they were the live market.
This repo does not demonstrate "building an MCP server". The official SDK documentation covers that in a page. It demonstrates, with runnable evidence, that a server handing market state to a model:
never lets a stale reading pass as current -- age, missing bars and a usability verdict travel with every value;
never double-counts a redelivered alert, and never lets a late one rewrite the present;
reports holes in history instead of closing them, including the case where the newest bar is perfectly fresh and the window behind it is broken;
rejects forged alerts, because TradingView signs nothing.
You can verify all of it in about five minutes, on your machine, without a TradingView account, without an Anthropic key and without any network access. No credentials of mine are involved anywhere.
Why this is hard with TradingView specifically
A webhook alert is fire-and-forget HTTP. There is no delivery receipt, no retry and no ordering guarantee. If your endpoint is slow, restarting or briefly unreachable, that alert is gone -- and nothing downstream is told.
Four more traps compound it:
{{time}}and{{timenow}}are different clocks.{{time}}is the open of the bar that triggered the alert;{{timenow}}is when the alert fired. The freshness of a market view is governed by the bar, not by the firing, and using the wrong one hides staleness behind a recent timestamp.{{interval}}renders intraday resolutions as a bare number of minutes."60"is one hour. Read it as seconds and every hourly bar looks 59 minutes stale; hard-code a unit and daily bars break instead.An alert that fires once per bar arrives before the bar closes. Its values can still change. Treated as settled history, it quietly corrupts the series.
There is no signature. No HMAC header, no shared-secret challenge -- anyone who learns the URL can post a signal the model will then reason over.
And the trap specific to putting any of this in front of an LLM: a model has
no clock and no feed. Handed {"signal": "long", "price": 21850.25} it will
analyse that as the present, because nothing in the payload says otherwise. It
cannot caveat what it cannot see.
Related MCP server: TradingView-MCP
The design under test
Freshness is computed server-side and travels with the value. Not a timestamp the model is expected to interpret -- an explicit
usable_for_analysisverdict plus the reason in words.The ledger is append-only and keyed by
alert_id. A redelivery is a no-op. Idempotency is what makes one signal one signal.Current state is ordered by bar time, never by arrival. A late packet is stored as history and never becomes the present. Discarding it instead would manufacture a gap that was not there.
A close revises a provisional reading; nothing revises a close.
Gaps inside the window are counted and reported, separately from staleness at the edge, because a fresh newest bar over a holed history is the case that looks healthy and is not.
The server does not claim to know why data stopped. It cannot distinguish a feed outage from a session break without a market calendar, so it reports the observable gap and refuses to call either one live. Guessing here is how a server cries wolf every weekend until the operator stops believing it.
Verify it in five minutes
git clone https://github.com/guillemustafa-ux/tradingview-mcp-proof
cd tradingview-mcp-proof
python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest -vtest_staleness.py::test_naive_tool_presents_a_40_minute_old_reading_as_current PASSED
test_staleness.py::test_hardened_tool_refuses_to_call_the_same_reading_current PASSED
test_staleness.py::test_a_fresh_closed_bar_is_usable PASSED
test_staleness.py::test_one_missed_bar_is_already_stale PASSED
test_staleness.py::test_empty_symbol_is_not_a_silent_none PASSED
test_delivery.py::test_redelivered_alert_is_stored_once PASSED
test_delivery.py::test_a_late_bar_never_becomes_the_present PASSED
test_delivery.py::test_bar_close_revises_the_provisional_reading PASSED
test_delivery.py::test_a_close_is_never_revised_by_a_later_provisional PASSED
test_gaps.py::test_hole_inside_the_window_is_reported_not_interpolated PASSED
test_gaps.py::test_a_contiguous_window_reports_no_gap PASSED
test_gaps.py::test_recovery_makes_the_newest_bar_fresh_while_history_stays_holed PASSED
test_auth.py::test_forged_alert_never_enters_the_ledger PASSED
test_auth.py::test_missing_secret_is_rejected PASSED
test_auth.py::test_absent_and_wrong_secret_are_indistinguishable PASSED
test_auth.py::test_hourly_interval_is_read_as_minutes_not_seconds PASSED
test_server.py::test_server_exposes_the_three_tools PASSED
test_server.py::test_stale_verdict_survives_the_protocol_round_trip PASSED
test_server.py::test_feed_health_answers_without_being_asked_for_a_price PASSED
test_server.py::test_tool_survives_being_dispatched_on_another_thread PASSED
20 passedWhat each scenario proves
# | Test | Situation | Proven |
1a |
| feed dead 40 min | the anti-pattern, demonstrated: the tutorial-shaped tool returns a payload with no temporal field at all, byte-identical to the one it gave when the data was fresh |
1b |
| same ledger, same instant |
|
1c |
| exactly one dropped alert | staleness does not require an outage; a single drop breaks the claim |
1d |
| healthy feed | no false alarm -- the verdict is usable and no warning is attached |
1e |
| never-seen symbol |
|
2a |
| TradingView re-fires the same alert | stored once; one signal stays one signal |
2b |
| bar 2 arrives after bar 3 | the present does not rewind, and the late bar is still kept as history |
2c |
| intrabar alert, then its close | provisional while open, revised by the close, flagged throughout |
2d |
| stray intrabar after a close | settled history is not reopened |
3a |
| bars 2 and 3 dropped | the hole is counted and named; the missing bars are absent, not guessed |
3b |
| feed recovers | "the feed is back" and "the data is usable" are two different claims |
3c |
| clean window | no false positive |
4a |
| wrong secret | rejected before storage |
4b |
| missing vs wrong secret | identical error, so the endpoint is not an oracle |
4c |
| 1-hour chart |
|
5a |
| real MCP wiring | the tools exist on an actual |
5b |
| real | the verdict reaches the client through the protocol, not just the unit test |
5c |
| SDK threading | see below |
The drill
python scripts/drill.pyReplays a 5-minute feed that stops at 14:15 and prints what each server hands to Claude at four points in time:
=== Feed healthy -- wall clock 14:21 UTC ===
naive server -> {"symbol": "NQ1!", "signal": "long", "price": 21850.25}
this server -> {"price": 21850.25, "status": "FRESH", "age_seconds": 360,
"missing_bars": 0, "usable_for_analysis": true}
=== Feed down for 40 minutes -- wall clock 14:55 UTC ===
naive server -> {"symbol": "NQ1!", "signal": "long", "price": 21850.25}
this server -> {"price": 21850.25, "status": "STALE", "age_seconds": 2400,
"missing_bars": 6, "usable_for_analysis": false}
warning -> DO NOT present this as the current market. newest bar is
2400s old on a 300s chart; 6 bar(s) that should have arrived
did not. [...]The naive line never changes. Same four fields at 14:21 and at 14:55 -- so a model reading it answers "NQ is long at 21850.25" with equal confidence on data that is four seconds old and on data that is forty minutes dead.
Two things that bite in production and not in tests
The SDK renamed FastMCP. In the Python SDK 2.x it is
from mcp.server.mcpserver import MCPServer. Every
from mcp.server.fastmcp import FastMCP example online is v1 code and will not
import. The package raises a pointed migration error rather than a bare
ModuleNotFoundError, which is a kindness worth knowing about.
The SDK runs synchronous tools on a worker thread. Tool functions are
dispatched through anyio.to_thread.run_sync, so a connection opened on one
thread gets used from another. A default sqlite3 connection raises
ProgrammingError: SQLite objects created in a thread can only be used in that same thread on the first real client call -- while every direct-call unit
test keeps passing. This repo hit exactly that, fixed it with
check_same_thread=False plus a lock, and pinned it with
test_tool_survives_being_dispatched_on_another_thread.
Wiring it to Claude Desktop
{
"mcpServers": {
"tradingview": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["-m", "tradingview_mcp_proof.server"],
"env": {
"TVMCP_LEDGER": "/absolute/path/to/tradingview-alerts.db"
}
}
}
}Ingest and serving are deliberately separate: your webhook receiver writes to the ledger, the MCP server only reads from it. Coupling them means a slow model call can drop an alert, and TradingView will not send it again.
Deliberately out of scope
Honesty about the edges is part of the argument:
No market-session calendar. The server reports the observable gap and refuses to guess whether it is an outage or a closed market. Supplying session hours would let it distinguish the two; that is a configuration decision an operator should make, not a default this repo invents.
No HTTP receiver. The transport in front of the ledger is a thin concern and varies per deployment (FastAPI, a Cloudflare Worker, a tunnel).
Ledger.record()is the seam.No price-history backfill. This models the alert stream, which is what a Pine strategy actually emits. Reconstructing full OHLC needs a data vendor and is a different problem.
One symbol per query. No fan-out, no subscriptions, no streaming.
Licence
MIT.
Available Tools
3 toolsget_feed_healthB
Whether the alert feed itself is alive, without asking for a price. Use this before answering any question that assumes live data.
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral disclosure burden, and it does clarify that this is a feed-liveness check rather than a price/data fetch. It does not disclose the output shape, whether the check is per-symbol, or runtime implications, but the core non-price health-check behavior is reasonably conveyed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two short sentences with no filler, and the primary purpose is front-loaded. The usage instruction is separate and actionable, so every clause earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description should clarify what the healthy/unhealthy result looks like, but it only vaguely suggests a boolean alive/not-alive idea. The mandatory symbol parameter is also completely unexplained, leaving an agent uncertain about how to call the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema provides only a title 'Symbol' for the sole required parameter, and the description never mentions symbol at all. With 0% schema description coverage, the description was expected to explain why a symbol is needed for a feed-health check and how it affects the result; it fails to do so.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The predicate 'Whether the alert feed itself is alive' clearly identifies the resource and the health-check operation, and it explicitly distances the tool from price retrieval. It is distinguishable from siblings such as get_recent_bars and get_market_state, though it lacks a direct imperative verb like 'check' or 'get'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The sentence 'Use this before answering any question that assumes live data' is an explicit, actionable condition for invoking the tool. 'Without asking for a price' provides a boundary, but the description does not name alternative tools or broader when-not-to-use scenarios.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_market_stateB
Current market state for a symbol, with an explicit freshness verdict. Check freshness.usable_for_analysis before using the value.
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the behavioral disclosure burden. It adds meaningful behavioral context: the response includes a freshness verdict and a usable_for_analysis field that must be checked before relying on the value, implying staleness is possible. This is genuinely informative, though it could also state read-only guarantees or error behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two tight sentences with the core purpose front-loaded and a critical usage caveat included. There is no filler or redundant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter tool with no output schema, the description gives essential freshness-checking guidance but does not explain what the market state payload actually contains or what other fields to expect. An agent can call it correctly but may not fully understand the response beyond the freshness verdict.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the description only restates that the tool works 'for a symbol' without adding format, examples, or accepted value conventions. It adds no real semantic value beyond the schema's property name and type.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that the tool returns the current market state for a symbol and flags an explicit freshness verdict. It identifies the resource and the nature of the result, though it does not explicitly distinguish itself from siblings like get_recent_bars or get_feed_health.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool instead of its siblings, nor any exclusions or alternative routing. The advice to check freshness.usable_for_analysis is useful after the call but does not help an agent decide when to invoke this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recent_barsB
The most recent bars, newest first, with a count of any bars missing inside the window. A gapped window cannot carry a trend.
| Name | Required | Description | Default |
|---|---|---|---|
| count | No | ||
| symbol | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description partially carries the behavioral burden: it discloses ordering, recency, and the missing-bar count. However, it does not disclose output shape, the meaning of 'window', paging/limits, side effects, or other operational behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the main result and ordering, with no filler. The second sentence adds a meaningful caveat rather than redundant restatement.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There are no annotations and no output schema, so the description must make the tool self-contained. It does not explain parameter semantics, the structure of returned bars, or the exact meaning of the window, leaving important invocation details unspecified.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description does not define the count or symbol parameters. The word 'window' might relate to count, but the description never explicitly states that count controls how many bars are returned or that symbol selects the bar series.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the resource (recent bars), the ordering (newest first), and a notable output trait (count of missing bars). It is clearly distinct from get_market_state and get_feed_health, though it lacks a direct verb such as 'retrieves'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit when-to-use or when-not-to-use guidance is given. The second sentence implies a trend-analysis caveat, but it does not describe alternatives or conditions that would select one sibling tool over another.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
TDQS
Each tool targets a clearly distinct concern: feed liveness, current market state, and recent bar history. There is no overlap because the metadata exposed by each tool is unique to its purpose.
All three tools follow the same get_<noun> pattern in lower_snake_case. The naming is perfectly uniform and predictable.
Three tools is a minimal but well-scoped set for a market-data proof server. Each tool earns its place and adds a non-redundant capability.
The set covers the core lifecycle of checking whether data is live, retrieving a current state, and inspecting recent bars. It lacks broader historical queries or symbol metadata, but those are reasonable gaps for a proof-focused server.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Verified, sourced, real-time intelligence layer for AI agents.
Verified market data for AI trading agents: quality-flagged candles, funding, OI, order flow. x402.
Live trading-pipeline intelligence for AI agents: signal scoring, calibration, recorded outcomes.
Live multi-asset market data for AI agents with provenance, starter credits, x402, and examples.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceEnables AI co-pilots to interact with TradingView charts, manage alerts via REST API, automate morning briefs with custom trading rules, and perform real-time market analysis.
- AlicenseNot gradedqualityBmaintenanceEnables AI to analyze and manipulate TradingView charts via Chrome DevTools Protocol, and access market data through public scanner API.5421MIT
- FlicenseNot gradedqualityDmaintenanceIntegrates with TradingView Desktop to enable morning briefs, chart analysis, Pine Script development, and trading workflow automation through natural language.552
- FlicenseNot gradedqualityBmaintenanceEnables AI agents to read and control TradingView Desktop charts in real time, supporting chart analysis, Pine Script development, alerts, replay practice, and multi-pane automation.552
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/guillemustafa-ux/tradingview-mcp-proof'
If you have feedback or need assistance with the MCP directory API, please join our Discord server