veyora — the Veyora command line
A zero-dependency MCP client for the Veyora research server, with a human face
and a stdio bridge. Node 18 or later; ESM; nothing to install beyond the package.
Every command is a tools/call on the Veyora MCP server (/api/mcp). The
command line uses the same access tokens and the same plan as the MCP server:
a read the plan does not carry says so, and ask is charged to the account's
credits exactly as a chat message is.
Install and sign in
npm install -g veyora
veyora login
Create an access token in Settings → Integrations, then paste it at the
prompt (typing is hidden). The token is checked with get_account and stored at
$XDG_CONFIG_HOME/veyora/credentials.json — else %APPDATA%\veyora\credentials.json
on Windows, else ~/.config/veyora/credentials.json — mode 0600, keyed by server
origin. veyora logout forgets the local copy; the token itself stays live
until revoked in Settings. veyora doctor checks the whole setup.
Token precedence: --token TOKEN > VEYORA_TOKEN > the stored file.
Server precedence: --server URL > VEYORA_SERVER > veyora config set server > https://veyora.ai.
Commands
veyora login [--token TOKEN] [--server URL]
veyora logout
veyora account # plan · credits · limits · this token's name and scopes
veyora usage # this calendar month: credits spent and left, news refreshes
veyora credits [--limit N] [--before CURSOR] # the account's credit ledger, a page at a time
veyora plans # every plan, price, allowance and credit pack (a listing)
veyora search <query> [--category C] [--limit N]
veyora market <ticker>
veyora history <ticker> [--range 1D|1W|1M|3M|ALL]
veyora book <ticker> [--depth N]
veyora trades <ticker> [--limit N]
veyora related <ticker>
veyora news <ticker>
veyora tracked
veyora settled [--series S] [--limit N] [--since ISO] [--cursor "before,beforeTicker"] [--candles] [--all]
veyora export history <ticker> [--period minute|hour|day] [--from ISO] [--to ISO] [--all] [--cursor-file PATH]
veyora export tape <ticker> --kind trades|books|candles [--period minute|hour] [--from ISO] [--to ISO] [--all] [--cursor-file PATH]
veyora analyses
veyora analyze <id> <ticker> [--param key=value ...] [--version N] [--async]
veyora analyze <id>,<id>... <ticker> --async # several analyses on one market, a job each
veyora notebooks # the starter notebooks, and which this server can run
veyora notebook <id> <ticker> [--async] # run a starter notebook on the server, where enabled
veyora ask <ticker> "<question>" [--depth quick|standard|deep] [--experience beginner|amateur|expert] [--async]
veyora jobs [--limit N] # this account's jobs, newest first
veyora jobs result <handle> # one finished job's result
veyora jobs cancel <handle> # ask a working job to stop
veyora wait <handle> [<handle> ...] [--timeout SECONDS]
veyora tools # tools/list as the server states it
veyora mcp # stdio ↔ HTTPS bridge
veyora init <claude-code|vscode|cursor|windsurf> [--global] [--force] [--dry-run]
veyora open <ticker> [--kalshi] [--print] # Veyora's page for the market, or the contract on Kalshi
veyora config [get <key> | set <key> <value> | unset <key> | path]
veyora doctor # check Node, the files, the server, the token and the output
veyora completion <bash|zsh|fish> # a shell completion script
veyora help [command] · veyora --version
Global flags: --format table|json|md|csv (--json is --format json; the
default is your configured format, else a table on a terminal and JSON on a
pipe), --text (the tool's plain-text answer instead of a table), --server,
--token, --no-color. veyora help <command> prints a manual page: name,
synopsis, description, options, examples and exit status.
Formats. md writes Markdown tables; csv writes a listing's table and
nothing else (search, tracked, history, book, trades, settled, export, plans, credits,
jobs and notebooks — another command is a usage error); notebook writes a
Note,Value block (as_of, delayed, the number of tables) and then each table
the run displayed, with RFC 4180 quoting and the
minus as ASCII so a spreadsheet reads it. Colour marks only a movement's sign:
--no-color > NO_COLOR (set and not empty) > FORCE_COLOR (0 or false
turns it off) > veyora config set color > a terminal; json, md and csv never
carry it.
veyora market reads one market at a glance: the question, the probability
with its price, today's move (or that no prior close is on file), a sparkline of
the week's closes with its window and range stated beside it (a gap breaks it,
and a narrow range is drawn across at least 8 points rather than stretched), the
book's cost, the outcome family, every stamp, delayed, each block that failed
to load named as a failure, and Open on Kalshi with the quote's time. A pipe
still gets get_market's structured content, unchanged.
veyora ask streams on a terminal: one quiet "thinking…" line on stderr
while the analyst reasons, then the answer written as it arrives (the server
sends the new text in its progress notifications, under
_meta["ai.veyora/delta"]). A pipe, --json or --text gets the finished
answer. --depth and --experience fall back to veyora config values.
veyora config shows every effective setting and where it came from
(server, token — masked to its last four characters — format, colour, the
analyst's depth and experience, the two files); set, get and unset change
config.json beside the credentials (mode 0600). It never holds a token.
veyora doctor checks Node, the credentials file (present, 0600, readable)
and the settings file, the server (and that it answers as an MCP server, and
offers sign-in), the token, the account it signs in as, the protocol era, the
tools offered and the output. It writes nothing; it exits 0 when everything
passes, 5 when the server could not be reached, 3 when sign-in failed, 1
otherwise.
veyora open opens Veyora's page for a market (no token needed), or with
--kalshi the contract's own page on Kalshi. It only ever opens the server's
address or kalshi.com, prints the address either way, and --print opens
nothing.
settled --all walks the next cursor up to ten pages and says so when it stops
short. settled --cursor-file PATH makes the walk resumable: after each page the
next cursor and the query it belongs to are written to the file, running the
same command again continues from it, a file for a different query is refused,
and a finished walk says so; with --format csv a resumed run writes no header
row, so >> rows.csv builds one file. export (where the server enables bulk
export) pages a market's candles or its recorded tape — trades, order-book
snapshots as one row per side and level, or candles — oldest first, the rows
written exactly as the server sent them; --all walks twenty pages and names
the cursor to continue from — --cursor C (a run given --cursor writes no
CSV header, so >> appends) or --cursor C --cursor-file PATH to make the rest
resumable — and --cursor-file resumes the same way settled does. A cursor
file never records a page before its rows are written: an error mid-walk prints
the rows read, exits non-zero, and the same command continues with the next
row. With --format csv the server's summary line (coverage, delayed, a cap,
a market never captured) goes to stderr, and a page that left rows out with no
cursor to follow is said, never reported as finished. credits prints the next page's command, with the server's cursor
verbatim. plans prints each price as the server's own text — the command line
formats no money — and buys nothing: plan changes and credit packs happen in
Settings → Plan & billing, signed in. analyze --param values are coerced: true/false become booleans, plain
numbers become numbers, anything else stays text.
--async starts the work as a JOB and prints its handle at once instead of
holding the terminal while it runs; veyora wait <handle> waits for it (calling
the server again every 25 s at most, for up to --timeout seconds, default 600)
and prints the result exactly as the synchronous command would. A job and its
result are kept on the server for 24 hours, then deleted; nothing is saved to
Tools history. A handle names a job of the account it was started from and no
other. ask --async is charged when it runs, exactly as ask is.
Human output prints every stamp the server states, the word delayed for a
figure that is last-known rather than current, — for an absence, a probability
as 62% · 62¢, and the true minus sign. A movement's sign is the only thing that
takes colour.
Exit codes
| 0 | ok |
| 1 | the tool answered with an error — a plan lock, insufficient credits, an outage, an unknown market; its sentence is on stderr |
| 2 | usage |
| 3 | sign-in needed (401, an invalid or revoked token) |
| 4 | rate-limited (429; the sentence carries Retry-After) |
| 5 | the server couldn't be reached or is unavailable |
veyora wait exits 0 when every job completed and 1 when any failed, was
cancelled, was not found for this account, or was still working when
--timeout elapsed (stderr names the handles to wait on again).
With --json (or a pipe) an error also writes one object to stdout:
{"ok":false,"code":"tool-error"|"usage"|"auth"|"rate-limited"|"unavailable","error":"…"}.
Add Veyora to an MCP host (veyora init)
veyora init <host> adds a veyora entry to the host's config file for the
server the credentials file knows. It never writes a token:
claude-code | .mcp.json (this project) | the server address; run /mcp and sign in |
vscode | .vscode/mcp.json (this project) | the server address; VS Code asks you to sign in on first start |
cursor | .cursor/mcp.json, or ~/.cursor/mcp.json with --global | the bridge, npx -y veyora mcp |
windsurf | ~/.codeium/windsurf/mcp_config.json | the bridge, npx -y veyora mcp |
Claude Code and VS Code connect by signing in with OAuth. Cursor and Windsurf
run the bridge, which uses the token veyora login stored, so a bridge entry
needs that login first. Every other server in the file is kept; a file that is
not JSON is left untouched; a veyora entry that differs is replaced only with
--force; --dry-run prints the file it would write.
Settings → Integrations has the same installs as links and a download: Add to
VS Code, Add to Cursor, and veyora.mcpb for Claude Desktop, which asks for an
access token when you open it and keeps it in the system keychain. None of them
carries a token.
Shell completion (veyora completion)
source <(veyora completion bash) # or add the line to ~/.bashrc
source <(veyora completion zsh) # or save the output as _veyora on $fpath
veyora completion fish | source # or save it as ~/.config/fish/completions/veyora.fish
It completes the commands, each command's flags, init's hosts and the
shells, and nothing that needs the network.
The bridge (veyora mcp)
For MCP hosts that only launch local servers. Newline-delimited JSON-RPC on
stdin becomes one HTTPS POST per message to the remote server, with the bearer
and the mirrored headers; each answer comes back as one line on stdout. Nothing
but JSON-RPC is written to stdout; diagnostics go to stderr. The bridge speaks
both eras — a request carrying modern _meta travels with
MCP-Protocol-Version/Mcp-Method/Mcp-Name mirrored from its body; a legacy
initialize is forwarded and its negotiated version remembered for later
requests. Notifications are forwarded and answered with nothing;
notifications/cancelled is dropped. Streamed answers forward
notifications/progress lines as they arrive, then the final response. The
process exits 0 when stdin closes or on SIGTERM.
{
"mcpServers": {
"veyora": {
"command": "npx",
"args": ["-y", "veyora", "mcp"]
}
}
}
The bridge reads the token veyora login stored; a host that cannot reach that
file can pass it as "env": { "VEYORA_TOKEN": "<your token>" } instead.
Hosts that sign in with OAuth need no bridge and no token:
claude mcp add --transport http veyora https://veyora.ai/api/mcp
then /mcp in Claude Code to sign in. A host that cannot sign in can set the
header instead: --header "Authorization: Bearer <your token>".
How it speaks MCP
The first request of a process is server/discover with modern (2026-07-28)
per-request _meta. A server that speaks only the handshake era answers with
something that is not a modern result, and the client falls back to
initialize (2025-11-25) and notifications/initialized. A modern server that
refuses the version with -32022 and lists what it supports gets a version the
client speaks, else the handshake. login records the era the server answered
in; a stored legacy era skips the probe on later runs. This is what makes the
command line a real MCP client: pointed at another MCP endpoint with --server,
it works the same way.
Development
packages/cli/test/stubServer.mjs is a stand-in for POST /api/mcp that speaks
both eras and serves the catalog's tool names with fixture results:
node packages/cli/test/stubServer.mjs [--legacy] [--port N] prints its endpoint
and tokens. From the repository root, npm run cli -- <command> runs the
checked-out source.