Connect an agent
Point Claude at vike-cli mcp — 17 tools to author strategies, run backtests, watch a live node, and (behind a preview gate) trade.
vike-cli mcp is a local stdio MCP server that exposes Vike's agent surface as tools: an agent
can author and validate a Rhai strategy, run backtests, sweeps and walk-forwards against a data
service, read a running trading node's live state, and — gated behind a mandatory preview —
place and manage orders on it. The client-side preview is UX; the node's own limits and the
core risk gate are what actually enforce.
This page is the Trader-side summary. Build with AI is the same server documented as an MCP surface — Client setup per client (Vike's remote data server is a separate entry the same client can hold at the same time; it is set up on Data client setup), and Trader tools tool by tool.
Add the server
vike-cli mcp speaks MCP's stdio transport (newline-delimited JSON-RPC 2.0), so any MCP client
can launch it directly. Two optional flags scope what the tools can reach:
--addr <host:port>— thevike-datahubserver the backtest tools use (default127.0.0.1:7878),--node <host:port>— the runningvike-tradehubnode the trade tools observe and control (no default; without it the create+backtest tools still work,node_snapshotreturns a clean error, and a write tool still previews — what errors is a call that passes the gate, carrying bothconfirm: trueand a validpreview_token, and finds no node to send to).
Two more decide which tools the session has at all, and they belong in the same entry:
--profile full|read-only|offline— the default isfull, all seventeen.read-onlyserves the ten read tools and withholds the seven order-write ones;offlineserves only the four that open no socket. A withheld tool is absent fromtools/listas well as refused if called, because the advertised roster and the routing gate are two complements of one pass over the registry (ToolAccess::new,crates/vike-cli/src/cmd/mcp.rs) rather than two lists that can disagree.--deny-tool <name>— withhold one more by name, repeatable.
An unknown value for either is a usage error naming the valid set and never widens to full. Only
whoever edits the launch command can change the scope; the agent cannot, and the refusal it gets
says so instead of sending it hunting for a switch.
Claude Code:
claude mcp add vike -- vike-cli mcp --node 127.0.0.1:7879Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"vike": {
"command": "vike-cli",
"args": ["mcp", "--node", "127.0.0.1:7879"]
}
}
}Node keys
The node tools authenticate with two HMAC keys, resolved from the process environment first and
then from the same credential store the daemon reads (<project>/settings/secrets.env):
VIKE_TRADEHUB_OBSERVE_KEY— reads (node_snapshot),VIKE_TRADEHUB_CONTROL_KEY— writes (the seven tools below,set_trading_stateincluded).
If a key is in neither place, the affected tools return an error naming both locations
(vike-cli secrets path prints where the store resolves; vike-cli secrets list shows key
names, never values).
Verify it works
Ask Claude:
show me the node snapshot
That calls the read-only node_snapshot tool and returns the node's live state — orders,
positions, per-venue equity, recent events. With no node configured you can still verify the
offline half: "list the vike strategy templates" calls list_templates with no server at
all.
The 17 tools
Read-only (safe, readOnlyHint):
| Tool | What it does | Needs |
|---|---|---|
validate_strategy | Compile a Rhai strategy — compile is validation | nothing (offline) |
discover_params | List a script's tunable param(name, default) knobs | nothing (offline) |
list_templates | Starter Rhai strategies as {name, code} | nothing (offline) |
list_indicators | The host-bound built-in roster; never your user_data/indicators | nothing (offline) |
run_backtest | Run a profile TOML on the datahub, return the report | --addr reachable |
run_sweep | Parameter-grid sweep, server-ranked | --addr reachable |
run_walk_forward | Anchored out-of-sample walk-forward | --addr reachable |
list_strategies | The compiled native strategies the server offers | --addr reachable |
list_series | Every stored data series, with coverage | --addr reachable |
node_snapshot | Live node state: orders, positions, equity, events | --node + observe key |
Order-write (destructiveHint, all preview-gated, all need --node + the control key):
submit_order, cancel_order, modify, flatten, market_exit, mass_cancel,
set_trading_state. This is the full write-verb roster of the human trade REPL — both
surfaces build commands through the same construction site (crate::cmd::verbs), so the two
can never drift.
A worked flow
A typical create-and-test session, tool by tool:
list_templates— start from a shipped starter (SMA cross, RSI reversion, Donchian breakout), each parameterized viaparam(). Read the body before trusting the name: the Donchian starter is a shape, not the indicator — it stands in withsma, because a baredonchian(...)is not bound: its first output line isupperrather than the namesake, so only the per-line accessors (donchian_upper()and its siblings) are callable.validate_strategy— compile the edited script. A bad script is anok: falseanswer with the compiler error, not a tool failure — the agent reads it and fixes the script.run_backtest— ship a profile TOML (the agent can inline the script via thescriptargument) and read the returned report JSON.run_sweep— the same profile with a[sweep]table; the server returns the ranked grid, best first. Follow withrun_walk_forwardbefore believing a search.
The safety model
Stated from the source, because an agent with order-writing tools deserves an exact contract:
- Paper by default, twice over. A venue with no credentials in
<project>/settings/secrets.envmounts as paper — absent credentials are the live gate — and the per-venue ceilingpolicy.venues.<venue>inpolicy.tomldefaults topaperand can only ever refuse: the fold that applies it is a minimum, never a maximum (crates/vike-config/src/venue_mode.rs'sVenueMode::cap), so aliveline arms nothing by itself. Nothing an MCP tool does can arm a venue. - Mandatory preview on every write, and the confirm needs a token. A write executes only on a
call carrying both
confirm: trueand thepreview_tokenan earlier preview of that same command returned. Anything less previews: it returns the fully resolved wire command, a client-side guardrail check, the rationale that would be recorded, and a fresh token. This is not convention — it is the code path: incrates/vike-cli/src/cmd/mcp.rs,Server::call_toolreadsconfirmas exactly the JSON booleantrue(the string"true"previews too) and looks the token up inPendingPreviews, which fires it once, expires it after 60 seconds and binds it to the command it previewed. ⚠ A confirming call with no token is the dangerous shape, because it is not an error: it returns another preview,will_execute: false,isError: false. Readwill_execute, never the absence of an error. A preview is also not necessarily offline — with--nodeset and a control key present it dials the node for a dry-run verdict (Server::node_preview); what it never does is send the command. And the gate is not the only answer available:--profile read-onlyin the launch command above hands the agent a server whosetools/listdoes not contain a write tool at all, which is the right shape when the session's job is to read and report. - The node is the enforcing gate, not the client. The preview's guardrail (the local
policy.tomlnotional ceiling) is advisory UX. What enforces is server-side on thevike-tradehubnode —crates/vike-tradehub/src/server.rs'sControlLimits::vet, a per-connection rate token bucket plus the notional cap built fromControlLimitsConfig, whose ceiling comes from the node'spolicy.toml(max_notional_per_order) — and, beneath it, the execution core'sRiskGate. An agent cannot loosen either from the client side. The two have different reach: the rate token is spent on every command, while the notional cap only judges order-increasing verbs that carry a price (submit_order,modify) — aflattenormass_cancelis rate-limited but never size-checked, which is the right way round for commands that only reduce exposure. - Every order carries a client order id. The node refuses a remote submit with an empty
client_order_id, so the local MCP server mints one before the preview renders (crates/vike-cli/src/cmd/verbs.rs'sfill_client_order_id— the same generator and wire form the live core uses). The agent does not have to carry that id back: a confirming call executes the stored command rather than the one it rebuilt, so the id that reaches the venue is the id the preview displayed — which is also whysame_intentignores exactly this field when it binds a token to its command.client_order_idin the accepted response is always the authoritative id. The node's registry is coid-keyed but does not dedupe: a repeated id re-sends the order and replaces its registry row. - Honest outcomes, per command. Each write tool reports the fate of its own command:
accepted, a named refusal, or — on a timeout or dropped connection — an explicit unknown telling the agent to checknode_snapshotbefore retrying, because the command may have executed. There is no shared error latch that could misattribute an old refusal to a new command. - A recorded "why". Every write tool takes an optional
reasonstring — the agent's rationale. It is echoed in the preview, sanitized server-side, and lands in the node's audit trail; it never reaches the order, the core, or a venue. The sanitizer issanitize_reasonincrates/vike-tradehub/src/audit.rs: every Unicode control character is stripped, the text is capped at 512 characters (MAX_REASON_CHARS), and a result that comes back empty is recorded as no rationale at all. - A record of what was attempted, if you ask for it. The node's trail holds accepted commands
only, so a refusal reaches it never — it was refused before a byte left the MCP process. Launch
the server with
--traceand it appends one JSONL entry per tool call under<project>/settings/state/agent/: the tool, the profile, the arguments and a verdict ofok,preview,refusedorerror(crates/vike-cli/src/cmd/mcp_trace.rs). It is off unless the flag is given, and it records no secret — credential-shaped argument keys, and credential-shaped SHOUTING_CASE identifiers inside string values, are both redacted — and, for a gated write, the preview token's identity rather than its bytes.
market_exit is the panic button
market_exit cancels every live order and then flattens every net position (optionally scoped
to one venue; a hedge-mode long/short leg has no net bucket and is skipped). It is preview-gated
like every other write, but treat it as the incident tool it is.
Guides
Task-shaped pages — arm a venue, connect an agent, record a feed, replay a session, stop and liquidate, triage a stuck order. Each one is a thing you came here to do, start to finish.
Arm one venue for demo
Two gates stand between a credential and a live socket, and the ceiling is the one consulted first.