@vectros-ai/mcp-server 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,167 @@
3
3
  All notable changes to `@vectros-ai/mcp-server` are documented here.
4
4
  This project adheres to [Semantic Versioning](https://semver.org).
5
5
 
6
+ ## 0.17.0 — 2026-09-07
7
+
8
+ ### Added
9
+
10
+ - **`record_batch_write`** — create or upsert up to 50 structured records in one call,
11
+ wrapping `POST /v1/records/batch` (API 0.43.0: previously a `501` stub, now live).
12
+ Use it instead of N `record_create` round-trips when you hold several records.
13
+ Items may mix types, and each one goes through the same schema validation,
14
+ `externalId` idempotency, unique-field enforcement and `records:c:<type>` scope
15
+ check as a single create, applied per item against that item's own type — a batch
16
+ is not a way to write a type your credential could not write one at a time.
17
+ `atomicity: "all_or_nothing"` commits every item in one transaction and writes
18
+ nothing at all if any item fails, which is a better failure shape for an agent than
19
+ a loop of creates that half-succeeds; the default `best_effort` writes each item
20
+ independently. The item vocabulary deliberately mirrors `record_create` (`type` +
21
+ `fields`), so nothing new has to be learned to use it.
22
+
23
+ **The call reports success whenever the batch was *processed* — including when
24
+ every item failed.** That is the endpoint's own contract, so the tool does not
25
+ fake an error result; read the per-item `results` (matched to what you sent by
26
+ `index`) and the `succeeded`/`failed` counts. Per-item `status` carries two values
27
+ beyond the ordinary outcomes, and they call for different remedies:
28
+ `forbidden` (your credential lacked the scope or ownership that item needed — fix
29
+ the credential, not the item) and `not_committed` (this item was fine, but an
30
+ `all_or_nothing` batch was aborted by a different one).
31
+
32
+ - **`record_query` can list by `folderId`, and serve the account-wide `recent` feed.**
33
+ Two list-mode selectors the API has carried on `GET /v1/records` for many releases
34
+ but this tool never exposed: `folderId` lists every record filed in a folder
35
+ regardless of type (combine with `type` for one type within one folder), and
36
+ `recent: true` returns the recently-updated feed across all types — the only way to
37
+ read records without naming a type. `record_create` has always accepted `folderId`,
38
+ so until now a record could be filed into a folder and never listed back out of it.
39
+
40
+ This matters more from 0.43.0 on, because `folder_delete` now refuses a folder that
41
+ still holds records and the supported way to find them is exactly this call.
42
+ `type` is consequently optional now: a lookup (`field`) still requires it, a list
43
+ with no selector at all is refused, and `recent` is refused alongside
44
+ `type`/`folderId`/`userId`/`scope` rather than silently ignoring them the way the
45
+ API does.
46
+
47
+ - **`scopeFilters` on `hybrid_search` and `rag_ask`** — narrow a search or a RAG
48
+ retrieval by more than one ownership dimension at once, e.g.
49
+ `["org:<id>", "client:<id>"]` for one specific client within one specific org.
50
+ Something `scope` alone could not express, for a credential whose access spans
51
+ several dimensions. Mutually exclusive with `scope`; passing both is refused
52
+ locally with a message naming both fields, rather than spending a round trip on
53
+ the API's own rejection.
54
+
55
+ ### Changed
56
+
57
+ - **`folder_delete` now states its emptiness precondition** — the folder must contain
58
+ no documents, **no records**, and no sub-folders, and the description says how to clear
59
+ each: a document can be moved out or deleted, a record can only be deleted (nothing on
60
+ this surface moves one), and a sub-folder cannot be re-parented, so nesting must be
61
+ cleared innermost-first. The description previously named only protected folders, so
62
+ the most common way this call fails read as impossible.
63
+
64
+ **API 0.43.0 widened the refusal, and it lands harder on this server than on most
65
+ callers.** The old guard saw sub-folders and *file-backed* documents only — it was blind
66
+ to *text-ingested* documents and to records, and a delete that hit one of those returned
67
+ success while orphaning the contents behind a folder id that no longer resolved.
68
+ Text-inline ingest is `document_ingest`'s default path, so folders this server filled are
69
+ precisely the ones whose deletes used to succeed and now do not.
70
+
71
+ One consequence worth planning for: the emptiness check counts **everything** in the
72
+ folder, including items the calling credential cannot read. A scoped credential can be
73
+ refused by content it has no way to enumerate, so a refusal that contradicts your own
74
+ listing is expected rather than a bug.
75
+ - **`rag_ask`'s `truncationWarning` now carries `truncatedCount` and `noContentCount`**,
76
+ and its `reason` can be `no_groundable_content` or `context_window_budget_and_no_content`
77
+ as well as `context_window_budget`. Results can be dropped before the prompt is built
78
+ for two independent reasons — not fitting the context window, or having no groundable
79
+ text at all — and these separate them. Inherited from the API with no code change here;
80
+ noted because it is new data an agent sees.
81
+ - **`hybrid_search`'s ARCHIVED note now covers records, and stops claiming an absolute.**
82
+ It said ARCHIVED *documents* never appear in results, which read as an asymmetry implying
83
+ archived records still match — they do not, and have refused to index since API 0.35.
84
+ API 0.43.0 additionally fixed two document write paths that could silently re-index an
85
+ already-archived document. But it deliberately does **not** sweep items already stranded
86
+ in that state: they stay searchable until written to again. So "never appear" was a false
87
+ absolute, and the description now names the repair — re-send `status: "ARCHIVED"` on the
88
+ item — instead of implying the case cannot arise.
89
+ - **A search hit's returned `createdAt` is documented as the INDEX timestamp.** It is the moment
90
+ the item entered the search index — the same moment it was created in the common case, but later
91
+ for anything re-indexed since — so it should not be reported to a user as the creation time; fetch
92
+ the item with `document_get`/`record_get` for that. The `createdAfter`/`createdBefore` FILTERS are a
93
+ separate thing and are unchanged: they filter on the item's true creation time, which does not move
94
+ when it is re-indexed, with a bounded allowance that lets an item matched on its FIRST index be
95
+ checked against its index time instead (clock skew between creating and indexing).
96
+ - **`folder_delete` no longer implies a record cannot be moved.** It said a record must be deleted
97
+ because it cannot be re-filed. The API supports re-filing one (`folderId` is patchable); it is
98
+ *this server* that does not expose it — `record_update` has no `folderId` argument. As written, an
99
+ agent clearing a folder could have deleted partner data where a move was available, then reported
100
+ that a move was impossible. It now names the gap as this server's and says to move the record
101
+ through the API/SDK rather than deleting it here.
102
+ - **`record_batch_write` no longer claims duplicate `externalId`s within one batch are refused.**
103
+ That holds under `atomicity: "all_or_nothing"` only. Under the default `best_effort` each item is
104
+ written independently, so a second item carrying the same `externalId` matches what the first just
105
+ wrote and reports `updated` — a duplicated key silently collapses two rows into one, counted as a
106
+ success. Both modes are now described.
107
+ - **`record_create` / `record_batch_write`: `scopes` is bounded by `data_scope`, not by identity.**
108
+ Both said the values "must come from the credential's own identity". The platform states the
109
+ opposite: identity supplies the DEFAULT when you state none and does not limit which value you may
110
+ state; each entry must fall inside the `data_scope` of a granting clause. The old wording would
111
+ stop an agent setting a scope it was entitled to set.
112
+ - **`record_query` rejects `folderId`/`recent` on a lookup** instead of ignoring them, matching the
113
+ existing guard for lookup arguments passed without `field`.
114
+ - **Bundled `@vectros-ai/sdk` repinned to the 0.43.0 line.**
115
+
116
+ ### Notes
117
+
118
+ - **A root `sk_*` key can no longer file into a non-`default` app context.** As of API
119
+ 0.43.0, a `folderId` / `parentFolderId` — or a document's `schemaId` — belonging to
120
+ another context is refused with a uniform `400`, on `document_ingest`,
121
+ `document_update`, `record_create`, `record_update` and `folder_create`. Those calls
122
+ previously succeeded, but never coherently: a root key's writes are stamped
123
+ `default`, so the row landed in `default` while its folder or schema lived elsewhere.
124
+ Existing rows are unaffected. Use a scoped key bound to the target context — the
125
+ credential this server recommends anyway. See README § Recommended credential.
126
+ - **A `filters` clause on `status` means something different for records now.** Records
127
+ no longer publish their lifecycle `status` into the search index (API 0.43.0), so on
128
+ `hybrid_search` / `rag_ask` a `status` filter matches a schema's own `status` field
129
+ where one is declared, and nothing where none is — it no longer collides with the
130
+ platform's lifecycle value. Record schemas can now use `status` as a filterable field
131
+ of their own.
132
+ - **0.43.0 capabilities this server deliberately does not expose:** stored scripts and
133
+ synchronous script execution, trigger rules, trigger-failure history, and usage
134
+ reporting. The reasoning is in README § What this server deliberately doesn't expose
135
+ — briefly: authoring stored code is a design-time act rather than a data-plane one;
136
+ declaring a trigger rule grants live authority; trigger-failure and usage reporting
137
+ are operational questions for a person, not mid-task tool calls; and synchronous
138
+ script execution has a failure surface (an undetermined-outcome run, and an
139
+ idempotency key a tool call has no natural identity for) that needs a deliberate
140
+ design rather than a thin wrapper.
141
+
142
+ ## 0.16.1 — 2026-08-27
143
+
144
+ ### Fixed
145
+
146
+ - **Claude Code setup docs: restart-to-load + Windows npx-resolution caveats.**
147
+ The "Configure manually (Claude Code)" section now covers three real
148
+ friction points found in dogfooding: a session already open when the
149
+ server is added needs a full quit + reopen (not just re-selecting the
150
+ tab); the `/mcp` panel shows the connector marketplace, not local stdio
151
+ servers, so it can't confirm the server loaded; and config resolves off
152
+ the git common root, so a linked worktree needs adding/opening from the
153
+ same project as its main repo. Also documents a Windows-specific trap: a
154
+ scoped-registry override for `@vectros-ai` in `.npmrc` can silently
155
+ resolve `npx -y @vectros-ai/mcp-server` to an unexpected internal build
156
+ that isn't guaranteed to run there — pin an explicit version if the
157
+ plain command fails to start.
158
+ - **`current_identity`'s `granted_capabilities` note now lists all five named capabilities.**
159
+ The tool description and the `identity` resource both listed only four
160
+ (`member-lifecycle` / `forensic-read` / `context-directory-read` / `delegate-mint`); the
161
+ platform's fifth, `delegate-principal-stamp`, is now named alongside them.
162
+
163
+ ### Changed
164
+
165
+ - Bundled `@vectros-ai/sdk` refreshed to the current release's staging build.
166
+
6
167
  ## 0.16.0 — 2026-08-27
7
168
 
8
169
  ### Added
package/README.md CHANGED
@@ -166,17 +166,32 @@ claude mcp add vectros -e VECTROS_API_KEY=ssk_live_... -- npx -y @vectros-ai/mcp
166
166
  ```
167
167
 
168
168
  Add `-e VECTROS_API_BASE_URL=https://api.staging.vectros.ai` for a non-production
169
- environment. Reopen the project in Claude Code and the Vectros tools are
170
- available.
171
-
172
- ## Tools (22 tools)
169
+ environment.
170
+
171
+ **Load it into a session by restarting.** A Claude Code session that was
172
+ already open when you added the server won't pick it up mid-session — fully
173
+ quit and reopen the project (not just re-select the tab). The `/mcp` panel
174
+ shows the connector marketplace, not locally-configured stdio servers, so it
175
+ won't confirm the server is loaded — ask the agent to call a Vectros tool
176
+ instead. Config is keyed by the **git common root**, so a linked worktree
177
+ resolves to its main repo's `.mcp.json` — add and open from the same project.
178
+
179
+ > **Windows note:** if your `.npmrc` (or a global npm config) points the
180
+ > `@vectros-ai` scope at a private registry, a bare `npx -y @vectros-ai/mcp-server`
181
+ > can resolve an unexpected internal build there instead of the public
182
+ > release — and an internal build is not guaranteed to run on Windows. If the
183
+ > command above fails to start, either remove the scoped-registry override
184
+ > for a plain `npx` run, or pin an explicit version (`npx -y
185
+ > @vectros-ai/mcp-server@<version>`) known to work.
186
+
187
+ ## Tools (23 tools)
173
188
 
174
189
  **Search & RAG**
175
190
 
176
191
  | Tool | What it does |
177
192
  |---|---|
178
- | `hybrid_search` | Hybrid BM25 + dense search across the tenant's indexed content (records + documents). Narrow by ownership, folder, type, metadata filters, a created date window, and keyword-precision (`textMode`) / relevance floors. Returns the indexed projection of each hit. |
179
- | `rag_ask` | Ask a question grounded against the indexed corpus. Scope retrieval (ownership / folder / type / metadata filters / date window) and steer generation (`instructions` / `temperature`). Streaming generation aggregated; progress notifications keep the call alive for the generation window. |
193
+ | `hybrid_search` | Hybrid BM25 + dense search across the tenant's indexed content (records + documents). Narrow by ownership (`scope` for one dimension, `scopeFilters` for several at once — e.g. one client within one org), folder, type, metadata filters, a created date window, and keyword-precision (`textMode`) / relevance floors. Returns the indexed projection of each hit. |
194
+ | `rag_ask` | Ask a question grounded against the indexed corpus. Scope retrieval (ownership — `scope` or multi-dimension `scopeFilters` — / folder / type / metadata filters / date window) and steer generation (`instructions` / `temperature`). Streaming generation aggregated; progress notifications keep the call alive for the generation window. |
180
195
  | `document_ask` | Ask a question grounded against a single document. Same aggregation + progress-notification shape as `rag_ask`. |
181
196
 
182
197
  **Records** (structured, schema-validated data)
@@ -184,10 +199,11 @@ available.
184
199
  | Tool | What it does |
185
200
  |---|---|
186
201
  | `list_schemas` | List the record-schema catalog the credential can see (filter by `surface` or resolve one by `recordType`). Makes `record_query` / `record_create` discoverable. |
187
- | `record_query` | Query records by lookup field — equality (`value`), range, or prefix, with `asc`/`desc` ordering and an optional `sortFrom`/`sortTo` window — or list mode (filter by ownership + type). Also supports composite equality across 2-3 fields (`values`), but only against a lookup the *schema* declares over those fields together (see `list_schemas`) — not any two fields you pick. |
202
+ | `record_query` | Query records by lookup field — equality (`value`), range, or prefix, with `asc`/`desc` ordering and an optional `sortFrom`/`sortTo` window — or list mode, choosing one of `type`, `folderId` (every record in a folder, any type — combine with `type` to narrow), or `recent` (the account-wide recently-updated feed across all types), then optionally filtering the first two by ownership. Also supports composite equality across 2-3 fields (`values`), but only against a lookup the *schema* declares over those fields together (see `list_schemas`) — not any two fields you pick. |
188
203
  | `record_get` | Fetch one record by id, including its full payload (large payloads truncated to protect the agent context window). |
189
204
  | `record_batch_get` | Fetch several records by id (1-100) in one call, each with its full payload. Returns `missingIds` for any requested id you can't access, since the API silently omits them. |
190
205
  | `record_create` | Create a record of a given type; idempotent by `externalId`; optional per-record `indexMode`. |
206
+ | `record_batch_write` | Create or upsert up to 50 records in one call, instead of N `record_create` round-trips. Items may mix types and are validated and scope-checked individually. `atomicity: all_or_nothing` commits them as one transaction (nothing is written if any item fails); the default `best_effort` writes each independently. Reports success whenever the batch was *processed* — read the per-item `results`, not just the absence of an error. |
191
207
  | `record_update` | Patch a record's payload (deep-merged; `null` deletes a key); optimistic concurrency via `expectedVersion`. |
192
208
  | `record_delete` | Permanently delete a record by id (leaves a tombstone). |
193
209
 
@@ -208,17 +224,17 @@ available.
208
224
  | `folder_query` | Get a folder by id, or list folders (a parent's children for tree navigation, or a flat tenant list; paginated via `nextCursor`). |
209
225
  | `folder_create` | Create a folder. |
210
226
  | `folder_update` | Update a folder's name / description / ownership (merge-patch; optimistic concurrency via `expectedVersion`; folders cannot be re-parented). |
211
- | `folder_delete` | Delete a folder. |
227
+ | `folder_delete` | Delete a folder. It must be empty first — no documents, no records, and no sub-folders — and a context root is protected outright. |
212
228
 
213
229
  **Identity & history**
214
230
 
215
231
  | Tool | What it does |
216
232
  |---|---|
217
- | `current_identity` | Describe the credential: tenantId, environment, principalType, principalKeyId, principalLabel, and (for scoped credentials) allowedActions + dataScope. Does **not** yet include `granted_capabilities` (`member-lifecycle` / `forensic-read` / `context-directory-read` / `delegate-mint`) — a separate reach dimension a scope clause can carry as of API 0.40.0 that `/v1/ping` doesn't report yet, so allowedActions + dataScope may understate a credential's true reach. Also reports this server's own version and the bundled SDK version (`mcpServerVersion`, `sdkVersion`). |
233
+ | `current_identity` | Describe the credential: tenantId, environment, principalType, principalKeyId, principalLabel, and (for scoped credentials) allowedActions + dataScope. Does **not** yet include `granted_capabilities` (`member-lifecycle` / `forensic-read` / `context-directory-read` / `delegate-mint`, as of API 0.40.0, joined by `delegate-principal-stamp` in 0.42.0) — a separate reach dimension a scope clause can carry that `/v1/ping` doesn't report yet, so allowedActions + dataScope may understate a credential's true reach. Also reports this server's own version and the bundled SDK version (`mcpServerVersion`, `sdkVersion`). |
218
234
  | `lookup_principal` | Resolve a user, or an identity entity in a namespace (`org`/`client`/any namespace you registered), by your own `externalId` (→ its Vectros UUID, for the ownership filters) or by a schema lookup field. Pass `contextId` to target a specific app context for a context-owned namespace. Read-only. |
219
235
  | `version_history` | Read the audit/version trail (CREATE/UPDATE/DELETE, with actor + diff) for one record or document. Read-only. |
220
236
 
221
- All 22 tools wrap published Vectros HTTP API endpoints. JSON
237
+ All 23 tools wrap published Vectros HTTP API endpoints. JSON
222
238
  responses are what the agent sees as tool output. Per-call cost
223
239
  surfaces via the `usage` field on inference responses.
224
240
 
@@ -260,6 +276,20 @@ AccessProfile for MCP") for least-privilege credential setup — the
260
276
  `vectros bootstrap` flow provisions a scoped `ssk_*` key and its AccessProfile
261
277
  in one command.
262
278
 
279
+ > **A root `sk_*` can no longer file into another app context, as of API 0.43.0.**
280
+ > If you run this server on a root key and a tool call names a `folderId` /
281
+ > `parentFolderId` — or a `schemaId` on a document — belonging to a context other
282
+ > than `default`, it is now refused with a uniform `400 "Folder not found"` /
283
+ > `"Schema not found"`. This affects `document_ingest`, `document_update`,
284
+ > `record_create`, `record_update` and `folder_create`, and it is a change in
285
+ > outcome: those calls used to succeed. They never did what they appeared to,
286
+ > though — a root key's writes are always stamped `default`, so the row landed in
287
+ > `default` while its folder or schema lived elsewhere, permanently invisible to
288
+ > the context that owned them. Existing rows written the old way are untouched and
289
+ > still readable, updatable and deletable. The fix is the same scoped key
290
+ > recommended above: one bound to the target context can file into it and always
291
+ > could.
292
+
263
293
  ## Credential resolution
264
294
 
265
295
  The server resolves its API key from the first source that yields one:
@@ -363,6 +393,49 @@ const server = new VectrosMCPServer({
363
393
  await server.connect(createStdioTransport());
364
394
  ```
365
395
 
396
+ ## What this server deliberately doesn't expose
397
+
398
+ This is a decision, not a backlog. An MCP server is a tool surface handed to an
399
+ autonomous caller, so a capability the API offers is not automatically a tool —
400
+ each one has to earn its place on an agent's surface.
401
+
402
+ The line is the **data plane**: records, documents, folders and search are here in
403
+ full. Anything that grants or administers authority is not, and neither is the
404
+ design-time layer that defines the data model.
405
+
406
+ - **Stored scripts** (`/v1/scripts` — push, list, fetch, delete). A stored script is
407
+ code, not data. Authoring it is a design-time act, the same reason schema mutation
408
+ lives in the CLI rather than here: an agent that can write the code that later runs
409
+ under your credential is a different proposition from one that can write records.
410
+ - **Synchronous script execution** (`POST /v1/scripts/execute`, the `scripts:x` scope).
411
+ The closer call of the two, and excluded on operational grounds rather than reach —
412
+ a `scripts:x:<name>` grant is a deliberately narrow, per-script permission, and an
413
+ ordinary scoped key can hold it. What an agent handles badly is the failure surface:
414
+ a run whose outcome cannot be determined returns `500 EXECUTION_OUTCOME_UNKNOWN` with
415
+ writes that may or may not have committed, which is not something a caller resolves
416
+ by retrying — it has to go and look. Getting a retry right also means reusing an
417
+ `Idempotency-Key` across attempts, and a tool call has no natural retry identity, so
418
+ an agent re-invoking after a timeout would silently run the script a second time.
419
+ Exposing this well needs a deliberate design for those two things, not a thin wrapper.
420
+ Until then, run scripts from a caller that can handle them — the API, the SDK, or the
421
+ CLI.
422
+ - **Trigger rules** (`/v1/triggers`). Declaring a rule grants authority — a rule's
423
+ `scopes`/`roleIds` are live, and declaring one is scope-monotonicity-checked like
424
+ any other authority-granting surface. Minting authority is not an agent action.
425
+ - **Trigger failure history** (`GET /v1/trigger-failures`) and **usage/billing**
426
+ (`GET /v1/usage`). Both are read-only, and both are operational rather than
427
+ data-plane — they describe how your automation and your account are behaving, not
428
+ what your content says. That is a question for the developer portal or the CLI,
429
+ where a person is looking at it, rather than a tool an agent reaches for mid-task.
430
+ - **Identity, access and credential administration** — users, access profiles,
431
+ roles, app contexts, issuer registrations, and scoped-key minting. Unchanged
432
+ since launch: identity CRUD stays off the agent tool surface by design.
433
+ `lookup_principal` resolves an identifier to an id and does nothing else.
434
+ - **Compliance operations** (erasure, export). Same reason.
435
+
436
+ If one of these belongs on your agent's surface, that's worth telling us — the
437
+ line is drawn on purpose and can be redrawn with a reason.
438
+
366
439
  ## What this server doesn't do (yet)
367
440
 
368
441
  - **No prompts capability** — `/rag` and `/ingest_pdf` slash-command