MCP tools
Every tool of the MCP server, the scope it needs, what it takes and what it calls.
Trading tools go to the account's terminal as REST calls; account and hook tools do what the dashboard does; build_alert_message runs on its own. Your app sees every tool your best role across the connection's workspaces allows: someone who is only ever a member sees the read tools.
Answers
Every tool answers the same shape, as structured content and as the same JSON in text:
{ "status": 202, "retry_after": null, "body": { "request": "req_5f0c2a9e41d7b3a8c6e2f190", "state": "pending", "...": "..." } }status: the HTTP status the REST API (or, for account and hook tools, the dashboard) answers.retry_after: the REST answer'sRetry-Afterheader in seconds, ornullwhen it has none. Account and hook tools always havenull.body: the REST (or dashboard) body, as it is.
A 4xx or 5xx status is a tool error (isError). A 2xx never means filled: read the body's state, as over REST.
Ids and workspaces
Accounts (acc_...) and hooks (hk_...) are named by their own id: ids are unique across workspaces, so a call acts in the workspace the account or hook belongs to and takes no workspace. Only create_hook and connect_account_link name a workspace (ws_...). list_accounts and list_hooks cover every workspace where the connection can read, each item naming its workspace, or just the workspace you pass.
Each tool is marked for your app: read-only tools change nothing, destructive ones can close, cancel, stop or send live trades, and the rest (changes) change setup without trading. Your app may ask you before the destructive ones.
Read
Needs read in the workspace of the account or hook, unless it says otherwise.
| Tool | Scope | Marked | Takes | Does |
|---|---|---|---|---|
list_workspaces | none | read-only | nothing | The connection's workspaces: id, name, your role and the scopes it has there now. |
list_accounts | read | read-only | workspace? | MT5 accounts: id, workspace, login, server, status and host location. |
get_account_status | read | read-only | account | The account's record, and what its engine reports now (live: null when the host can't be reached). |
get_account | read | read-only | account | GET /v1/accounts/{account}: balance, equity, margin. |
list_positions | read | read-only | account | GET .../positions |
list_pending_orders | read | read-only | account | GET .../orders |
list_history_deals | read | read-only | account, from, to | GET .../history/deals |
list_history_orders | read | read-only | account, from, to | GET .../history/orders |
list_symbols | read | read-only | account | GET .../symbols |
get_symbol | read | read-only | account, symbol | GET .../symbols/{symbol} |
get_tick | read | read-only | account, symbol | GET .../symbols/{symbol}/tick |
get_request | read | read-only | account, request | GET .../requests/{request}: a trade request and how it ended. |
get_kill_switch | read | read-only | account | GET .../kill-switch |
list_hooks | read | read-only | workspace? | TradingView hooks, with status, applied, revision and targets. The URL is masked. |
get_hook | read | read-only | hook | One hook, URL masked. |
list_hook_alerts | read | read-only | hook, state?, reason?, account?, symbol?, before?, limit? | The hook's alert log, newest first, up to 100 a page. Pass the answer's next as before for the next page. |
get_hook_alert | read | read-only | hook, alert | One alert, its outcome on each target account and each engine's trace. |
list_symbol_maps | read | read-only | account | The account's TradingView symbol mappings. |
list_broker_symbols | read | read-only | account, names? | The broker's symbols on the account; with names (comma-separated), their specifications and default pip sizes. |
list_owned | read | read-only | account | What the account's hooks own on it: positions, orders, lanes and managed stops. |
connect_account_link | read | read-only | workspace, server?, login? | A dashboard link that opens the connect dialog for the workspace, filled in. You connect the account there. |
build_alert_message | none | read-only | the builder's choices, all optional (below) | The JSON message for a TradingView alert, as the dashboard's builder writes it, with problems and warnings. |
? marks an optional argument. from and to are broker server time in seconds, as over REST.
Trade
Needs trade in the account's (or, for resend_alert, the hook's) workspace. A trade tool takes the REST call's path values and body fields as arguments, named as on its REST page, plus account.
| Tool | Scope | Marked | Takes | Does |
|---|---|---|---|---|
check_order | trade | read-only | account and the order's fields | POST .../orders/check: MT5's dry run. Sends nothing to the trade server. |
place_order | trade | destructive | account, idempotency_key and the order's fields | POST .../orders |
modify_order | trade | destructive | account, order, idempotency_key and the new values | PATCH .../orders/{order} |
cancel_order | trade | destructive | account, order, idempotency_key | DELETE .../orders/{order} |
set_position_levels | trade | destructive | account, position, idempotency_key, sl, tp | PATCH .../positions/{position} |
close_position | trade | destructive | account, position, idempotency_key and the close's fields | POST .../positions/{position}/close |
close_by | trade | destructive | account, position, idempotency_key, position_by | POST .../positions/{position}/close-by |
engage_kill_switch | trade | destructive | account, deviation? | POST .../kill-switch |
lift_kill_switch | trade | destructive | account | DELETE .../kill-switch |
resend_alert | trade | destructive | hook, alert, accounts, idempotency_key | Sends an earlier alert again, as a new one, to these target accounts (1 to 20): live trades. Only targets whose delivery is settled and wasn't partly done. |
For the account tools, idempotency_key is your own key for the request, sent as the Idempotency-Key header and scoped to the account, exactly as over REST. For resend_alert it's a key of the hook's: the same key with the same alert and accounts answers the one resend it made, for 7 days, and the same key with a different alert or accounts is refused 409 (key_reused). It's required wherever it's listed. How to retry is on retries and errors.
Manage
Needs manage in the account's or hook's workspace (create_hook: the workspace it names).
| Tool | Scope | Marked | Takes | Does |
|---|---|---|---|---|
start_account | manage | changes | account | Starts the account's terminal. |
stop_account | manage | destructive | account | Stops the terminal: Pipsocket sends nothing to the account until it's started. Positions, pending orders and their stops stay at the broker as they are; to close them, use the kill switch first. |
restart_account | manage | changes | account | Restarts the terminal. |
create_hook | manage | changes | workspace, name, maxAgeS?, tvIpsOnly?, quota?, notify?, targets? | Makes a hook in the workspace. Every target must be an account of that workspace. Answers the hook and its url (below). |
update_hook | manage | changes | hook, name?, maxAgeS?, tvIpsOnly?, quota?, notify? | The hook's own settings. |
pause_hook | manage | changes | hook | Alerts are recorded, not acted on. status is pausing until the hook and every engine have it, then paused. |
resume_hook | manage | changes | hook | New alerts trade again. |
delete_hook | manage | destructive | hook | Deletes the hook. done: false means removal goes on in the background. Owned positions and managed stops stay until they close (list_owned). |
rotate_hook_url | manage | destructive | hook | A new secret: the old URL stops once the hook has it. Answers the new url (below). |
get_new_hook_url | manage | read-only | hook | The URL this connection just made (below). |
add_hook_target | manage | changes | hook, account, shortName?, enabled?, settings? | Adds a target account of the hook's workspace, with its settings. |
update_hook_target | manage | changes | hook, account, shortName?, enabled?, settings? | Changes a target. |
remove_hook_target | manage | destructive | hook, account | Removes a target. |
test_hook | manage | read-only | hook, message | A dry run of a message on the hook's targets: checked and sized by each engine, never traded. The message is sent as written: give real values, not {{placeholders}}. |
set_symbol_map | manage | changes | account, from, to, pipSize? | Maps a TradingView symbol or ticker (BITSTAMP:BTCUSD) to a broker symbol on the account. |
delete_symbol_map | manage | changes | account, from | Removes a mapping. |
release_owned | manage | destructive | account, hook, tag?, symbol | Releases a hook lane's ownership on the account: its positions are no longer the hook's. |
Hook settings and targets take the dashboard's own fields; anything a tool doesn't know is refused, not ignored. Each account and hook tool answers the dashboard's status and body for the same action.
Pausing or deleting a hook doesn't close what it already opened: its positions and managed stops stay until they close.
start_account, stop_account and restart_account are asked once. After a 503 the outcome is unknown, and the account stays busy until its lease runs out: read get_account_status rather than asking again. 409 means another change to the account is still going: wait, then read it.
Building an alert message
build_alert_message takes the dashboard builder's choices, not the message's own fields (those are on alert messages). Every argument is optional: left out, it builds a buy at market with each account's own size, on the chart's symbol. Values may be numbers or TradingView placeholders such as "{{close}}".
| Argument | Takes |
|---|---|
mode | indicator or strategy |
action | open, close, reverse, modify, cancel, close_all or kill_switch |
side | buy, sell or all |
symbol, tag | Text. symbol is the chart's by default. |
size | { "kind": "default" }, or { "kind": "lots" | "risk_percent" | "risk_amount", "value": ... } |
sl, tp | { "unit": "pips" | "points" | "price" | "percent", "value": ... }, or null |
ladder | Take-profit rungs: [{ "unit": ..., "value": ..., "part": ... }], or null |
entry | A pending entry: { "type": "limit" | "stop" | "stop_limit", "at": { "unit": "price" | "offset_pips" | "offset_points", "value": ... }, "limit"?: ..., "expires"?: ... }, or null |
trailing | In pips: { "kind": "set", "value": { "distance": ..., "step"?: ..., "start"?: ... } }, { "kind": "clear" } or { "kind": "off" } |
breakEven | In pips: { "kind": "set", "value": { "trigger": ..., "offset"?: ... } }, { "kind": "clear" } or { "kind": "off" } |
portion | For a close: { "kind": "all" }, or { "kind": "percent" | "lots", "value": ... } |
accounts | The hook's targets the alert is for (account ids or target short names); every target when left out |
maxAge, slippage | The alert's age limit for entries, in seconds, and the slippage for market orders, in points |
src | The template's short id in its event ids (up to 8 lowercase letters and digits); a new one when left out |
A buy risking 1% with a 25 pip stop:
{ "action": "open", "side": "buy", "size": { "kind": "risk_percent", "value": 1 }, "sl": { "unit": "pips", "value": 25 } }It answers { "message": "...", "problems": [...], "warnings": [...] } in body: paste message into the alert as it is, placeholders and all. A message with problems would be refused on every alert.
To dry-run it with test_hook, send a copy with each {{placeholder}} replaced by what TradingView would send: the symbol, and the current UTC time for {{timenow}} (written like 2026-11-08T14:00:00Z). The time must be now: an entry older than its age limit (30 seconds unless the hook or message sets another) is refused stale, as a real alert would be. The test is sent as written: a placeholder left in a time or a number is refused before anything is checked, and one left in text goes through as those characters, so replace them all.
Hook URLs
A hook's URL carries its secret: whoever holds it can send trades. Over MCP it's shown only to the connection that made it, and only briefly:
create_hookandrotate_hook_urlanswer the URL once the hook has the new secret, usually at once. Until thenurlisnull: callget_new_hook_urla few seconds later.get_new_hook_urlanswers the URL only if this connection made the hook's current secret in the last 15 minutes. Otherwise it answers409with areason:not_applied_yet: the hook doesn't have the secret yet. Ask again in a few seconds.superseded: another rotation replaced it. Tell the person; don't rotate again.expired: more than 15 minutes, or this connection didn't make it. Reveal it in the dashboard (it asks for your password).
- A lost answer to
create_hookorrotate_hook_urlis recovered withlist_hooksandget_new_hook_url, never by creating or rotating again.
The URL is never in a list, a hook's details or the audit log. It does go into your chat with the app, so treat that chat as you would the URL.
Set up TradingView from a chat
Ask for the whole thing at once: "Set up a hook that trades my EMA strategy on BTCUSD on my FBS account, 1% risk, 25 pip stop." With manage and trade ticked, an app can:
list_accountsto find the account and its workspace.list_broker_symbolsfor the broker's name for BTCUSD, andset_symbol_mapto map TradingView's symbol to it.create_hookin that workspace, with the account as its target;get_new_hook_urlifurlwasnull.build_alert_messagefor the message, andtest_hookto dry-run a copy of it, with real values in place of the placeholders, on the account.
Then you paste the URL and the message into the TradingView alert. See TradingView alerts.