# 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):

```json
{
  "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:

```bash
npx -y @vectros-ai/cli bootstrap
```

See [cli.md](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:

```bash
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 / range / prefix, ascending or descending) or list records by type. |
| `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.

- **`text` mode** works on **both** transports.
- **`filePath` mode is stdio-only.** The HTTP transport rejects `filePath` at
  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`;
  poll `document_get` until it reports `INDEXED`.
- **Idempotent by `externalId`.** Pass a stable `externalId` and re-ingesting the
  same one returns the existing document instead of creating a duplicate — the
  same semantics `record_create` has, so a retried ingest is safe.
- **Typed documents.** Pass `schemaId` + `payload` to bind the document to a
  schema; its declared lookup fields then become directly queryable via
  `document_query`, exactly like records.

### 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** | **10** |
| `record_query` | 100 | 100 | **3** | **10** |
| `rag_ask` retrieval | — | — | **5** | **10** |

Agents that need more search hits paginate with `offset`. 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).

### Environment variables

| Variable | Required | Default | Purpose |
|---|---|---|---|
| `VECTROS_API_KEY` | yes | — | Vectros credential. Recommended: `ssk_*`. |
| `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_ingest` file upload is stdio-only.** Use text mode (or the SDK)
  on HTTP.
- **One upstream credential per process.** The HTTP transport uses its
  `VECTROS_API_KEY` for 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_identity` returns a minimal shape today** and gains richer fields
  (allowed actions, data scope) automatically as the backend rolls them out — no
  server release needed.
- **`rag_ask` / `document_ask` aggregate 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](cli.md) — `bootstrap` provisions the `ssk_*` this server runs on.
- [sdk.md](sdk.md) — the SDK the tools wrap, for when you want code instead of an
  agent.
- [blueprints.md](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.
