MCP server
Explanation — what & why
Vectros ships an official Model Context Protocol (MCP) server
(@vectros-ai/mcp-server) that connects MCP-aware agents — Claude Desktop,
Cursor, Cline, Continue, VS Code, and hosted agent platforms — directly to a
tenant's hybrid search, structured records, documents, folders, and in-perimeter
inference. Drop one line into the agent's config and the model can search the
indexed corpus, query and write records, ingest documents, and ask questions
grounded against documents — with no custom integration code.
The MCP server is a thin shim over the Vectros SDK: partner-observable behavior matches the SDK and the underlying HTTP API one-to-one. Tenant isolation and scope enforcement all hold — running an MCP server does not expand a credential's reach. What it does is make that reach available to the agent as callable tools.
Two properties are worth understanding up front:
- Data-plane tools only. The server exposes exactly 21 tools, and every one of them wraps a Vectros data-plane operation. There are no web-search or third-party agent tools — none are present, by design. An agent driven through this server reaches your tenant's data and nothing else.
- Built for the context window. A tool result is injected straight into the model's context, a fundamentally different consumption shape than a UI response. The server applies deliberately smaller default limits than the HTTP API to protect the window and the cost.
How-to
Install and configure (stdio — the desktop case)
Drop this into your MCP client config (Claude Desktop:
claude_desktop_config.json; Cursor / Cline / Continue have equivalents):
{
"mcpServers": {
"vectros": {
"command": "npx",
"args": ["-y", "@vectros-ai/mcp-server"],
"env": {
"VECTROS_API_KEY": "ssk_live_..."
}
}
}
}
Restart the client. That is the whole install. The server runs as a stdio subprocess of the client; JSON-RPC over stdin/stdout is the wire protocol. No ports, no URLs, no auth ceremony.
VECTROS_API_KEY accepts any of the three credential types, but a scoped
permanent key (ssk_*) is the right shape for a desktop install — the blast
radius of a root key on a desktop is too broad.
Provision the credential in one command
You do not have to hand-build that ssk_*. The companion CLI mints a
least-privilege scoped key and its access profile and merges the config entry
above for you — no root key, no manual portal steps:
npx -y @vectros-ai/cli bootstrap
See cli.md for the full bootstrap flow. The minted key is data-plane only and per-machine.
Install and configure (HTTP — the hosted case)
For self-hosted scenarios — running the server behind a network boundary, sharing it across agent instances, deploying as a sidecar — the same package exposes a second binary that speaks Streamable HTTP instead of stdio:
VECTROS_API_KEY=ssk_live_... \
VECTROS_MCP_HTTP_PORT=8765 \
VECTROS_MCP_HTTP_BEARER_TOKEN=$(openssl rand -hex 32) \
npx -y -p @vectros-ai/mcp-server vectros-mcp-server-http
The server listens on 127.0.0.1:8765 by default. A bearer token is optional on
localhost but required to bind a non-loopback host — without one, anyone who
can reach the port could call Vectros with your credentials, so the server
refuses to start (override with VECTROS_MCP_HTTP_ALLOW_INSECURE=1, which is not
recommended). DNS-rebinding protection is built in; set
VECTROS_MCP_HTTP_ALLOWED_HOSTS to your public hostname(s) when running behind a
reverse proxy.
Constrain which tools an agent gets
Narrow the catalog at install time with VECTROS_MCP_TOOLS — a comma-separated
list of tool names (e.g. "hybrid_search,rag_ask"). This gives an agent
read-only search without exposing writes or inference cost. Unknown tool names
fail fast at startup with a clear error.
Reference
The 21 tools
Every tool wraps a Vectros data-plane operation. Records, documents, and folders
each have a full create / read / update / delete / query set; search and
inference round it out; list_schemas, current_identity, lookup_principal,
and version_history are discovery and history.
| Tool | Purpose |
|---|---|
hybrid_search | Keyword + dense ranking across indexed documents and records. Narrow by ownership, folder, type, metadata filters, date window, keyword precision, and relevance floors. |
list_schemas | Discover the available record / document / identity types (filter by surface, or resolve one by type name). |
current_identity | Describe the credential's tenant and principal scope. |
lookup_principal | Resolve a user / org / client by your own externalId (to its Vectros id, for the ownership filters) or by a schema lookup field. Read-only. |
record_create | Create a structured record. Idempotent by externalId; optional per-record indexMode. |
record_get | Fetch a record by id. |
record_update | Update a record. |
record_delete | Delete a record. |
record_query | Look up (exact / composite / range / prefix, ascending or descending) or list records by type — see record_query supports composite (multi-field) lookups below. |
document_ingest | Create a document — inline text, or a local file upload. Idempotent by externalId; optional schemaId + payload for a typed, lookup-queryable document. |
document_get | Fetch a document by id (metadata; optional text; optional presigned file download URL). |
document_update | Update a document. |
document_delete | Delete a document. |
document_query | Look up (exact / range / prefix, ascending or descending) or list documents. |
document_ask | Ask a question scoped to a single document. |
folder_create | Create a folder. |
folder_update | Update a folder. |
folder_delete | Delete a folder. |
folder_query | Look up or list folders (paginated). |
rag_ask | Ask a question grounded against the indexed corpus. Scope retrieval (ownership, folder, type, metadata filters, date window) and steer generation (instructions, temperature). |
version_history | Read the change history (create / update / delete, with actor and diff) of a record or document. Read-only. |
There are deliberately no web, search-the-internet, or third-party agent tools — the server's reach is your tenant's data plane only.
Read-only resources
Alongside the tools, the server exposes two read-only MCP resources for ambient context an agent can pick up without spending a tool call:
- a schema catalog resource (the same payload as
list_schemas), and - an identity resource (the same payload as
current_identity).
Transports
| Transport | Binary | Default bind | Use for |
|---|---|---|---|
| stdio | vectros-mcp-server | n/a (subprocess) | Desktop agents. |
| Streamable HTTP | vectros-mcp-server-http | 127.0.0.1:8765 | Hosted / shared / sidecar deployments. |
document_ingest is dual-mode
Pass text for an inline-body ingest, or filePath for a local-file upload —
exactly one; both is an error.
textmode works on both transports.filePathmode is stdio-only. The HTTP transport rejectsfilePathat validation time, because a remote server cannot read the caller's local filesystem. On HTTP, ingest text inline or call the SDK's upload method from your own code.- On stdio, an uploaded path is jailed to a configured ingest root
(
VECTROS_MCP_INGEST_ROOT, else the server's working directory). Paths that escape the root (traversal, absolute, or symlink) or match a sensitive-file pattern (SSH/AWS/credential files,.env, and similar) are refused before any bytes are read. - File uploads are asynchronous: the tool returns with status
PENDING_INDEX; polldocument_getuntil it reportsINDEXED. - Idempotent by
externalId. Pass a stableexternalIdand re-ingesting the same one returns the existing document instead of creating a duplicate — the same semanticsrecord_createhas, so a retried ingest is safe. - Typed documents. Pass
schemaId+payloadto bind the document to a schema; its declared lookup fields then become directly queryable viadocument_query, exactly like records.
record_query supports composite (multi-field) lookups
A schema can declare a lookup over 2–3 fields together (see
blueprints.md); record_query queries one by comma-joining
the field names in field (e.g. "status,area") and passing values — one
value per field, in the schema's declared order — or a single value to match
just the first named field, which groups the results by the rest. Supplying
fewer values than the lookup declares is allowed only as a leading run of
the declared fields (the first field alone, the first two, and so on — never a
later field alone). A composite lookup is exact-match only: from/to/prefix
don't apply. sortFrom/sortTo narrow an exact match to a window of the sort
key, but on a composite lookup need a value for every declared field — a
partial (grouped) match has no single ordering to window. Document, user, and
entity lookups remain single-field only; composite lookups exist for records
only.
Context-window protection
Because tool results land directly in the model's context, the server caps results more tightly than the HTTP API:
| Tool | API default | API max | MCP default | MCP max |
|---|---|---|---|---|
hybrid_search | 10 | 50 | 3 | 50 |
record_query | 100 | 100 | 3 | 100 |
rag_ask retrieval | — | — | 5 | 50 |
hybrid_search pages with offset (skip the first N hits). record_query
pages with the cursor envelope instead — pass the nextCursor a page returns
back as startFrom to fetch the next one, and stop only when nextCursor
comes back null; a full page is not itself a signal to keep going, and a
hand-built startFrom is rejected. rag_ask retrieval does not page at all —
limit is a one-shot cap on how many passages ground a single answer, not an
offset you advance; ask again with different scoping filters to change what's
retrieved. Agents that want a full document fall back to document_ask for
question-driven extraction rather than dumping text into context.
Long-running calls stay alive
rag_ask and document_ask can run tens of seconds. MCP clients have
tool-execution timeouts that would otherwise fire mid-generation. The server
emits an MCP progress notification for each streamed chunk, keeping the
JSON-RPC connection warm; the final aggregated answer arrives as the tool result.
Clients that surface progress show the answer building; clients that ignore it
still receive the complete answer.
Startup credential validation
On connect, the server performs a credential check so a bad key fails fast as a
startup error rather than opaquely failing on the first tool call. Disable it
with VECTROS_MCP_SKIP_PING_VALIDATION=1 (useful in CI where the API is not
reachable).
Credential resolution
The server takes its key from the first source that supplies one:
VECTROS_API_KEY— always wins when set.- The
vectrosCLI keyring — if the key is unset and the CLI is onPATH, the server runsvectros keyring show --format rawas a subprocess and uses the key it prints (the active entry, orVECTROS_KEYRING_ALIASif set). One credential store, shared by the server, scripts, and hooks — no plaintext copy per tool. Same pattern asgit credential/aws credential_process. - Neither — startup fails with guidance naming both options.
The key is held in memory only and never logged; startup logs the resolved alias
so you can see which identity the server is running as. vectros keyring doctor
shows that view, including which entries hold live keys (the ones that act on
real data).
Startup warns when it picks an identity you didn't name — that is, when
VECTROS_API_KEY is unset and no VECTROS_KEYRING_ALIAS is given, so the key comes
from whichever entry vectros switch last made active. That is easy to arrive at by
accident: a blank placeholder ("VECTROS_API_KEY": "" in a client config, or
-e VECTROS_API_KEY passing an unset var through Docker) looks unconfigured but
resolves like an unset key. The warning does not depend on which key turns up —
silently getting a ssk_live_* key (acting on real data) and silently getting a
ssk_test_* one (not acting on it, when you thought you were) are both surprises.
Nothing is blocked: name an entry with VECTROS_KEYRING_ALIAS, or set
VECTROS_API_KEY, and the warning stops. vectros keyring doctor shows which entry
is active and which keys are live.
Environment variables
| Variable | Required | Default | Purpose |
|---|---|---|---|
VECTROS_API_KEY | no* | — | Vectros credential. Recommended: ssk_*. *Required unless the vectros CLI is installed with a usable keyring entry: when this is unset, the server resolves the key by running vectros keyring show --format raw (see Credential resolution). Takes precedence when set. |
VECTROS_KEYRING_ALIAS | no | (the active entry) | Resolve this vectros keyring entry instead of the active one. Ignored when VECTROS_API_KEY is set. |
VECTROS_API_BASE_URL | no | https://api.vectros.ai | API base URL. Use the staging URL for the staging tenant. Validated before the key is attached. |
VECTROS_MCP_TOOLS | no | (all) | Comma-separated tool filter. |
VECTROS_MCP_INGEST_ROOT | no | server cwd | Root directory document_ingest file uploads are jailed to (stdio). |
VECTROS_MCP_DEBUG | no | — | Set 1 for debug logging on stderr. |
VECTROS_MCP_SKIP_PING_VALIDATION | no | — | Set 1 to skip the startup credential check. |
VECTROS_MCP_HTTP_PORT | HTTP only | 8765 | Listen port. |
VECTROS_MCP_HTTP_HOST | HTTP only | 127.0.0.1 | Bind address. |
VECTROS_MCP_HTTP_BEARER_TOKEN | HTTP only | — | Client→server bearer token. Required to bind a non-loopback host. |
VECTROS_MCP_HTTP_ALLOWED_HOSTS | HTTP only | — | Extra allowed Host header values (DNS-rebinding protection). |
VECTROS_MCP_HTTP_ALLOWED_ORIGINS | HTTP only | — | Extra allowed Origin header values. |
VECTROS_MCP_HTTP_ALLOW_INSECURE | HTTP only | — | Set 1 to bind a non-loopback host without a bearer token (not recommended). |
Notes & limits
- Exactly 21 data-plane tools; no web/agent tools. The server cannot reach the public internet or any non-Vectros service.
document_ingestfile upload is stdio-only. Use text mode (or the SDK) on HTTP.- One upstream credential per process. The HTTP transport uses its
VECTROS_API_KEYfor all upstream calls; the incoming HTTP bearer token is for client→server auth only, not mapped to a per-request Vectros credential. Deploy one server per credential boundary you want to enforce. current_identityreturns the full identity shape — tenant, environment, principal type, principal key id and label, and (for scoped credentials) the allowed actions and data scope ({ userId, scopes[] }). It degrades to a derived-only shape only when no upstream call can be made (e.g. a test mock).rag_ask/document_askaggregate before returning. The full answer is assembled server-side (progress notifications cover the latency); native tool-level streaming is not yet exposed.
Where to go next
- cli.md —
bootstrapprovisions thessk_*this server runs on. - sdk.md — the SDK the tools wrap, for when you want code instead of an agent.
- blueprints.md — declare the schemas the agent will read and write.
- The blueprint walkthroughs — narrated builds where an agent drives a provisioned blueprint over MCP.