
Security News
PHP Foundation Says Chainguard Broke Security Embargo Ahead of Emergency Patches
The PHP Foundation says Chainguard broke a security embargo ahead of critical RCE patches, as leaks disrupted the coordinated release.
The Veyora command line: a zero-dependency MCP client for the Veyora research server, with a stdio bridge for MCP hosts that only launch local servers.
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.
npm install -g veyora
veyora login
Create an access token in Settings → Integrations, then paste it at the
prompt (typing is hidden; veyora login --token with no value asks the same
way). 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; a token the server refuses is said to be refused, and nothing is stored.
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.
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] [--after DATE] [--before DATE]
veyora browse <lens> [--category C] [--limit N] # movers, most_active, resolving_soon, newly_listed
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] [--cursor-file PATH]
veyora export history <ticker> [--period minute|hour|day] [--from ISO] [--to ISO] [--cursor C] [--all] [--cursor-file PATH]
veyora export tape <ticker> --kind trades|books|candles [--period minute|hour] [--from ISO] [--to ISO] [--cursor C] [--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 own text, on a terminal or a pipe and ahead of a
configured format; beside --format or --json it is a usage error, and under
it an error goes to stderr alone), --server, --token, --no-color. veyora help <command> prints a manual page: name,
synopsis, description, options, examples and exit status.
A flag the command does not take is refused (exit 2, code usage), never
ignored: veyora search rate --limt 5 says that veyora search has no --limt
option, asks "Did you mean --limit?" when exactly one of the command's flags or
the global flags is within two edits (never one reached only by rewriting every
character, so export --n is not pointed at --to), and names
veyora help search. It is refused before the server or a token is read. So is a
switch given a value (--all=yes), and --version N beside any command but
analyze; a bare --version prints the version. Each command's flags are the
ones its manual page lists, which are also exactly what the shell completions
offer. -- ends the flags: everything after it is an argument
(veyora search -- --weird).
A flag that takes a value, given none, is refused the same way (exit 2,
code usage), never read as absent: at the end of the line, before another
flag or --, empty after =, or only whitespace (--token " ", a token file
holding just a line ending). An empty --format= chooses no format, so its
refusal goes where any other does: on a pipe, the usage object on stdout.
veyora search fed --category says that
veyora search takes --category with a value: --category C, with the
placeholder its manual page writes; a bare --server used to talk to the
default server and now talks to none. Which flags take a value is the same
table: the ones a usage line writes with a placeholder, and the global
--format, --server and --token. A value may begin with one dash
(--limit -5 reaches the range check, a cursor or a path may start with -);
one beginning with two goes after = (--category=--x). The one exception is
veyora login --token with no value, which asks for the token at the hidden
prompt as veyora login does, so a token need never be typed into shell
history; an empty answer is exit 2, and --token= or a --token of only
spaces is refused.
Formats. md writes Markdown tables; csv writes a listing's table and
nothing else (search, browse, tracked, history, book, trades, settled, export, plans, credits,
jobs and notebooks — another command is a usage error; a listing with no row
writes the header row alone and says why on stderr, exit 0); 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 search matches one phrase inside each listed market's question or
ticker. --after DATE and --before DATE keep markets resolving in
[after, before), in UTC: a date YYYY-MM-DD (00:00 UTC that day) or an ISO
instant with its zone (2026-10-01T14:00:00Z); one month is the 1st to the 1st
of the next (--after 2026-10-01 --before 2026-11-01). Either may be given
alone. They travel as typed, as resolves_after and resolves_before; the
server applies the window before the rows are cut to the busiest, states it
above the rows, and refuses a value it cannot read in its own sentence (exit 1).
veyora browse finds markets with no words to search for, through Explore's
four measured lenses: movers (the top 20 by today's move, thin markets
excluded), most_active (the top 20 by 24-hour volume), resolving_soon
(contested markets resolving within 14 days, thin markets excluded) and
newly_listed (opened in the last 7 days, thin markets excluded). It prints the
lens's rule as the server states it, then the rows with the listing's stamp;
--category keeps one desk before the lens is applied. A lens is a measurement,
never a recommendation. A server older than this command has no lenses and says
so; veyora search works there.
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), this machine's clock against the Date of the server's answer
(more than 300 s apart fails, with the offset measured), the token, the account
it signs in as and the scopes the token carries, 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. While a call works its progress words go to stderr, and a word
the server repeats back to back prints once.
| Code | Meaning |
|---|---|
| 0 | ok |
| 1 | the tool answered with an error — a plan lock, insufficient credits, an outage, an unknown market — or the server does not offer the tool (it predates this version); its sentence is on stderr |
| 2 | usage: an unknown command or option, a missing argument, a value out of range |
| 3 | sign-in needed: no token for the server, or the server refused the one sent (401) — the sentence says which, where a refused token came from, and how to replace it there (--token, VEYORA_TOKEN, or veyora login for the stored one) |
| 4 | rate-limited: a 429, or a tool that answered rate-limited (the per-account wall, or a spent budget such as export history's live reads) — the tool's own sentence, which says when to try again and what else stood in the way |
| 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":"…","error":"…"}. code is tool-error, jsonrpc,
unknown-tool (the server does not offer that tool: it predates this command)
(exit 1),
usage, config-invalid, exists (exit 2), auth (with reason: missing
when no token reached the command, refused when the server refused it),
forbidden (exit 3), rate-limited (exit 4, with retryAfter in seconds when
the server named one, and a tool's structured error as structured),
unavailable, handshake, empty, shape, open-failed or unexpected
(exit 5). Branch on the exit code, which is the stable signal.
veyora init)veyora init <host> adds a veyora entry to the host's config file for the
server this command resolves (--server, else VEYORA_SERVER, else
veyora config set server, else https://veyora.ai). It never writes a token:
| Host | File | Entry |
|---|---|---|
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.
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 — the same table the command
line enforces, so it never offers a flag a command refuses — the global flags,
init's hosts, browse's lenses and the shells, and nothing that needs the
network.
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, even when the bridge cannot start (no token,
a bad --server: the sentence goes to stderr); diagnostics go to stderr. A 401
is answered with the refused-token sentence, naming where the token came from —
an MCP host that sets VEYORA_TOKEN (Claude Desktop's bundle does) needs its
own token setting changed, not veyora login. 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. A host's
notifications/cancelled is passed through as a cancel: it aborts that
request's HTTPS request, which closes the stream the server answers on (the
server's own cancel), and nothing is written for that id; it is not POSTed,
and a cancel naming initialize, a finished request or an unknown id changes
nothing. 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>".
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.
In the Veyora source repository (not part of this package):
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.
FAQs
The Veyora command line: a zero-dependency MCP client for the Veyora research server, with a stdio bridge for MCP hosts that only launch local servers.
The npm package veyora receives a total of 35 weekly downloads. As such, veyora popularity was classified as not popular.
We found that veyora demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
The PHP Foundation says Chainguard broke a security embargo ahead of critical RCE patches, as leaks disrupted the coordinated release.

Security News
PHP releases emergency patches for two critical RCE vulnerabilities as agencies urge organizations to take unpatched sites offline.

Threat Research
/Security News
A supply chain attack compromised LiteLLM 1.107.0 on PyPI and Docker, spreading malware that steals cloud credentials, developer secrets, and AI provider keys.