Client setup
Add the local vike-cli mcp stdio server to Claude Desktop, Claude Code, Cursor, Gemini CLI, VS Code, Codex CLI, Amazon Q Developer CLI or Windsurf, or drive it from a script.
The trader server is a command your client launches, not a URL it points at. Every client on
this page ends up doing the same thing — start vike-cli mcp as a child process and speak
newline-delimited JSON-RPC 2.0 to its stdin and stdout. What differs is where the configuration
lives and how that client spells a local command.
Pick your client
| Your client | How the command is spelled | Page |
|---|---|---|
| Claude Desktop | claude_desktop_config.json, command + args | Claude Desktop |
| Claude Code | claude mcp add vike-trader -- vike-cli mcp | Claude Code |
| Cursor | mcp.json, command + args | Cursor |
| Gemini CLI | gemini mcp add, the default (stdio) transport | Gemini CLI |
| GitHub Copilot in VS Code | .vscode/mcp.json, "type": "stdio" | VS Code |
| Codex CLI | config.toml, an [mcp_servers.*] table — TOML, not JSON | Codex CLI |
| Amazon Q Developer CLI | ~/.aws/amazonq/mcp.json, command + args | Amazon Q |
| Windsurf | ~/.codeium/windsurf/mcp_config.json, command + args | Windsurf |
| A script | no client at all — pipe JSON-RPC into its stdin | Raw JSON-RPC |
These pages name the server vike-trader. Any name works; a client keys its servers by name, so
the only rule is that no two entries share one.
The data server goes in the same file
Vike's other MCP server — the hosted data endpoint — is a URL rather than a command, and every client here can hold both at once, side by side in the same config file or the same CLI. Its entries are on Data client setup. That is where the name rule bites: give the two entries different names, or the second replaces the first.
The command, and its flags
vike-cli mcp [--addr 127.0.0.1:7878] [--node <host:port>] \
[--profile full|read-only|offline] [--deny-tool NAME]... \
[--trace | --trace-dir <dir>]That usage string is USAGE in crates/vike-cli/src/cmd/mcp.rs. Its parse_config accepts those
flags plus -h/--help, which prints the usage to stdout and exits 0; anything else exits 1
with unknown argument. The --addr default is that same file's DEFAULT_ADDR.
| Flag | Default | What it reaches |
|---|---|---|
--addr | 127.0.0.1:7878 | the vike-datahub server the backtest, sweep, walk-forward and inventory tools run against |
--node | none | the running vike-tradehub node node_snapshot reads and the seven order-write tools write to |
Both are optional and both only widen what the tools reach. With neither, the four offline
authoring tools — validate_strategy, discover_params, list_templates, list_indicators —
still work, which makes a flag-less entry a reasonable first step. --node has no default on
purpose: without it, node_snapshot returns a clean error naming the flag before any socket is
opened — Server::ensure_observe and Server::ensure_control in
crates/vike-cli/src/cmd/mcp.rs check for the address before they connect. A write tool says
nothing about it until much later: a call that does not carry both confirm: true and a
valid preview_token returns a preview whatever the flag says, so the missing-node error appears
only on a fully confirmed call. A preview is never evidence the flag arrived; a node snapshot is.
The other two narrow it, and they are the half a client entry is the natural place for:
| Flag | Default | What it decides |
|---|---|---|
--profile | full | which tools this server serves at all — full (17), read-only (10, no order-write tools) or offline (4, nothing that opens a socket) |
--deny-tool | none | one more tool withheld by name; repeatable |
--trace / --trace-dir | off | append one JSONL record per tool call under <project>/settings/state/agent, or in a directory you name |
Under read-only the seven order-write tools are absent from tools/list, not merely refused —
ToolAccess::new produces the advertised set and its withheld complement in one pass, so the roster
and the router cannot disagree. That makes a scoped entry the right shape for a client you want
reading a live node and never trading it:
{
"mcpServers": {
"vike-trader": {
"command": "vike-cli",
"args": ["mcp", "--node", "127.0.0.1:7879", "--profile", "read-only"]
}
}
}An unknown profile name or deny name is a usage error naming the valid set; neither ever falls back to something wider, which matters most in a config file nobody re-reads. And because a scope lives in the launch command, only whoever edits that file can widen it — the agent cannot, and the refusal it gets says so rather than sending it hunting for a switch.
A node listens on nothing until tradehub_addr in config.toml (or VIKE_TRADEHUB_ADDR) names an
address — start_observe_server in crates/vike-tradehub/src/tradehub_cli.rs returns without
binding when it is unset. 127.0.0.1:7879 is the conventional one
(crates/vike-tradehub/src/server.rs's DEFAULT_ADDR), and loopback is deliberate — reach a remote
one through an ssh tunnel rather than by binding it publicly; Remote node
is that recipe.
The node protocol is not encrypted, and the HMAC covers the connection rather than each frame
vike-cli mcp authenticates to your node with an HMAC-SHA256 nonce challenge, and the scope byte
is signed into the tag — so a key issued for reads cannot mint a write. That is the whole of what
it buys. The handshake itself is plaintext, and it authenticates the connection, not the
individual frames that follow it. Anyone who can read the traffic can read your orders, your
positions and your symbols; anyone who can modify it in flight is not stopped by the handshake
either. The primitive says so about itself, in a section headed What this does NOT buy —
crates/vike-datahub-client/src/node_auth.rs, where sign and verify live, with
crates/vike-tradehub-client/src/auth.rs's DOMAIN binding it to this service.
On loopback the party who can read that traffic is nobody but the machine's own users, which is
why the daemon binds 127.0.0.1 and why these pages steer you away from binding it publicly. For a
node on another host, an ssh tunnel (or a VPN) is not a convenience — it is the layer supplying the
confidentiality and the in-flight integrity the protocol deliberately does not.
Remote node is the tunnel recipe, including what the daemon does
when someone binds 0.0.0.0 anyway and what a dropped tunnel looks like from the tools.
The credentials
No Vike account is involved, and nothing here is metered. vike-cli mcp talks to services you
run, and the two node keys are HMAC secrets shared with your own vike-tradehub daemon:
VIKE_TRADEHUB_OBSERVE_KEY— reads (node_snapshot),VIKE_TRADEHUB_CONTROL_KEY— writes (the seven order-write tools).
Each is resolved independently, and in this order: the process environment first, then the
credential store the daemon itself reads (<project>/settings/secrets.env). The environment wins
because it is the narrower, more deliberate source — that is what makes "point at a different node
for one command" possible at all; crates/vike-cli/src/cmd/nodekeys.rs argues the precedence at
length. Values are trimmed on both sides, because the daemon trims too (from_vars in
crates/vike-tradehub-client/src/auth.rs) and a key with a trailing newline would otherwise fail
the handshake as an opaque AuthDenied.
A key present in neither place disables only the tools that need it. The server still starts, the
offline and backtest tools still work, and the affected tool returns an error naming both
locations (Server::missing_key in crates/vike-cli/src/cmd/mcp.rs). The two scopes are separate
at the node as well as here: a control key cannot authenticate a read.
A GUI-launched client inherits none of your shell's exports
Claude Desktop, Cursor and VS Code start the server as a child of the application, not of your
terminal, so an export VIKE_TRADEHUB_CONTROL_KEY=... in your shell profile is invisible to it.
Put the keys in the credential store, or set them in the client's own config where it supports an
env block. The store is also where the daemon reads them, so one copy serves both halves.
vike-cli secrets path prints which store this project resolves to; vike-cli secrets list shows
whether a key is in it, by name — never values.
Where the binary comes from
Every client entry starts with a path to vike-cli, so it has to exist and be findable:
- A release download.
vike-cliis published as its own asset, alongsidevike-tradehub,backtest,vike-datahub,vike-recorder,vike-study,tearsheetandvike, and covered by the release'sSHA256SUMS, which is the upload manifest — an asset not named there is not published. - The consolidated
vikeexecutable. One multicall binary answers to every tool name — byargv[0]when it is installed under that name, and otherwise as a verb, sovike vike-cli mcpis the same server.crates/vike/src/main.rs'sTOOLSis the list of names it answers to, andcrates/vike/src/lib.rs'sresolveis the two shapes. - A checkout.
cargo run -p vike-cli -- mcpworks, but a client entry wants a real path.
If your install is the container, the client cannot just run vike-cli
The image's ENTRYPOINT is the daemon's entrypoint script (deploy/docker/Dockerfile), so
reaching the CLI means overriding it and keeping stdin open (-i):
docker run -i --rm -v /srv/vike-trader:/project \
--entrypoint /opt/vike/bin/vike-cli \
vikeio/vike-tradehub:<version> mcp/opt/vike/bin/vike-cli is a relative symlink to the multicall binary — the links are the image's
interface, not a convenience. And note that 127.0.0.1 inside a container is the container's own
loopback, so a --node pointing at a daemon elsewhere needs an address that container can actually
reach. Install with Docker covers the mount.
Verify the connection
One prompt, and it proves the whole path — the client launching the binary, the protocol, and tool dispatch — rather than just the config file being read:
list the vike strategy templates
That calls list_templates, which is offline: no datahub, no node, no key, no network. If it
answers, the wiring is correct and anything still failing is an address or a key rather than the
setup. With a node configured, follow it with "show me the node snapshot" to prove the observe
key too.
If nothing happens, three checks in order:
- Is the server listed at all? Every client has a way to show its configured MCP servers. If
vike-traderis missing, the config file was not read — usually a restart, or the wrong file. - Does the command run by hand?
vike-cli mcpin a terminal reads JSON-RPC on stdin, so a binary that is not onPATH, or a flag it rejects, surfaces immediately instead of as a silently absent server. Raw JSON-RPC has the one-line handshake. - Did the tools load but not get used? Some clients require you to enable a server's tools per
conversation. Name the tool — "use the
list_templatestool" — to force the issue.
Removing it
There is nothing to revoke centrally, because there is no account: delete the entry from the
client's config, or rotate the node keys in <project>/settings/secrets.env and restart the
daemon. A client holding the old key then fails the handshake.
Once it is connected, Trader MCP server is the tool-by-tool reference — and the seven order-write tools have their own contract on Live node.
Trader MCP server
17 tools over a local stdio server — author and backtest Rhai strategies, read a live trading node, and place orders behind a mandatory preview gate, or fewer tools under a profile that withholds the writes outright.
Claude Desktop
Declare vike-cli mcp as a local command in claude_desktop_config.json, and get the node keys somewhere a GUI-launched process can actually see them.