Authentication
Every request carries a credential. The server never supplies one on your behalf, and here is why.
Accepted formats
Three, all sent in the Authorization header and all forwarded unchanged to the
API, which is the sole authority on whether they are valid.
| Scheme | Value | Where it comes from |
|---|---|---|
ApiKey | ApiKey <orgUUID>:<64-hex> | Dashboard, vulnetix auth login, or vulnetix_auth_poll |
Bearer | An opaque Vulnetix API token | Identity provider |
Bearer | A vdb_token session JWT | Web session exchange |
ApiKey is what you want in a client config. It does not expire.
~/.vulnetix/credentials.json you will get half of what the
header needs. The header wants ApiKey <orgId>:<hex>: organisation UUID, colon,
digest.Signing in from inside the agent
If you have no key to hand, three tools handle it without leaving the conversation. They work when nothing is authenticated, which is the point.
vulnetix_auth_start → returns a URL, a short code, and an opaque handle
vulnetix_auth_poll → redeem the handle once you have approved
vulnetix_auth_status → which organisation this session is acting as
Ask your agent to start a login. It returns something like:
https://www.vulnetix.com/cli-login-code?user_code=5HWL-6L4B
Open it, check the code on screen matches the one the agent showed you, and
approve. The agent then calls vulnetix_auth_poll and hands you a ready-formed
header value.
What the approval page shows
The page names the application that asked. Because the MCP server runs the same grant that the CLI does, it forwards your client’s name so you are not told “the Vulnetix CLI wants access” while approving something else.
That name is unverified, because the endpoint that starts a grant is public, so anyone can claim any name. The page says as much next to it. Only approve a sign-in you just started yourself.
Where to keep the key
Treat it as a password: anyone holding it acts as your organisation, at your plan tier, against your quota.
Prefer a file your client already treats as private: ~/.claude.json,
~/.cursor/mcp.json, ~/.codeium/windsurf/mcp_config.json. These are outside
the repository and are not committed.
In a repository, use your client’s secret indirection. VS Code has
inputs; several clients expand ${env:VAR}. Both are covered per client in the
tutorials.
.mcp.json
and .cursor/mcp.json are ordinary files that get committed by reflex. If you
have already pushed one, rotate the key and assume it is public.For CI, use the platform’s secret store and inject at runtime. See Agents in CI.
Rotating
Rotate from the dashboard, or run vulnetix auth login again to mint a fresh
credential. Update the header in each client config; the server holds no session,
so the new value takes effect on the very next request with no reconnection
dance.
Rotate immediately if a key has ever appeared in a committed file, a shared terminal transcript, a screenshot, or a conversation you exported.
Why there is no anonymous mode
Quota is metered per organisation. A shared MCP server that fell back to one built-in credential would put every anonymous caller into a single bucket, the same bucket every Vulnetix CLI user in the world already draws from, where one runaway agent loop would deny service far beyond this deployment.
So the server has no fallback. You bring a credential and are metered as yourself. Free Community keys take about a minute to get.
When authentication fails
A request with no credential is not rejected at the transport, because the auth tools have to work precisely when there is nothing to authenticate with. Instead the tool that needed the API returns an error result naming the problem:
Vulnetix rejected the credential (HTTP 401). Set an
Authorization: ApiKey <orgId>:<hex>header on the MCP endpoint, or runvulnetix_auth_startand thenvulnetix_auth_pollto obtain one.
Troubleshooting covers each case.