vike
Client setup

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 clientHow the command is spelledPage
Claude Desktopclaude_desktop_config.json, command + argsClaude Desktop
Claude Codeclaude mcp add vike-trader -- vike-cli mcpClaude Code
Cursormcp.json, command + argsCursor
Gemini CLIgemini mcp add, the default (stdio) transportGemini CLI
GitHub Copilot in VS Code.vscode/mcp.json, "type": "stdio"VS Code
Codex CLIconfig.toml, an [mcp_servers.*] table — TOML, not JSONCodex CLI
Amazon Q Developer CLI~/.aws/amazonq/mcp.json, command + argsAmazon Q
Windsurf~/.codeium/windsurf/mcp_config.json, command + argsWindsurf
A scriptno client at all — pipe JSON-RPC into its stdinRaw 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.

FlagDefaultWhat it reaches
--addr127.0.0.1:7878the vike-datahub server the backtest, sweep, walk-forward and inventory tools run against
--nodenonethe 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:

FlagDefaultWhat it decides
--profilefullwhich tools this server serves at all — full (17), read-only (10, no order-write tools) or offline (4, nothing that opens a socket)
--deny-toolnoneone more tool withheld by name; repeatable
--trace / --trace-diroffappend 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-cli is published as its own asset, alongside vike-tradehub, backtest, vike-datahub, vike-recorder, vike-study, tearsheet and vike, and covered by the release's SHA256SUMS, which is the upload manifest — an asset not named there is not published.
  • The consolidated vike executable. One multicall binary answers to every tool name — by argv[0] when it is installed under that name, and otherwise as a verb, so vike vike-cli mcp is the same server. crates/vike/src/main.rs's TOOLS is the list of names it answers to, and crates/vike/src/lib.rs's resolve is the two shapes.
  • A checkout. cargo run -p vike-cli -- mcp works, 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:

  1. Is the server listed at all? Every client has a way to show its configured MCP servers. If vike-trader is missing, the config file was not read — usually a restart, or the wrong file.
  2. Does the command run by hand? vike-cli mcp in a terminal reads JSON-RPC on stdin, so a binary that is not on PATH, or a flag it rejects, surfaces immediately instead of as a silently absent server. Raw JSON-RPC has the one-line handshake.
  3. 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_templates tool" — 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.

On this page