MCP tools
The seventeen tools vike-cli mcp advertises, what each one reaches, the profile that can withhold whole rings of them, and the gates — confirm plus a single-use preview_token — that stand between an agent and a real order.
tools/list returns the whole registry in one response. tools_spec() in
crates/vike-cli/src/cmd/mcp.rs is that registry — every tool's name, description, input schema
and risk annotations — and Server::call_tool's match name is the dispatch half. Each tool has
its own page below: what it does, its schema, a call you can paste into a stdio session, the shape
that comes back, and one prompt that reaches it.
Three planes, and what each one needs
The tools split by what they touch, not by what they do. Nothing in the first group opens a socket at all.
| Plane | Tools | Needs |
|---|---|---|
Offline — pure vike-script, no server | validate_strategy · discover_params · list_templates · list_indicators | nothing |
A vike-datahub server | run_backtest · run_sweep · run_walk_forward · list_strategies · list_series | --addr, default 127.0.0.1:7878 (DEFAULT_ADDR) |
A running vike-tradehub node | node_snapshot | --node + the observe key |
| The same node, order-write | submit_order · cancel_order · modify · flatten · market_exit · mass_cancel · set_trading_state | --node + the control key |
The two halves fail separately and the offline four never notice: Server::ensure_observe and
Server::ensure_control run before any socket is opened, and a missing key produces an error that
names both places it looked. The server page has the flag and key detail.
The registry a session sees can be smaller than this
tools/list returns the whole registry only under the default --profile full. An operator who
launches the server as --profile read-only serves the first three rows above and withholds the
fourth; --profile offline serves only the first; --deny-tool NAME takes out one more, and is
repeatable. A withheld tool is absent from the advertised registry as well as refused by
tools/call, because ToolAccess::new produces the advertised set and its withheld complement from
one pass over tools_spec() — an advertised-but-refused tool, or a withheld-but-routed one, is the
bug a scoping feature must not ship with. The rings are predicates rather than second rosters:
read-only is the negation of is_write_tool (so an eighth write verb joins it by construction)
and offline is each tool's own openWorldHint annotation read back. Which tools each ring leaves
you, and how a refusal reads, is on vike-cli mcp.
Every result has the same shape
A success is tool_ok: the payload pretty-printed into one text content block, the same value
again as structuredContent, isError: false. A failure is tool_err — the message as text with
isError: true. That is an ordinary tool result, not a JSON-RPC error, so the agent reads it and
retries rather than seeing the call fail at the protocol layer. Even a panicking tool arrives that
way: Server::handle wraps every call in catch_unwind and turns a panic into
tool panicked: <name>, pinned by a_panicking_tool_becomes_iserror_not_a_dead_session.
Ten read, seven write
The split is machine-pinned rather than conventional. Every read-only tool carries
readOnlyHint: true and every write tool carries destructiveHint: true in tools_spec(), and
tools_list_has_read_and_write_tools_with_correct_hints fails the build if a tool falls on the
wrong side — it also asserts each write tool's description mentions confirm, so at least half
the gate is part of the advertised contract. Only half: the description is what that test reads,
and the schema carries the other half. Both properties are defined once and spliced into all
seven tools — confirm_property and preview_token_property — so the wording cannot drift between
them, which is how the old inline copies ended up in four different spellings. The write roster is
a fixed-size const, WRITE_TOOLS: [&str; 7], because it must stay the full write-verb set of the
human trade REPL: both surfaces build their commands through the one construction site in
crate::cmd::verbs, so they cannot drift into two vocabularies.
A write needs confirm: true AND a preview_token — and one without the other is a preview, not an error
Server::call_tool's write arm reads the two together: a preview_token is only looked up when
confirm is the JSON boolean true, and a confirm with no token binds nothing. Fail either
condition and preview_of returns the resolved wire command, the advisory guardrail check, the
node's own dry-run when there is a node to ask, the rationale that would be recorded — and a fresh
preview_token. Nothing is sent, and nothing is flagged: will_execute: false is the only signal.
confirm is read as a JSON boolean and nothing else, so "true", 1 and "yes" fall through the
same way.
A token that is unknown, already used, expired past PREVIEW_WINDOW or bound to a different
command is the loud case — a tool error naming which. The mechanism, and why the token is
deliberately not a secret, is on Live node.
The arming ceiling applies to every order-placing tool
A write tool mounts nothing. It hands a WireCommand to a node that is already running, and what
that node may do to a venue was decided at its mount, not here. vike_mount::make_engine
consults the deployment's per-venue arming ceiling — policy.venues.<venue>, ordered
paper < demo < live — first, above the credential read: a paper venue returns the paper
client having loaded no credential, fetched no instrument grid and opened no socket. The ceiling
can only ever refuse, because vike_config::VenueMode::cap is min and never max, so
venues.bybit = "live" does not put bybit live — it declines to stop it. Below that sits the older
rule, that absent credentials are the live gate.
Two more gates stand between an accepted command and a fill, and both are server-side on the node:
ControlLimits::vet consumes a rate token on every command and applies its notional cap only to
the order-increasing verbs that carry a price (Submit and Modify — cancel, flatten, mass-cancel,
market-exit and set-trading-state are risk-reducing and never size-capped), and beneath it the
execution core's RiskGate. The preview's own guardrail is client-side advisory UX; nothing an
agent sends from here can loosen either. Risk and
arming is the concept page,
policy.toml the key.
Where to start
Drive Vike from Claude runs the whole loop end to end;
reference/cli/mcp is the verb that starts the server.
Drive Vike Trader from Claude
Two captured MCP sessions, annotated — the handshake, the templates, a compile error, a tool call that failed because the agent guessed, and the backtest report that came back.
validate_strategy
Compile a Rhai strategy offline — and understand exactly which class of mistake a compile can and cannot catch.