@vectros-ai/mcp-server 0.17.2 → 0.18.1

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/dist/index.js CHANGED
@@ -62,6 +62,7 @@ __export2(api_exports, {
62
62
  ContentTooLargeError: () => ContentTooLargeError,
63
63
  DocumentContextEvent: () => DocumentContextEvent,
64
64
  DocumentLookupRequest: () => DocumentLookupRequest,
65
+ DocumentPatchRequest: () => DocumentPatchRequest,
65
66
  DocumentRequest: () => DocumentRequest,
66
67
  DocumentResponse: () => DocumentResponse,
67
68
  DoneEvent: () => DoneEvent,
@@ -405,6 +406,13 @@ var DocumentLookupRequest;
405
406
  Desc: "desc"
406
407
  };
407
408
  })(DocumentLookupRequest || (DocumentLookupRequest = {}));
409
+ var DocumentPatchRequest;
410
+ ((DocumentPatchRequest2) => {
411
+ DocumentPatchRequest2.Status = {
412
+ Active: "ACTIVE",
413
+ Archived: "ARCHIVED"
414
+ };
415
+ })(DocumentPatchRequest || (DocumentPatchRequest = {}));
408
416
  var FileUploadRequest;
409
417
  ((FileUploadRequest2) => {
410
418
  FileUploadRequest2.IndexMode = {
@@ -417,6 +425,7 @@ var FileUploadRequest;
417
425
  var documents_exports = {};
418
426
  __export2(documents_exports, {
419
427
  DocumentLookupRequest: () => DocumentLookupRequest,
428
+ DocumentPatchRequest: () => DocumentPatchRequest,
420
429
  FileUploadRequest: () => FileUploadRequest,
421
430
  LookupDocumentsRequestOrder: () => LookupDocumentsRequestOrder
422
431
  });
@@ -2463,7 +2472,7 @@ var AuthClient = class {
2463
2472
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/auth/jwks");
2464
2473
  }
2465
2474
  /**
2466
- * Returns a page of per-subject PHI read-access rows: who read which subject's PHI, when, against which record, and whether any sensitive value was actually revealed in plaintext. Metadata only — never the PHI itself. This is the disclosure-accounting surface from which a covered entity derives its HIPAA §164.528 accounting of disclosures. Provide at least one query axis: a subject (`subjectType` + `subjectId`) within a `contextId` for the primary accounting query; `resourceId` within a `contextId` for 'who read this record'; `callerKeyId` for 'what did this credential read' (account-wide forensic); or `contextId` alone to enumerate a whole context. `from`/`to` bound the time window. Results are scoped to your account, derived from your token — never from input. Requires the `access-log:r` scope.
2475
+ * Returns a page of per-subject PHI read-access rows: who read which subject's PHI, when, against which record, and whether any sensitive value was actually revealed in plaintext. Metadata only — never the PHI itself. Rows exist only where read-access logging is enabled. Provide at least one query axis: a subject (`subjectType` + `subjectId`) within a `contextId` for the primary accounting query; `resourceId` within a `contextId` for 'who read this record'; `callerKeyId` for 'what did this credential read' (account-wide forensic); or `contextId` alone to enumerate a whole context. `from`/`to` bound the time window. Results are scoped to your account, derived from your token — never from input. Requires the `access-log:r` scope.
2467
2476
  *
2468
2477
  * @param {Vectros.GetAccessLogRequest} request
2469
2478
  * @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -3081,6 +3090,7 @@ var AuthClient = class {
3081
3090
  * @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
3082
3091
  *
3083
3092
  * @throws {@link Vectros.BadRequestError}
3093
+ * @throws {@link Vectros.PaymentRequiredError}
3084
3094
  * @throws {@link Vectros.ForbiddenError}
3085
3095
  * @throws {@link Vectros.TooManyRequestsError}
3086
3096
  *
@@ -3130,6 +3140,8 @@ var AuthClient = class {
3130
3140
  switch (_response.error.statusCode) {
3131
3141
  case 400:
3132
3142
  throw new BadRequestError(_response.error.body, _response.rawResponse);
3143
+ case 402:
3144
+ throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
3133
3145
  case 403:
3134
3146
  throw new ForbiddenError(_response.error.body, _response.rawResponse);
3135
3147
  case 429:
@@ -4165,7 +4177,7 @@ var AuthClient = class {
4165
4177
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/auth/issuers/{issuerId}");
4166
4178
  }
4167
4179
  /**
4168
- * Updates the mutable fields of a registered issuer: `subClaim`, `emailClaim`, `userinfoUri`, `capturedClaims`, `restrictedToDomain` (a new non-blank value must already be a VERIFIED domain for your account; an empty string clears it back to domain-less; see `IssuerUpdateRequest.restrictedToDomain` for the full semantics), `status` (`active`/`suspended` — a registration awaiting verification (`pending_verification`) accepts no status change until it is verified; a suspended issuer's tokens are rejected identically to an unregistered issuer at exchange time), and `selfSignupPolicies`. Fields omitted from the body are left unchanged (partial update). `issuer`, `jwksUri`, `audience`, and `contextId` are trust-anchor / routing-pin fields and are immutable via this route — supplying a value that differs from the current registration is rejected with 400; supplying the current value back is a no-op. Rotating a trust anchor requires deleting and re-registering the issuer, which is itself refused while any user is bound through it. **`subClaim` is identity-determining, not merely cosmetic**: it names which verified JWT claim is read as the federated user's identifier, so changing it on an issuer that already has bound users would silently re-identify (or, under self-signup, orphan) every one of them — changing it is therefore refused once any user has bound through this issuer, the same guard `DELETE` already applies, and is only free before the first real login. Requires a root API key or the bootstrap's provisioning capability, gated identically to every other operation on this surface. A credential confined to one app context may only update an issuer registered in that context; naming one registered in another context returns 404, identically to a nonexistent issuerId. A root API key may update any issuer.
4180
+ * Updates the mutable fields of a registered issuer: `subClaim`, `emailClaim`, `userinfoUri`, `capturedClaims`, `restrictedToDomain` (a new non-blank value must already be a VERIFIED domain for your account; an empty string clears it where permitted; see `IssuerUpdateRequest.restrictedToDomain` for the full semantics), `status` (`active`/`suspended` — a registration awaiting verification (`pending_verification`) accepts no status change until it is verified; a suspended issuer's tokens are rejected identically to an unregistered issuer at exchange time), and `selfSignupPolicies`. Fields omitted from the body are left unchanged (partial update). `issuer`, `jwksUri`, `audience`, and `contextId` are trust-anchor / routing-pin fields and are immutable via this route — supplying a value that differs from the current registration is rejected with 400; supplying the current value back is a no-op. Rotating a trust anchor requires deleting and re-registering the issuer, which is itself refused while any user is bound through it. **`subClaim` is identity-determining, not merely cosmetic**: it names which verified JWT claim is read as the federated user's identifier, so changing it on an issuer that already has bound users would silently re-identify (or, under self-signup, orphan) every one of them — changing it is therefore refused once any user has bound through this issuer, the same guard `DELETE` already applies, and is only free before the first real login. Requires a root API key or the bootstrap's provisioning capability, gated identically to every other operation on this surface. A credential confined to one app context may only update an issuer registered in that context; naming one registered in another context returns 404, identically to a nonexistent issuerId. A root API key may update any issuer.
4169
4181
  *
4170
4182
  * @param {Vectros.IssuerUpdateRequest} request
4171
4183
  * @param {AuthClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -4975,7 +4987,7 @@ var ComplianceClient = class {
4975
4987
  this._options = normalizeClientOptionsWithAuth(options);
4976
4988
  }
4977
4989
  /**
4978
- * Submits a right-to-erasure request for a single end-subject (a user, or an identity entity in any namespace). Erasure removes exactly the data the subject solely owns across the declared contexts, plus the subject's identity and lookup rows. It never touches another account's data and never cascades into another subject's data. The request is asynchronous: it returns 202 with a `requestId`; poll `GET /v1/erasure-requests/{id}` until the job completes to obtain the completion certificate. Requires a root API key — a scoped credential is rejected with 403.
4990
+ * Submits an erasure request for a single end-subject (a user, or an identity entity in any namespace). Erasure deletes the records, documents, folders and record schemas the subject solely owns across the declared contexts (and, for a user, its access profiles there), plus its identity and lookup rows when all contexts are covered. Rows co-owned with another owner scope or principal are kept; read-access log entries are not erased. The request is asynchronous: it returns 202 with a `requestId`; poll `GET /v1/erasure-requests/{id}` until the job completes to obtain the completion certificate. Requires a root API key — a scoped credential is rejected with 403.
4979
4991
  *
4980
4992
  * @param {Vectros.ErasureRequest} request
4981
4993
  * @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -5038,7 +5050,7 @@ var ComplianceClient = class {
5038
5050
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/erasure-requests");
5039
5051
  }
5040
5052
  /**
5041
- * Polls an erasure request by id. While the job is still running this returns its status only; once it completes, the response also includes the verifiable completion certificate (which contexts were swept, per-context deletion counts, and reports of dangling references and shared rows that were left intact). Requires a root API key.
5053
+ * Polls an erasure request by id. While the job is still running this returns its status only; once it completes, the response also includes the completion certificate (which contexts were swept, per-context deletion counts, whether the identity was kept, and reports of dangling references and shared rows left intact). Requires a root API key.
5042
5054
  *
5043
5055
  * @param {Vectros.GetErasureRequestRequest} request
5044
5056
  * @param {ComplianceClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -5281,7 +5293,7 @@ var DocumentsClient = class {
5281
5293
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents");
5282
5294
  }
5283
5295
  /**
5284
- * Creates a document from a raw text string and queues it for asynchronous indexing so it becomes searchable. Optionally supply an `externalId` to make the create idempotent — if a document with the same `externalId` already exists in your context, that existing document is returned unchanged instead of a duplicate being created. The response's `created` field (and the HTTP status — 201 when created, 200 when an existing document was returned) tells the two apart. To overwrite an existing document's content instead of returning it unchanged, set `?upsert=true` (this also requires the `documents:u` scope). Requires the `documents:c` scope to create. Being returned the existing document on a collision is a read of that document's data and additionally requires the `documents:r` scope — a credential holding `documents:c` alone receives a `400` ("already exists") on collision instead of the document.
5296
+ * Creates a document from a raw text string and queues it for asynchronous indexing so it becomes searchable. Optionally supply an `externalId` to make the create idempotent — if a document with the same `externalId` already exists in your context, that existing document is returned unchanged instead of a duplicate being created. The response's `created` field (and the HTTP status — 201 when created, 200 when an existing document was returned) tells the two apart. To overwrite an existing document's content instead of returning it unchanged, set `?upsert=true` (this also requires the `documents:u` scope). Requires the `documents:c` scope to create. Being returned the existing document on a collision is a read of that document's data and additionally requires the `documents:r` scope — a credential holding `documents:c` alone receives a `400` ("already exists") on collision instead of the document. Supplying `externalId` WITHOUT `schemaId` is refused (`400`) unless you also pass `confirmUntyped=true` — externalId uniqueness is scoped per schema, so omitting `schemaId` by mistake would otherwise silently create a separate untyped document instead of targeting the typed one you likely meant.
5285
5297
  *
5286
5298
  * @param {Vectros.IngestDocumentRequest} request
5287
5299
  * @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -5301,10 +5313,11 @@ var DocumentsClient = class {
5301
5313
  return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
5302
5314
  }
5303
5315
  async __ingestDocument(request, requestOptions) {
5304
- const { upsert, allowClear, body: _body } = request;
5316
+ const { upsert, allowClear, confirmUntyped, body: _body } = request;
5305
5317
  const _queryParams = {
5306
5318
  upsert,
5307
- allowClear
5319
+ allowClear,
5320
+ confirmUntyped
5308
5321
  };
5309
5322
  const _authRequest = await this._options.authProvider.getAuthRequest();
5310
5323
  const _headers = mergeHeaders(
@@ -5539,7 +5552,7 @@ var DocumentsClient = class {
5539
5552
  /**
5540
5553
  * Partially updates a document using an RFC 7386 JSON Merge Patch. The `payload` object is deep-merged: keys you send overwrite existing values (recursing into nested objects), a key set to `null` is deleted, and keys you omit are preserved — unlike PUT, which replaces the whole payload. Top-level fields (`title`, `folderId`, `schemaId`, ownership) are set when present and left unchanged when omitted; sending a top-level field as `null` is rejected. Supplying `text` re-ingests the document body (same as PUT). `indexMode`, `externalId`, and `storeText` (text retention is fixed at ingest) are immutable and rejected if present. The merged result is validated against the bound schema. Pass `expectedVersion` for optimistic concurrency (409 on conflict). Requires the `documents:u` scope.
5541
5554
  *
5542
- * @param {Vectros.PatchDocumentRequest} request
5555
+ * @param {Vectros.DocumentPatchRequest} request
5543
5556
  * @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
5544
5557
  *
5545
5558
  * @throws {@link Vectros.BadRequestError}
@@ -5550,17 +5563,14 @@ var DocumentsClient = class {
5550
5563
  *
5551
5564
  * @example
5552
5565
  * await client.documents.patchDocument({
5553
- * id: "id",
5554
- * body: {
5555
- * title: "Patient Intake Form \u2014 Jane Doe"
5556
- * }
5566
+ * id: "id"
5557
5567
  * })
5558
5568
  */
5559
5569
  patchDocument(request, requestOptions) {
5560
5570
  return HttpResponsePromise.fromPromise(this.__patchDocument(request, requestOptions));
5561
5571
  }
5562
5572
  async __patchDocument(request, requestOptions) {
5563
- const { id, body: _body } = request;
5573
+ const { id, ..._body } = request;
5564
5574
  const _authRequest = await this._options.authProvider.getAuthRequest();
5565
5575
  const _headers = mergeHeaders(
5566
5576
  _authRequest.headers,
@@ -5932,7 +5942,7 @@ var DocumentsClient = class {
5932
5942
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/versions");
5933
5943
  }
5934
5944
  /**
5935
- * Starts a file-based document by returning a short-lived presigned S3 PUT URL. Upload the file bytes directly to `uploadUrl`; the document is then automatically queued for text extraction and asynchronous indexing. Supplying an `externalId` makes this idempotent — re-initiating an upload with the same `externalId` re-issues a fresh presigned URL for the SAME existing document, targeting a newly-staged object rather than the document's current one — your PUT lands there first, and the document only adopts it once the upload is validated (so a re-upload inherently replaces the file body) rather than creating a duplicate. The response's `created` field (and the HTTP status — 201 when a new document was minted, 200 when an existing one was matched) tells the two apart. With `?upsert=true`, the submitted `payload`/`title` are also applied to the matched document (file-body divergence cannot be diffed at upload-init — the bytes have not arrived yet — so the re-upload itself replaces the body). Creating a NEW document requires the `documents:c` scope. Re-uploading over an EXISTING document overwrites (and re-indexes) its body, so it is an update: it requires the `documents:u` scope (as does `?upsert=true` for the metadata). **Your PUT to `uploadUrl` MUST include the header named in the response's `requiredHeaderName`, set to `requiredHeaderValue` exactly.** It is a signed part of the presigned request — the PUT never reaches this service either way, so neither failure uses this API's JSON error shape. Omitting it, or sending a different value, invalidates the request's signature and S3 rejects it with a 403 `SignatureDoesNotMatch`. Sending it correctly makes the URL single-use: `requiredHeaderName` is always `If-None-Match` and `requiredHeaderValue` is always `*` — a create, a recovery upload for a document whose earlier file is missing, and a re-upload ALL target a freshly-minted, never-written key, so the same S3 conditional-write precondition applies uniformly (S3's own documented conditional-write behavior): a replay after the object already exists (from your own completed PUT, or anyone else's) fails with `412 Precondition Failed`; a replay after the object was deleted and never re-created finds an absent key again and can succeed — single-use is a property of the object's existence, not a one-time token. **Either precondition can also fail with `409 Conflict` if your PUT genuinely races a concurrent delete on the same object.** Whichever of `412` or `409` you receive, the remedy is the same: call this endpoint again for a fresh URL rather than retrying the same PUT — a fresh call always targets a new key, so it is never subject to whatever raced the previous one.
5945
+ * Starts a file-based document by returning a short-lived presigned S3 PUT URL. Upload the file bytes directly to `uploadUrl`; the document is then automatically queued for text extraction and asynchronous indexing. Supplying an `externalId` makes this idempotent — re-initiating an upload with the same `externalId` re-issues a fresh presigned URL for the SAME existing document, targeting a newly-staged object rather than the document's current one — your PUT lands there first, and the document only adopts it once the upload is validated (so a re-upload inherently replaces the file body) rather than creating a duplicate. The response's `created` field (and the HTTP status — 201 when a new document was minted, 200 when an existing one was matched) tells the two apart. With `?upsert=true`, the submitted `payload`/`title` are also applied to the matched document (file-body divergence cannot be diffed at upload-init — the bytes have not arrived yet — so the re-upload itself replaces the body). Creating a NEW document requires the `documents:c` scope. Re-uploading over an EXISTING document overwrites (and re-indexes) its body, so it is an update: it requires the `documents:u` scope (as does `?upsert=true` for the metadata). **Your PUT to `uploadUrl` MUST include the header named in the response's `requiredHeaderName`, set to `requiredHeaderValue` exactly.** It is a signed part of the presigned request — the PUT never reaches this service either way, so neither failure uses this API's JSON error shape. Omitting it, or sending a different value, invalidates the request's signature and S3 rejects it with a 403 `SignatureDoesNotMatch`. Sending it correctly makes the URL single-use: `requiredHeaderName` is always `If-None-Match` and `requiredHeaderValue` is always `*` — a create, a recovery upload for a document whose earlier file is missing, and a re-upload ALL target a freshly-minted, never-written key, so the same S3 conditional-write precondition applies uniformly (S3's own documented conditional-write behavior): a replay after the object already exists (from your own completed PUT, or anyone else's) fails with `412 Precondition Failed`; a replay after the object was deleted and never re-created finds an absent key again and can succeed — single-use is a property of the object's existence, not a one-time token. **Either precondition can also fail with `409 Conflict` if your PUT genuinely races a concurrent delete on the same object.** Whichever of `412` or `409` you receive, the remedy is the same: call this endpoint again for a fresh URL rather than retrying the same PUT — a fresh call always targets a new key, so it is never subject to whatever raced the previous one. Supplying `externalId` WITHOUT `schemaId` is refused (`400`) unless you also pass `confirmUntyped=true` — externalId uniqueness is scoped per schema, so omitting `schemaId` by mistake would otherwise silently create a separate untyped document instead of targeting the typed one you likely meant.
5936
5946
  *
5937
5947
  * @param {Vectros.FileUploadRequest} request
5938
5948
  * @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -5951,9 +5961,10 @@ var DocumentsClient = class {
5951
5961
  return HttpResponsePromise.fromPromise(this.__uploadDocument(request, requestOptions));
5952
5962
  }
5953
5963
  async __uploadDocument(request, requestOptions) {
5954
- const { upsert, ..._body } = request;
5964
+ const { upsert, confirmUntyped, ..._body } = request;
5955
5965
  const _queryParams = {
5956
- upsert
5966
+ upsert,
5967
+ confirmUntyped
5957
5968
  };
5958
5969
  const _authRequest = await this._options.authProvider.getAuthRequest();
5959
5970
  const _headers = mergeHeaders(
@@ -7858,6 +7869,8 @@ var InferenceClient = class {
7858
7869
  throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
7859
7870
  case 403:
7860
7871
  throw new ForbiddenError(_response.error.body, _response.rawResponse);
7872
+ case 413:
7873
+ throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
7861
7874
  case 429:
7862
7875
  throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
7863
7876
  default:
@@ -7997,6 +8010,8 @@ var InferenceClient = class {
7997
8010
  throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
7998
8011
  case 403:
7999
8012
  throw new ForbiddenError(_response.error.body, _response.rawResponse);
8013
+ case 413:
8014
+ throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
8000
8015
  case 429:
8001
8016
  throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
8002
8017
  case 500:
@@ -10249,7 +10264,7 @@ var logging;
10249
10264
 
10250
10265
  // src/server.ts
10251
10266
  var import_types3 = require("@modelcontextprotocol/sdk/types.js");
10252
- var import_zod24 = require("zod");
10267
+ var import_zod25 = require("zod");
10253
10268
 
10254
10269
  // src/auth.ts
10255
10270
  var KEY_RE = /^(sk|ssk|st)_(live|test)_/;
@@ -10281,7 +10296,7 @@ function warnOnSuboptimalKey(key, log) {
10281
10296
  if (key.prefix === "sk") {
10282
10297
  log.warn(
10283
10298
  { prefix: key.prefix, env: key.env },
10284
- 'wildcard-scope key (sk_*) on a desktop MCP server is overly broad. For production, prefer a scoped ssk_* key bound to a narrowed AccessProfile. See the Vectros scoped-token documentation ("Recommended AccessProfile for MCP").'
10299
+ 'wildcard-scope key (sk_*) on a desktop MCP server is overly broad. For production, prefer a scoped ssk_* key bound to a narrowed AccessProfile: `npx -y @vectros-ai/cli bootstrap` mints one, and the "Recommended credential" section of this package\'s README describes it.'
10285
10300
  );
10286
10301
  } else if (key.prefix === "st") {
10287
10302
  log.warn(
@@ -10293,8 +10308,8 @@ function warnOnSuboptimalKey(key, log) {
10293
10308
 
10294
10309
  // src/build-info.ts
10295
10310
  var BUILD_INFO = {
10296
- mcpServer: true ? "0.17.2" : "dev",
10297
- sdk: true ? "0.45.0" : "dev"
10311
+ mcpServer: true ? "0.18.1" : "dev",
10312
+ sdk: true ? "0.47.0" : "dev"
10298
10313
  };
10299
10314
 
10300
10315
  // src/log.ts
@@ -10586,7 +10601,7 @@ var inputSchema = {
10586
10601
  "Keyword-match precision for the BM25 leg (TEXT/HYBRID modes). OR = any term (broad recall), AND = all terms (higher precision), PHRASE = contiguous phrase. Default: PHRASE (slop 3) in HYBRID, OR in TEXT. (The advanced raw-query COMPLEX mode is intentionally not exposed here.)"
10587
10602
  ),
10588
10603
  limit: import_zod.z.number().int().min(1).max(MCP_MAX_LIMIT).optional().describe(
10589
- `Max hits per page. Defaults to a low ${MCP_DEFAULT_LIMIT} to protect the agent context window; raise up to ${MCP_MAX_LIMIT} (the most a single search returns) in one call when you can accept the larger result, or page with \`offset\` instead.`
10604
+ `Max hits per page. Defaults to a low ${MCP_DEFAULT_LIMIT} to protect the agent context window; raise up to ${MCP_MAX_LIMIT} (this tool's ceiling) in one call when you can accept the larger result, or page with \`offset\` instead.`
10590
10605
  ),
10591
10606
  offset: import_zod.z.number().int().min(0).max(200).optional().describe("Skip the first N hits \u2014 for pagination (0\u2013200)."),
10592
10607
  folderId: import_zod.z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
@@ -11335,52 +11350,65 @@ var recordDelete = ({ client, log }) => ({
11335
11350
  var record_delete_default = recordDelete;
11336
11351
 
11337
11352
  // src/tools/rag_ask.ts
11353
+ var import_zod10 = require("zod");
11354
+
11355
+ // src/tools/provider-alias.ts
11338
11356
  var import_zod9 = require("zod");
11357
+ var providerAliasInput = import_zod9.z.string().min(1, "providerAlias must be non-empty when supplied").optional().describe(
11358
+ "Route THIS call through your own model provider instead of the platform-hosted default, by the alias of a provider config your account has activated. It works only after your account has set up that provider and signed the platform's risk-acceptance waiver; without both, the platform refuses the call with a 403, which is returned to you as written. If your account has accepted the Business Associate Agreement with Vectros in the developer portal, an account owner must also have attested that provider config in the developer portal (Model Providers); until then the platform refuses the call with a 403 that says it needs a BAA-holder attestation. A call served by your own provider leaves the Vectros perimeter. Omit it (the default) to use the platform-hosted path. When set, `model` names the model on your provider's own id space, and falls back to that provider config's default model when omitted."
11359
+ );
11360
+
11361
+ // src/tools/rag_ask.ts
11339
11362
  var SEARCH_MCP_DEFAULT_LIMIT = 5;
11340
11363
  var SEARCH_MCP_MAX_LIMIT = 50;
11341
11364
  var inputSchema9 = {
11342
- query: import_zod9.z.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
11343
- instructions: import_zod9.z.string().optional().describe(
11365
+ query: import_zod10.z.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
11366
+ instructions: import_zod10.z.string().optional().describe(
11344
11367
  'System prompt that overrides the default for the generation step \u2014 e.g. "answer only from the provided context, cite sources, be concise". Default: a generic answer-from-context instruction.'
11345
11368
  ),
11346
- model: import_zod9.z.string().optional().describe(
11347
- "Inference model alias (e.g. claude-haiku-4-5, claude-sonnet-5, claude-opus-4-8). Default = tier-appropriate Haiku. See GET /v1/models for the catalog the calling key can reach."
11369
+ model: import_zod10.z.string().optional().describe(
11370
+ "Inference model alias (e.g. claude-haiku-5-5, claude-sonnet-5-5, claude-opus-5-5). Omit it to use the platform default, which GET /v1/models reports as `defaultModel`; that endpoint also lists the catalog the calling key can reach. When `providerAlias` is set, this instead names the model on that provider's own id space."
11348
11371
  ),
11349
- search: import_zod9.z.object({
11350
- mode: import_zod9.z.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
11351
- limit: import_zod9.z.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
11352
- `Passages to retrieve before generating (the grounding-corpus size). Default ${SEARCH_MCP_DEFAULT_LIMIT}; raise up to ${SEARCH_MCP_MAX_LIMIT} (the RAG search API max) for a broader corpus when the extra grounding is worth the larger prompt.`
11372
+ providerAlias: providerAliasInput,
11373
+ search: import_zod10.z.object({
11374
+ mode: import_zod10.z.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
11375
+ limit: import_zod10.z.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
11376
+ `Passages to retrieve before generating (the grounding-corpus size). Default ${SEARCH_MCP_DEFAULT_LIMIT}; raise up to ${SEARCH_MCP_MAX_LIMIT} (this tool's ceiling) for a broader corpus when the extra grounding is worth the larger prompt.`
11353
11377
  ),
11354
11378
  // Retrieval scoping — parity with hybrid_search so an agent can ground the
11355
11379
  // answer on a specific owner / folder / type / metadata subset, not just the
11356
11380
  // whole-tenant corpus.
11357
- userId: import_zod9.z.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
11358
- scope: import_zod9.z.string().optional().describe(
11381
+ userId: import_zod10.z.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
11382
+ scope: import_zod10.z.string().optional().describe(
11359
11383
  'Restrict retrieval to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "client:<uuid>", "group:eng-team"). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself. Mutually exclusive with `scopeFilters` \u2014 use this for ONE dimension.'
11360
11384
  ),
11361
- scopeFilters: import_zod9.z.array(import_zod9.z.string()).min(1, "scopeFilters must name at least one dimension when supplied").optional().describe(
11385
+ scopeFilters: import_zod10.z.array(import_zod10.z.string()).min(1, "scopeFilters must name at least one dimension when supplied").optional().describe(
11362
11386
  'Restrict retrieval to content matching ALL of these scope values \u2014 one per namespace \u2014 for a credential whose access spans more than one ownership dimension. e.g. ["org:<uuid>", "client:<uuid>"] grounds the answer on one specific client WITHIN one specific org, which `scope` alone cannot express. Naming the same namespace twice is rejected. Mutually exclusive with `scope`.'
11363
11387
  ),
11364
- folderId: import_zod9.z.string().optional().describe("Restrict retrieval to this exact folder."),
11365
- rootFolderId: import_zod9.z.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
11366
- typeName: import_zod9.z.string().optional().describe("Restrict record retrieval to this schema type."),
11367
- contentTypes: import_zod9.z.array(import_zod9.z.enum(["documents", "records"])).optional().describe('Narrow retrieval to content types. ["documents"] or ["records"]; omit for both.'),
11368
- filters: import_zod9.z.record(import_zod9.z.unknown()).optional().describe(
11388
+ folderId: import_zod10.z.string().optional().describe("Restrict retrieval to this exact folder."),
11389
+ rootFolderId: import_zod10.z.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
11390
+ typeName: import_zod10.z.string().optional().describe("Restrict record retrieval to this schema type."),
11391
+ contentTypes: import_zod10.z.array(import_zod10.z.enum(["documents", "records"])).optional().describe('Narrow retrieval to content types. ["documents"] or ["records"]; omit for both.'),
11392
+ filters: import_zod10.z.record(import_zod10.z.unknown()).optional().describe(
11369
11393
  'Field-level metadata filters (AND-combined). Value = scalar (equality), array (OR-set), or operator map ($eq/$ne/$gt/$gte/$lt/$lte, $in/$nin). e.g. {"status":"open"}.'
11370
11394
  ),
11371
- createdAfter: import_zod9.z.string().optional().describe(
11395
+ createdAfter: import_zod10.z.string().optional().describe(
11372
11396
  "Restrict retrieval to content CREATED at/after this ISO 8601 UTC timestamp \u2014 the item's own creation time, which does not move when it is re-indexed. An item matched on its FIRST index may be checked against its index time instead, a bounded clock-skew allowance."
11373
11397
  ),
11374
- createdBefore: import_zod9.z.string().optional().describe("Same creation-time basis as createdAfter, bounding at/before this ISO 8601 UTC value."),
11375
- requireComplete: import_zod9.z.boolean().optional().describe("Fail closed (503) on a degraded search backend instead of grounding on partial results. Default false.")
11398
+ createdBefore: import_zod10.z.string().optional().describe("Same creation-time basis as createdAfter, bounding at/before this ISO 8601 UTC value."),
11399
+ requireComplete: import_zod10.z.boolean().optional().describe("Fail closed (503) on a degraded search backend instead of grounding on partial results. Default false.")
11376
11400
  }).optional().describe("Retrieval params + scoping. Default mode HYBRID, limit " + SEARCH_MCP_DEFAULT_LIMIT + "."),
11377
- maxTokens: import_zod9.z.number().int().min(1).optional().describe("Max output tokens."),
11378
- temperature: import_zod9.z.number().min(0).max(1).optional().describe("Sampling temperature (0.0\u20131.0). Default 0.3 \u2014 lower favors deterministic, retrieval-grounded answers.")
11401
+ maxTokens: import_zod10.z.number().int().min(1).optional().describe(
11402
+ "Max output tokens. claude-haiku-5-5, claude-sonnet-5, claude-sonnet-5-5 and claude-opus-5-5 can reason before they answer; when they do, the reasoning counts against this limit, so a small value can return little or no text. Leave headroom: 1024 or more for a short answer."
11403
+ ),
11404
+ temperature: import_zod10.z.number().min(0).max(1).optional().describe(
11405
+ "Sampling temperature (0.0\u20131.0). Default 0.3 \u2014 lower favors deterministic, retrieval-grounded answers. The platform ignores it for claude-haiku-5-5, claude-sonnet-5, claude-sonnet-5-5 and claude-opus-5-5, which sample at their own default; it applies to models that accept a temperature, such as claude-haiku-4-5."
11406
+ )
11379
11407
  };
11380
11408
  var ragAsk = ({ client, log }) => ({
11381
11409
  name: "rag_ask",
11382
11410
  title: "RAG ask (corpus-wide)",
11383
- description: "Ask a question grounded against your tenant's indexed content. Vectros performs a hybrid search, injects the top-K passages into the prompt, and streams a model answer back. Scope retrieval with `search` (ownership \u2014 `scope` for one ownership dimension or `scopeFilters` for several at once, e.g. one client within one org \u2014 folder, type, metadata filters, date window) to ground on a subset \u2014 e.g. one patient or one folder \u2014 and steer generation with `instructions` / `temperature`. The full answer is returned as a single response; progress notifications keep the call alive during the 30-45s generation window. Inference runs in-perimeter against AWS Bedrock \u2014 PHI never leaves the BAA boundary.",
11411
+ description: "Ask a question grounded against your tenant's indexed content. Vectros performs a hybrid search, injects the top-K passages into the prompt, and streams a model answer back. Scope retrieval with `search` (ownership \u2014 `scope` for one ownership dimension or `scopeFilters` for several at once, e.g. one client within one org \u2014 folder, type, metadata filters, date window) to ground on a subset \u2014 e.g. one patient or one folder \u2014 and steer generation with `instructions` (and `temperature`, for the models that accept one). The full answer is returned as a single response; progress notifications keep the call alive during the 30-45s generation window. Calls without `providerAlias` are served by platform-hosted AWS Bedrock. A call that sets `providerAlias` goes to your own model provider, outside Vectros's AWS BAA boundary.",
11384
11412
  inputSchema: inputSchema9,
11385
11413
  // NOT a pure read: this call reserves an atomic hold against the caller's prepaid inference
11386
11414
  // balance before generation starts, then debits it on completion — a real financial side
@@ -11422,6 +11450,7 @@ var ragAsk = ({ client, log }) => ({
11422
11450
  query: args.query,
11423
11451
  instructions: args.instructions,
11424
11452
  model: args.model,
11453
+ providerAlias: args.providerAlias,
11425
11454
  search,
11426
11455
  maxTokens: args.maxTokens,
11427
11456
  temperature: args.temperature
@@ -11476,19 +11505,22 @@ var ragAsk = ({ client, log }) => ({
11476
11505
  var rag_ask_default = ragAsk;
11477
11506
 
11478
11507
  // src/tools/document_ask.ts
11479
- var import_zod10 = require("zod");
11508
+ var import_zod11 = require("zod");
11480
11509
  var inputSchema10 = {
11481
- documentId: import_zod10.z.string().min(1, "documentId is required").describe("ID of the document to ask against."),
11482
- prompt: import_zod10.z.string().min(1, "prompt is required").describe("Question to ask about the document."),
11483
- model: import_zod10.z.string().optional().describe(
11484
- "Inference model alias. Default = tier-appropriate Haiku. See GET /v1/models for the catalog the calling key can reach."
11510
+ documentId: import_zod11.z.string().min(1, "documentId is required").describe("ID of the document to ask against."),
11511
+ prompt: import_zod11.z.string().min(1, "prompt is required").describe("Question to ask about the document."),
11512
+ model: import_zod11.z.string().optional().describe(
11513
+ "Inference model alias. Omit it to use the platform default, which GET /v1/models reports as `defaultModel`; that endpoint also lists the catalog the calling key can reach. When `providerAlias` is set, this instead names the model on that provider's own id space."
11485
11514
  ),
11486
- maxTokens: import_zod10.z.number().int().min(1).optional().describe("Max output tokens.")
11515
+ providerAlias: providerAliasInput,
11516
+ maxTokens: import_zod11.z.number().int().min(1).optional().describe(
11517
+ "Max output tokens. claude-haiku-5-5, claude-sonnet-5, claude-sonnet-5-5 and claude-opus-5-5 can reason before they answer; when they do, the reasoning counts against this limit, so a small value can return little or no text. Leave headroom: 1024 or more for a short answer."
11518
+ )
11487
11519
  };
11488
11520
  var documentAsk = ({ client, log }) => ({
11489
11521
  name: "document_ask",
11490
11522
  title: "Document ask (single-document)",
11491
- description: "Ask a question against a single indexed document. Returns a grounded answer with the document context that informed it. For documents exceeding the input-token cap, the API returns a structured 413 with `estimatedTokens` and `limitTokens`. Inference runs in-perimeter against AWS Bedrock \u2014 PHI never leaves the BAA boundary.",
11523
+ description: "Ask a question against a single indexed document. Returns a grounded answer with the document context that informed it. For documents exceeding the input-token cap, the API returns a structured 413 with `estimatedTokens` and `limitTokens`. Calls without `providerAlias` are served by platform-hosted AWS Bedrock. A call that sets `providerAlias` goes to your own model provider, outside Vectros's AWS BAA boundary.",
11492
11524
  inputSchema: inputSchema10,
11493
11525
  // NOT a pure read: this call reserves an atomic hold against the caller's prepaid inference
11494
11526
  // balance before generation starts, then debits it on completion — a real financial side
@@ -11505,6 +11537,7 @@ var documentAsk = ({ client, log }) => ({
11505
11537
  id: args.documentId,
11506
11538
  prompt: args.prompt,
11507
11539
  model: args.model,
11540
+ providerAlias: args.providerAlias,
11508
11541
  maxTokens: args.maxTokens
11509
11542
  });
11510
11543
  const progressToken = extra._meta?.progressToken;
@@ -11557,18 +11590,18 @@ var documentAsk = ({ client, log }) => ({
11557
11590
  var document_ask_default = documentAsk;
11558
11591
 
11559
11592
  // src/tools/list_schemas.ts
11560
- var import_zod11 = require("zod");
11593
+ var import_zod12 = require("zod");
11561
11594
  var inputSchema11 = {
11562
- userId: import_zod11.z.string().optional().describe(
11595
+ userId: import_zod12.z.string().optional().describe(
11563
11596
  "Filter to schemas visible to a specific user. Optional; default returns all schemas the credential can see."
11564
11597
  ),
11565
- scope: import_zod11.z.string().optional().describe(
11598
+ scope: import_zod12.z.string().optional().describe(
11566
11599
  'Filter to schemas carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `org` and `client` are reserved namespace names, registered like any other. Optional; default returns all schemas the credential can see.'
11567
11600
  ),
11568
- surface: import_zod11.z.enum(["record", "document", "user", "entity"]).optional().describe(
11601
+ surface: import_zod12.z.enum(["record", "document", "user", "entity"]).optional().describe(
11569
11602
  "Filter to schemas bindable to this surface \u2014 e.g. `document` to list only document types, or `record` for record types. Identity entities in any namespace (org/client/one you registered) bind under the single `entity` surface; `user` and `entity` are account-wide. Optional."
11570
11603
  ),
11571
- recordType: import_zod11.z.string().optional().describe(
11604
+ recordType: import_zod12.z.string().optional().describe(
11572
11605
  'Resolve the single schema for this record type (its natural handle, e.g. "patient") instead of listing \u2014 returns a one-element result, or empty if none. Combine with `surface=user|entity` for an identity schema. Takes precedence over userId. Optional.'
11573
11606
  )
11574
11607
  };
@@ -11604,19 +11637,19 @@ var listSchemas = ({ client, log }) => ({
11604
11637
  var list_schemas_default = listSchemas;
11605
11638
 
11606
11639
  // src/tools/document_get.ts
11607
- var import_zod12 = require("zod");
11640
+ var import_zod13 = require("zod");
11608
11641
  var MAX_TEXT_CHARS = 32e3;
11609
11642
  var inputSchema12 = {
11610
- documentId: import_zod12.z.string().optional().describe("The document ID returned by document_ingest or hybrid_search. Provide this OR externalId+type."),
11611
- externalId: import_zod12.z.string().optional().describe(
11643
+ documentId: import_zod13.z.string().optional().describe("The document ID returned by document_ingest or hybrid_search. Provide this OR externalId+type."),
11644
+ externalId: import_zod13.z.string().optional().describe(
11612
11645
  "Your caller-owned externalId \u2014 an alternative selector to `documentId` (requires `type`). Fetches the document you ingested under that externalId without a manual lookup first."
11613
11646
  ),
11614
- type: import_zod12.z.string().optional().describe("Document type \u2014 REQUIRED when selecting by `externalId` (externalId is unique within a type)."),
11615
- includeText: import_zod12.z.boolean().optional().describe(
11647
+ type: import_zod13.z.string().optional().describe("Document type \u2014 REQUIRED when selecting by `externalId` (externalId is unique within a type)."),
11648
+ includeText: import_zod13.z.boolean().optional().describe(
11616
11649
  "If true, fetch and include the document text (capped at ~8K tokens, truncated: true flag if cut). Default false \u2014 metadata only. For larger documents, prefer `document_ask` over dumping text into context."
11617
11650
  ),
11618
- includeDownloadUrl: import_zod12.z.boolean().optional().describe(
11619
- "If true, also return a short-lived presigned `downloadUrl` for the original file (file-backed documents only \u2014 the way to retrieve the raw bytes of a document ingested via file mode). Default false. Unavailable for text-only documents \u2014 `downloadAvailable: false` is set instead."
11651
+ includeDownloadUrl: import_zod13.z.boolean().optional().describe(
11652
+ "If true, also return a short-lived presigned `downloadUrl` for the original file (file-backed documents only \u2014 the way to retrieve the raw bytes of a document ingested via file mode). Default false. Unavailable for text-only documents \u2014 `downloadAvailable: false` is set instead. `downloadExpires` is an upper bound, not a guarantee: if the file behind this document is replaced (a document_ingest upsert) after this URL is minted, the URL can stop working before that time. Request a fresh one after any re-ingest rather than reusing one you already hold."
11620
11653
  )
11621
11654
  };
11622
11655
  function truncateText(text) {
@@ -11699,28 +11732,28 @@ var documentGet = ({ client, log }) => ({
11699
11732
  var document_get_default = documentGet;
11700
11733
 
11701
11734
  // src/tools/document_query.ts
11702
- var import_zod13 = require("zod");
11735
+ var import_zod14 = require("zod");
11703
11736
  var MCP_DEFAULT_LIMIT3 = 3;
11704
11737
  var MCP_MAX_LIMIT3 = 100;
11705
11738
  var inputSchema13 = {
11706
11739
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
11707
- field: import_zod13.z.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
11708
- value: import_zod13.z.string().optional().describe("Lookup mode (equality): exact-match value for `field`. Works on sensitive fields too."),
11709
- from: import_zod13.z.string().optional().describe("Lookup mode (range): inclusive lower bound; requires `to`. Range-enabled, non-sensitive fields only."),
11710
- to: import_zod13.z.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
11711
- prefix: import_zod13.z.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
11712
- order: import_zod13.z.enum(["asc", "desc"]).optional().describe(
11740
+ field: import_zod14.z.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
11741
+ value: import_zod14.z.string().optional().describe("Lookup mode (equality): exact-match value for `field`. Works on sensitive fields too."),
11742
+ from: import_zod14.z.string().optional().describe("Lookup mode (range): inclusive lower bound; requires `to`. Range-enabled, non-sensitive fields only."),
11743
+ to: import_zod14.z.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
11744
+ prefix: import_zod14.z.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
11745
+ order: import_zod14.z.enum(["asc", "desc"]).optional().describe(
11713
11746
  "Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc` \u2014 use `desc` with a range/prefix lookup to get the most-recent / highest values first (e.g. latest-N). Ignored in list mode."
11714
11747
  ),
11715
- type: import_zod13.z.string().optional().describe("Lookup mode: the document type (the bound schema's type). Required with `field`."),
11748
+ type: import_zod14.z.string().optional().describe("Lookup mode: the document type (the bound schema's type). Required with `field`."),
11716
11749
  // List-mode args:
11717
- folderId: import_zod13.z.string().optional().describe("List mode: only documents in this folder (Vectros folder id)."),
11718
- userId: import_zod13.z.string().optional().describe("List mode: scope to documents owned by this user."),
11719
- scope: import_zod13.z.string().optional().describe(
11750
+ folderId: import_zod14.z.string().optional().describe("List mode: only documents in this folder (Vectros folder id)."),
11751
+ userId: import_zod14.z.string().optional().describe("List mode: scope to documents owned by this user."),
11752
+ scope: import_zod14.z.string().optional().describe(
11720
11753
  'List mode: filter to documents carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "client:<uuid>", "group:eng-team"). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself.'
11721
11754
  ),
11722
- startFrom: import_zod13.z.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
11723
- limit: import_zod13.z.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
11755
+ startFrom: import_zod14.z.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
11756
+ limit: import_zod14.z.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
11724
11757
  `Max documents per page. Defaults to a low ${MCP_DEFAULT_LIMIT3} to protect the agent context window; raise up to ${MCP_MAX_LIMIT3} (the documents API max) in one call when you can accept the larger payload, or page with \`startFrom\` instead.`
11725
11758
  )
11726
11759
  };
@@ -11810,29 +11843,29 @@ var documentQuery = ({ client, log }) => ({
11810
11843
  var document_query_default = documentQuery;
11811
11844
 
11812
11845
  // src/tools/document_update.ts
11813
- var import_zod14 = require("zod");
11846
+ var import_zod15 = require("zod");
11814
11847
  var inputSchema14 = {
11815
- documentId: import_zod14.z.string().optional().describe("The Vectros document id to update. Provide this OR externalId+type."),
11816
- externalId: import_zod14.z.string().optional().describe(
11848
+ documentId: import_zod15.z.string().optional().describe("The Vectros document id to update. Provide this OR externalId+type."),
11849
+ externalId: import_zod15.z.string().optional().describe(
11817
11850
  "Your caller-owned externalId \u2014 an alternative selector to `documentId` (requires `type`). Updates the document you ingested under that externalId without a manual lookup first."
11818
11851
  ),
11819
- type: import_zod14.z.string().optional().describe("Document type \u2014 REQUIRED when selecting by `externalId` (externalId is unique within a type)."),
11820
- title: import_zod14.z.string().optional().describe("New title. Omit to keep the current title."),
11821
- fields: import_zod14.z.record(import_zod14.z.string(), import_zod14.z.unknown()).optional().describe(
11852
+ type: import_zod15.z.string().optional().describe("Document type \u2014 REQUIRED when selecting by `externalId` (externalId is unique within a type)."),
11853
+ title: import_zod15.z.string().optional().describe("New title. Omit to keep the current title."),
11854
+ fields: import_zod15.z.record(import_zod15.z.string(), import_zod15.z.unknown()).optional().describe(
11822
11855
  "Typed-payload field\u2192value changes DEEP-MERGED into the existing payload (a field set to `null` is deleted; unspecified fields preserved). Omit to leave the payload unchanged."
11823
11856
  ),
11824
- text: import_zod14.z.string().optional().describe(
11857
+ text: import_zod15.z.string().optional().describe(
11825
11858
  "New raw text body. Supplying it re-ingests the document body and re-queues it for indexing, so the search index reflects the edit (text-ingested documents only \u2014 not file-backed ones). Omit to leave the stored text unchanged. This is the in-place alternative to re-ingesting with upsert:true."
11826
11859
  ),
11827
- folderId: import_zod14.z.string().optional().describe("Move the document into this folder (cannot be cleared once set)."),
11828
- status: import_zod14.z.enum(["ACTIVE", "ARCHIVED"]).optional().describe(
11860
+ folderId: import_zod15.z.string().optional().describe("Move the document into this folder (cannot be cleared once set)."),
11861
+ status: import_zod15.z.enum(["ACTIVE", "ARCHIVED"]).optional().describe(
11829
11862
  "Lifecycle status. `ARCHIVED` soft-retracts the document \u2014 it is pulled from search and recall but kept and recoverable; set it back to `ACTIVE` to re-index and restore it. The way to retire superseded content when the credential has no delete authority. Omit to leave unchanged."
11830
11863
  ),
11831
- userId: import_zod14.z.string().optional().describe("Reassign the owning user (Vectros UUID)."),
11832
- scopes: import_zod14.z.array(import_zod14.z.string()).optional().describe(
11864
+ userId: import_zod15.z.string().optional().describe("Reassign the owning user (Vectros UUID)."),
11865
+ scopes: import_zod15.z.array(import_zod15.z.string()).optional().describe(
11833
11866
  'Reassign scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]) \u2014 the COMPLETE new parent set. `org`/`client` are reserved namespace names, registered like any other. Omit to leave ownership unchanged; `[]` clears it (private to the owning user).'
11834
11867
  ),
11835
- expectedVersion: import_zod14.z.number().int().optional().describe(
11868
+ expectedVersion: import_zod15.z.number().int().optional().describe(
11836
11869
  "Optimistic concurrency: the version you last read. If the document changed since, the update is refused as a conflict (re-read and retry). Omit for last-write-wins."
11837
11870
  )
11838
11871
  };
@@ -11860,18 +11893,18 @@ var documentUpdate = ({ client, log }) => ({
11860
11893
  try {
11861
11894
  const resolved = await resolveDocumentIdByExternalId(client, { id: documentId, externalId, type });
11862
11895
  if ("error" in resolved) return toolError("document_update", new Error(resolved.error));
11863
- const body = {};
11864
- if (title !== void 0) body.title = title;
11865
- if (fields !== void 0) body.payload = fields;
11866
- if (text !== void 0) body.text = text;
11867
- if (folderId !== void 0) body.folderId = folderId;
11868
- if (status !== void 0) body.status = status;
11869
- if (userId !== void 0) body.userId = userId;
11870
- if (scopes !== void 0) body.scopes = scopes;
11871
- if (expectedVersion !== void 0) body.expectedVersion = expectedVersion;
11896
+ const patch = {};
11897
+ if (title !== void 0) patch.title = title;
11898
+ if (fields !== void 0) patch.payload = fields;
11899
+ if (text !== void 0) patch.text = text;
11900
+ if (folderId !== void 0) patch.folderId = folderId;
11901
+ if (status !== void 0) patch.status = status;
11902
+ if (userId !== void 0) patch.userId = userId;
11903
+ if (scopes !== void 0) patch.scopes = scopes;
11904
+ if (expectedVersion !== void 0) patch.expectedVersion = expectedVersion;
11872
11905
  const updated = await client.documents.patchDocument({
11873
11906
  id: resolved.id,
11874
- body
11907
+ ...patch
11875
11908
  });
11876
11909
  log.debug({ tool: "document_update", documentId: resolved.id, externalId }, "document_update ok");
11877
11910
  return { content: [{ type: "text", text: JSON.stringify(updated, null, 2) }] };
@@ -11884,9 +11917,9 @@ var documentUpdate = ({ client, log }) => ({
11884
11917
  var document_update_default = documentUpdate;
11885
11918
 
11886
11919
  // src/tools/document_delete.ts
11887
- var import_zod15 = require("zod");
11920
+ var import_zod16 = require("zod");
11888
11921
  var inputSchema15 = {
11889
- documentId: import_zod15.z.string().min(1, "documentId is required").describe("The Vectros document id to delete.")
11922
+ documentId: import_zod16.z.string().min(1, "documentId is required").describe("The Vectros document id to delete.")
11890
11923
  };
11891
11924
  var documentDelete = ({ client, log }) => ({
11892
11925
  name: "document_delete",
@@ -11913,18 +11946,18 @@ var documentDelete = ({ client, log }) => ({
11913
11946
  var document_delete_default = documentDelete;
11914
11947
 
11915
11948
  // src/tools/folder_query.ts
11916
- var import_zod16 = require("zod");
11949
+ var import_zod17 = require("zod");
11917
11950
  var MCP_DEFAULT_LIMIT4 = 10;
11918
11951
  var MCP_MAX_LIMIT4 = 100;
11919
11952
  var inputSchema16 = {
11920
- id: import_zod16.z.string().optional().describe("Get mode: fetch this single folder by id."),
11921
- parentId: import_zod16.z.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
11922
- userId: import_zod16.z.string().optional().describe("List mode: scope to folders owned by this user."),
11923
- scope: import_zod16.z.string().optional().describe(
11953
+ id: import_zod17.z.string().optional().describe("Get mode: fetch this single folder by id."),
11954
+ parentId: import_zod17.z.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
11955
+ userId: import_zod17.z.string().optional().describe("List mode: scope to folders owned by this user."),
11956
+ scope: import_zod17.z.string().optional().describe(
11924
11957
  'List mode: filter to folders carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "client:<uuid>", "group:eng-team"). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself.'
11925
11958
  ),
11926
- startFrom: import_zod16.z.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
11927
- limit: import_zod16.z.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
11959
+ startFrom: import_zod17.z.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
11960
+ limit: import_zod17.z.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
11928
11961
  `List mode: max folders per page. Default 10; raise up to ${MCP_MAX_LIMIT4} (the folders API max) in one call when you can accept the larger page, or page with \`startFrom\`.`
11929
11962
  )
11930
11963
  };
@@ -11964,16 +11997,16 @@ var folderQuery = ({ client, log }) => ({
11964
11997
  var folder_query_default = folderQuery;
11965
11998
 
11966
11999
  // src/tools/folder_create.ts
11967
- var import_zod17 = require("zod");
12000
+ var import_zod18 = require("zod");
11968
12001
  var inputSchema17 = {
11969
- name: import_zod17.z.string().min(1, "name is required").describe("Folder display name."),
11970
- description: import_zod17.z.string().optional().describe("Optional description of the folder's purpose."),
11971
- parentId: import_zod17.z.string().optional().describe("Parent folder id \u2014 places the folder under this parent. Omit for the context default root. Create-only."),
11972
- slug: import_zod17.z.string().optional().describe(
12002
+ name: import_zod18.z.string().min(1, "name is required").describe("Folder display name."),
12003
+ description: import_zod18.z.string().optional().describe("Optional description of the folder's purpose."),
12004
+ parentId: import_zod18.z.string().optional().describe("Parent folder id \u2014 places the folder under this parent. Omit for the context default root. Create-only."),
12005
+ slug: import_zod18.z.string().optional().describe(
11973
12006
  "Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. SUPPLY THIS to make a retry safe \u2014 if omitted, a derived slug is only ever a freshly-proved-free one, so a redelivered call creates a new sibling folder every time rather than matching the first."
11974
12007
  ),
11975
- userId: import_zod17.z.string().optional().describe("Owning user (Vectros UUID)."),
11976
- scopes: import_zod17.z.array(import_zod17.z.string()).optional().describe(
12008
+ userId: import_zod18.z.string().optional().describe("Owning user (Vectros UUID)."),
12009
+ scopes: import_zod18.z.array(import_zod18.z.string()).optional().describe(
11977
12010
  'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself. This is the folder\'s COMPLETE scope declaration and values must come from the credential\'s own identity. An empty array `[]` creates a PRIVATE folder owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default.'
11978
12011
  )
11979
12012
  };
@@ -12015,16 +12048,16 @@ var folderCreate = ({ client, log }) => ({
12015
12048
  var folder_create_default = folderCreate;
12016
12049
 
12017
12050
  // src/tools/folder_update.ts
12018
- var import_zod18 = require("zod");
12051
+ var import_zod19 = require("zod");
12019
12052
  var inputSchema18 = {
12020
- id: import_zod18.z.string().min(1, "id is required").describe("The Vectros folder id to update."),
12021
- name: import_zod18.z.string().optional().describe("New folder name. Omit to keep the current name."),
12022
- description: import_zod18.z.string().optional().describe("New description. Omit to leave unchanged."),
12023
- userId: import_zod18.z.string().optional().describe("Reassign the owning user (Vectros UUID)."),
12024
- scopes: import_zod18.z.array(import_zod18.z.string()).optional().describe(
12053
+ id: import_zod19.z.string().min(1, "id is required").describe("The Vectros folder id to update."),
12054
+ name: import_zod19.z.string().optional().describe("New folder name. Omit to keep the current name."),
12055
+ description: import_zod19.z.string().optional().describe("New description. Omit to leave unchanged."),
12056
+ userId: import_zod19.z.string().optional().describe("Reassign the owning user (Vectros UUID)."),
12057
+ scopes: import_zod19.z.array(import_zod19.z.string()).optional().describe(
12025
12058
  'Reassign scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]) \u2014 the COMPLETE new parent set. `org`/`client` are reserved namespace names, registered like any other. Omit to leave ownership unchanged; `[]` clears it (private to the owning user).'
12026
12059
  ),
12027
- expectedVersion: import_zod18.z.number().int().optional().describe(
12060
+ expectedVersion: import_zod19.z.number().int().optional().describe(
12028
12061
  "Optimistic concurrency: the version you last read. If the folder changed since, the update is refused as a conflict (re-read and retry). Omit for last-write-wins."
12029
12062
  )
12030
12063
  };
@@ -12063,14 +12096,14 @@ var folderUpdate = ({ client, log }) => ({
12063
12096
  var folder_update_default = folderUpdate;
12064
12097
 
12065
12098
  // src/tools/folder_delete.ts
12066
- var import_zod19 = require("zod");
12099
+ var import_zod20 = require("zod");
12067
12100
  var inputSchema19 = {
12068
- id: import_zod19.z.string().min(1, "id is required").describe("The Vectros folder id to delete.")
12101
+ id: import_zod20.z.string().min(1, "id is required").describe("The Vectros folder id to delete.")
12069
12102
  };
12070
12103
  var folderDelete = ({ client, log }) => ({
12071
12104
  name: "folder_delete",
12072
12105
  title: "Delete a folder",
12073
- description: 'Permanently delete a folder by id. The folder must be EMPTY first \u2014 it must contain no documents, no records, and no sub-folders \u2014 and a non-empty folder is refused, which is the most common way this call fails. Use record_query with this folderId, and document_query, to see what is in it. Clearing it differs by content type: a document can be moved out (document_update with a different folderId) or deleted. A RECORD can also be re-filed into another folder \u2014 the API supports it \u2014 but THIS SERVER does not expose that yet: record_update has no folderId argument, so from here your only option is record_delete (needs records:d). Do NOT read that as "the record cannot be moved" and delete partner data on that basis: if the record should be kept, move it with the Vectros API/SDK directly rather than deleting it here. A sub-folder cannot be re-parented at all, so nested folders have to be emptied and deleted innermost-first. Note the emptiness check counts everything in the folder, INCLUDING items your credential cannot read \u2014 so a refusal you cannot account for from what you can list is expected behaviour, not a bug, and retrying will not clear it. Requires the key to allow folders:d \u2014 a key without it gets a permission error. Protected folders (e.g. a context root) cannot be deleted at all, however empty.',
12106
+ description: 'Permanently delete a folder by id. The folder must be EMPTY first \u2014 it must contain no documents, no records, and no sub-folders \u2014 and a non-empty folder is refused, which is the most common way this call fails. Use record_query with this folderId, and document_query, to see what is in it. Clearing it differs by content type: a document can be moved out (document_update with a different folderId) or deleted. A RECORD can also be re-filed into another folder \u2014 the API supports it \u2014 but THIS SERVER does not expose that yet: record_update has no folderId argument, so from here your only option is record_delete (needs records:d). Do NOT read that as "the record cannot be moved" and delete the record on that basis: if the record should be kept, tell the user to move it with the Vectros API/SDK directly rather than deleting it here. A sub-folder cannot be re-parented at all, so nested folders have to be emptied and deleted innermost-first. Note the emptiness check counts everything in the folder, INCLUDING items your credential cannot read \u2014 so a refusal you cannot account for from what you can list is expected behaviour, not a bug, and retrying will not clear it. Requires the key to allow folders:d \u2014 a key without it gets a permission error. Protected folders (e.g. a context root) cannot be deleted at all, however empty.',
12074
12107
  inputSchema: inputSchema19,
12075
12108
  // Deletes. Idempotent in the standard REST sense: the end state ("this id no longer
12076
12109
  // resolves") is the same whether this is the first delete or a repeat of one.
@@ -12115,7 +12148,7 @@ var currentIdentity = ({ log, apiKey, environment }) => ({
12115
12148
  var current_identity_default = currentIdentity;
12116
12149
 
12117
12150
  // src/tools/document_ingest.ts
12118
- var import_zod20 = require("zod");
12151
+ var import_zod21 = require("zod");
12119
12152
  var import_promises = require("fs/promises");
12120
12153
  var import_node_path = require("path");
12121
12154
  var IngestPathError = class extends Error {
@@ -12191,43 +12224,46 @@ function inferMimeType(filePath) {
12191
12224
  return EXT_TO_MIME[ext] ?? "application/octet-stream";
12192
12225
  }
12193
12226
  var inputSchema21 = {
12194
- title: import_zod20.z.string().min(1, "title is required").describe("Human-readable document title \u2014 appears in search results and the UI."),
12195
- indexMode: import_zod20.z.enum(["HYBRID", "SEMANTIC", "TEXT", "NONE"]).optional().describe(
12227
+ title: import_zod21.z.string().min(1, "title is required").describe("Human-readable document title \u2014 appears in search results and the UI."),
12228
+ indexMode: import_zod21.z.enum(["HYBRID", "SEMANTIC", "TEXT", "NONE"]).optional().describe(
12196
12229
  "Index strategy. HYBRID (BM25 + dense, default; best for general retrieval), SEMANTIC (dense only; concept-driven queries), TEXT (BM25 only; exact-match / keyword), NONE (store-only / archival \u2014 retrievable by id and structured-field lookup but never in search results). Omit to inherit the bound schema's default."
12197
12230
  ),
12198
- externalId: import_zod20.z.string().optional().describe(
12199
- 'Stable caller-supplied id. Immutable; unique per type within your context. Re-ingesting with the same externalId returns the existing document (idempotent) instead of creating a duplicate \u2014 use it to make ingests safely retryable and as the key other records reference. Mirrors record_create. Receiving the existing document back is a read of it and needs documents:r in addition to documents:c; a create-only key gets an "already exists" error instead of the document.'
12231
+ externalId: import_zod21.z.string().optional().describe(
12232
+ 'Stable caller-supplied id. Immutable; unique per type within your context. Re-ingesting with the same externalId returns the existing document (idempotent) instead of creating a duplicate \u2014 use it to make ingests safely retryable and as the key other records reference. Mirrors record_create. Receiving the existing document back is a read of it and needs documents:r in addition to documents:c; a create-only key gets an "already exists" error instead of the document. Pass `schemaId` with it: an externalId with no `schemaId` is refused (400) unless `confirmUntyped` is true.'
12200
12233
  ),
12201
- upsert: import_zod20.z.boolean().optional().describe(
12202
- "When true, re-ingesting an existing externalId OVERWRITES that document and re-indexes the new body (text mode) / re-uploads and re-indexes the new file (file mode), instead of returning the existing one unchanged. This is the curation/re-sync primitive: edit a source, re-ingest with upsert:true, and the search index reflects the new content. Requires the documents:u scope. IMPORTANT: pass the SAME schemaId (and type) you used originally \u2014 externalId is unique WITHIN a type, so omitting schemaId resolves in the untyped namespace and mints a duplicate instead of updating. The response `created` flag confirms which happened (created:true = a new document was minted)."
12234
+ upsert: import_zod21.z.boolean().optional().describe(
12235
+ "When true, re-ingesting an existing externalId OVERWRITES that document and re-indexes the new body (text mode) / re-uploads and re-indexes the new file (file mode), instead of returning the existing one unchanged. This is the curation/re-sync primitive: edit a source, re-ingest with upsert:true, and the search index reflects the new content. Requires the documents:u scope. IMPORTANT: pass the SAME schemaId (and type) you used originally \u2014 externalId is unique WITHIN a type. Omitting schemaId is refused (400) unless `confirmUntyped` is true, and with it the call targets the UNTYPED namespace, so it mints or updates an untyped document and never touches the typed one. The response `created` flag confirms which happened (created:true = a new document was minted)."
12203
12236
  ),
12204
- schemaId: import_zod20.z.string().optional().describe(
12205
- "Bind this document to a record schema. When set, `payload` is validated against the schema and its lookup fields become directly queryable via document_query (records parity). Resolve a schema id from its type via list_schemas. Omit for an untyped document."
12237
+ schemaId: import_zod21.z.string().optional().describe(
12238
+ "Bind this document to a record schema. When set, `payload` is validated against the schema and its lookup fields become directly queryable via document_query (records parity). Resolve a schema id from its type via list_schemas. Omit for an untyped document (an untyped document that also carries an `externalId` needs `confirmUntyped`)."
12206
12239
  ),
12207
- payload: import_zod20.z.record(import_zod20.z.string(), import_zod20.z.unknown()).optional().describe(
12240
+ confirmUntyped: import_zod21.z.boolean().optional().describe(
12241
+ "Set true to confirm that an UNTYPED document is what you intend when you pass `externalId` without `schemaId`. Without it the platform refuses that combination (400): externalId is unique per schema, so a forgotten `schemaId` would otherwise quietly create a separate untyped copy instead of reaching the typed document you meant. Do not set it just to get past that error \u2014 check that the document really is untyped first. Ignored when `schemaId` is given or `externalId` is omitted."
12242
+ ),
12243
+ payload: import_zod21.z.record(import_zod21.z.string(), import_zod21.z.unknown()).optional().describe(
12208
12244
  "The document's structured data \u2014 a flat key/value object. With `schemaId`, declared fields are validated and lookup fields indexed; undeclared keys pass through as free-form and are searchable via the hybrid_search `filters` param. Without a schema it is stored as free-form metadata. Every number must fall within the signed 64-bit range \u2014 send a larger whole number as a STRING (stored exactly, exact-match lookup), else the ingest is refused with a 400 naming the field."
12209
12245
  ),
12210
12246
  // Inline text mode (mutually exclusive with filePath):
12211
- text: import_zod20.z.string().min(1).optional().describe("Text body to ingest inline. Mutually exclusive with filePath. Either text or filePath is required."),
12212
- storeText: import_zod20.z.boolean().optional().describe(
12247
+ text: import_zod21.z.string().min(1).optional().describe("Text body to ingest inline. Mutually exclusive with filePath. Either text or filePath is required."),
12248
+ storeText: import_zod21.z.boolean().optional().describe(
12213
12249
  "FILE MODE ONLY. Whether the uploaded file's EXTRACTED text is retained after indexing (default true), fixed at ingest. true keeps the extracted text retrievable via document_get(includeText:true) and usable by document_ask; false discards it once indexing completes \u2014 search still works and the original file remains downloadable, but includeText returns textAvailable:false and document_ask is unavailable. NOT accepted with `text`: a text-ingested body is the document itself and is ALWAYS retained, so the backend has no storeText knob on the text path \u2014 passing it with `text` is rejected rather than silently ignored."
12214
12250
  ),
12215
12251
  // File upload mode (mutually exclusive with text; stdio-transport-only):
12216
- filePath: import_zod20.z.string().min(1).optional().describe(
12252
+ filePath: import_zod21.z.string().min(1).optional().describe(
12217
12253
  "Local filesystem path to upload. Mutually exclusive with text. STDIO TRANSPORT ONLY \u2014 rejected on HTTP transport (remote MCP servers can't read your filesystem; use text mode or call the SDK's uploadDocument directly). Must resolve INSIDE the configured ingest root (VECTROS_MCP_INGEST_ROOT, else the server's working directory); paths that escape it (traversal/absolute/symlink) or match a sensitive pattern are rejected."
12218
12254
  ),
12219
- fileType: import_zod20.z.string().optional().describe('MIME type of the file (e.g. "application/pdf"). Inferred from filePath extension if omitted.'),
12255
+ fileType: import_zod21.z.string().optional().describe('MIME type of the file (e.g. "application/pdf"). Inferred from filePath extension if omitted.'),
12220
12256
  // Common (both modes):
12221
- folderId: import_zod20.z.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
12222
- userId: import_zod20.z.string().optional().describe("Owning user ID. Optional."),
12223
- scopes: import_zod20.z.array(import_zod20.z.string()).optional().describe(
12257
+ folderId: import_zod21.z.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
12258
+ userId: import_zod21.z.string().optional().describe("Owning user ID. Optional."),
12259
+ scopes: import_zod21.z.array(import_zod21.z.string()).optional().describe(
12224
12260
  "TEXT MODE ONLY. Scope ownership as `namespace:value` entries, at most 2 (e.g. [\"org:<uuid>\", \"group:eng-team\"]). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself. This is the document's COMPLETE scope declaration and values must come from the credential's own identity. An empty array `[]` creates a PRIVATE document owned by the calling user alone. Omit to stamp the credential's full identity \u2014 the default. Rejected with `filePath` (file uploads do not accept a scope declaration): a file-uploaded document is stamped with the credential's full identity."
12225
12261
  )
12226
12262
  };
12227
12263
  var documentIngest = ({ client, log, transport, ingestRoot }) => ({
12228
12264
  name: "document_ingest",
12229
12265
  title: "Ingest a new document (text body or file upload)",
12230
- description: "Create a new document in your Vectros tenant. Two modes:\n \u2022 Text mode: pass `text` (string body). Single-call. Use for crawled content, notes, generated text.\n \u2022 File mode: pass `filePath` (local path on the MCP server's host machine). STDIO-TRANSPORT ONLY \u2014 rejected on HTTP transport. MCP server reads the bytes, requests a presigned upload URL, and PUTs them. Returns when the upload is accepted (indexStatus: PENDING_INDEX); poll with `document_get` until indexStatus is INDEXED.\nEither `text` or `filePath` must be present; both is an error. Idempotent by `externalId` (re-ingest returns the existing document, not a duplicate \u2014 requires documents:r in addition to documents:c, since being handed the existing document is a read of it). To UPDATE an existing document instead \u2014 re-index an edited body \u2014 pass `upsert:true` with the same `externalId` (and the same `schemaId`); this is the re-sync primitive for keeping a knowledge base current. Pass `schemaId` + `payload` for a typed, lookup-queryable document (records parity). indexMode defaults to HYBRID for untyped documents (omit to inherit a bound schema's default). storeText is a FILE-MODE knob (default true): false discards the uploaded file's extracted text after indexing (search + file download keep working; text retrieval and document_ask do not). Text-mode bodies are always retained, so storeText is not accepted with `text`.",
12266
+ description: "Create a new document in your Vectros tenant. Two modes:\n \u2022 Text mode: pass `text` (string body). Single-call. Use for crawled content, notes, generated text.\n \u2022 File mode: pass `filePath` (local path on the MCP server's host machine). STDIO-TRANSPORT ONLY \u2014 rejected on HTTP transport. MCP server reads the bytes, requests a presigned upload URL, and PUTs them. Returns when the upload is accepted (indexStatus: PENDING_INDEX); poll `document_get` until indexStatus is INDEXED. EXCEPTION \u2014 re-ingesting file mode against an externalId that already has a document (response `created:false`): the file body is replaced by ANY such re-ingest, whether or not `upsert` was set (`upsert` there only additionally applies `payload`/`title` to the matched document). The document keeps serving the OLD file's bytes until the new one is adopted, and indexStatus already reads INDEXED left over from that old file, so polling it returns immediately without waiting for the new content. Poll `document_get` and watch `fileItemId` instead \u2014 once it differs from the value it held before this call, the new file has been adopted. Re-request any download URL after that: one minted before adoption can stop working even within its stated `downloadExpires`.\nEither `text` or `filePath` must be present; both is an error. Idempotent by `externalId` (re-ingest returns the existing document, not a duplicate \u2014 requires documents:r in addition to documents:c, since being handed the existing document is a read of it; in FILE MODE a re-ingest against an existing externalId needs documents:u). TEXT MODE: without `upsert`, the existing document's content is genuinely unchanged. FILE MODE: as noted above, a re-ingest against an existing externalId always replaces the file body regardless of `upsert` \u2014 only the `payload`/`title` metadata overwrite is gated by it. To UPDATE an existing document instead \u2014 re-index an edited body \u2014 pass `upsert:true` with the same `externalId` (and the same `schemaId`); this is the re-sync primitive for keeping a knowledge base current. Pass `schemaId` + `payload` for a typed, lookup-queryable document (records parity). An `externalId` with no `schemaId` is refused (400) unless you also pass `confirmUntyped:true` to confirm that an untyped document is intended. indexMode defaults to HYBRID for untyped documents (omit to inherit a bound schema's default). storeText is a FILE-MODE knob (default true): false discards the uploaded file's extracted text after indexing (search + file download keep working; text retrieval and document_ask do not). Text-mode bodies are always retained, so storeText is not accepted with `text`.",
12231
12267
  inputSchema: inputSchema21,
12232
12268
  // Creates by default, idempotent ONLY when the caller supplies `externalId` (optional,
12233
12269
  // per the doc comment above) — re-ingest then returns the existing document rather than
@@ -12245,6 +12281,7 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
12245
12281
  const filePath = args.filePath;
12246
12282
  const schemaId = args.schemaId;
12247
12283
  const upsert = args.upsert;
12284
+ const confirmUntyped = args.confirmUntyped;
12248
12285
  const indexMode = args.indexMode ?? (schemaId ? void 0 : "HYBRID");
12249
12286
  if (!text && !filePath) {
12250
12287
  return toolError(
@@ -12293,6 +12330,7 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
12293
12330
  if (text) {
12294
12331
  const result = await client.documents.ingestDocument({
12295
12332
  upsert,
12333
+ confirmUntyped,
12296
12334
  body: {
12297
12335
  title,
12298
12336
  text,
@@ -12333,6 +12371,7 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
12333
12371
  const storeTextFile = args.storeText ?? true;
12334
12372
  const upload = await client.documents.uploadDocument({
12335
12373
  upsert,
12374
+ confirmUntyped,
12336
12375
  fileName,
12337
12376
  fileType,
12338
12377
  indexMode,
@@ -12365,6 +12404,8 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
12365
12404
  { tool: "document_ingest", mode: "file", id: docId, fileName, fileType, bytes: bytes.length },
12366
12405
  "document_ingest file ok"
12367
12406
  );
12407
+ const uploadCreated = upload?.created;
12408
+ const note = uploadCreated === false ? "File replaced: this re-ingest matched an existing externalId, which always replaces that document's file body (upsert or not \u2014 upsert only additionally applies payload/title to it). The next document_get call may already show indexStatus: INDEXED \u2014 that is left over from the PREVIOUS file, not confirmation the new one is ready, and the document keeps serving the old file's bytes until adoption completes. Poll document_get(id) and watch fileItemId instead: once it differs from the value it held before this call, the new file has been adopted. Any download URL already held for this document was minted for the old file and can stop working before its stated downloadExpires once adoption happens \u2014 request a fresh one after fileItemId changes." : "File uploaded; indexing is asynchronous. Poll document_get(id) until indexStatus is INDEXED.";
12368
12409
  return {
12369
12410
  content: [
12370
12411
  {
@@ -12373,7 +12414,7 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
12373
12414
  {
12374
12415
  ...upload,
12375
12416
  indexStatus: "PENDING_INDEX",
12376
- _note: "File uploaded; indexing is asynchronous. Poll document_get(id) until indexStatus is INDEXED."
12417
+ _note: note
12377
12418
  },
12378
12419
  null,
12379
12420
  2
@@ -12397,29 +12438,29 @@ function presignedUploadHeaders(response) {
12397
12438
  }
12398
12439
 
12399
12440
  // src/tools/lookup_principal.ts
12400
- var import_zod21 = require("zod");
12441
+ var import_zod22 = require("zod");
12401
12442
  var MCP_DEFAULT_LIMIT5 = 10;
12402
12443
  var MCP_MAX_LIMIT5 = 100;
12403
12444
  var inputSchema22 = {
12404
- kind: import_zod21.z.string().describe(
12445
+ kind: import_zod22.z.string().describe(
12405
12446
  'Which principal to look up: "user" (the fixed principal surface), or an entity namespace \u2014 "org", "client" (reserved, registered like any other), or a namespace you registered.'
12406
12447
  ),
12407
- externalId: import_zod21.z.string().optional().describe(
12448
+ externalId: import_zod22.z.string().optional().describe(
12408
12449
  "Resolve mode: your own stable identifier for the principal. Returns the single matching principal (with its Vectros UUID), or an empty array if none. The fastest path to the UUID the ownership filters need. Takes precedence if both this and a `field` lookup are supplied."
12409
12450
  ),
12410
- contextId: import_zod21.z.string().optional().describe(
12451
+ contextId: import_zod22.z.string().optional().describe(
12411
12452
  "Entity kinds only (not `user`, which is always tenant-wide): the app context to look up entities in, for a namespace registered as context-owned rather than tenant-wide. A context-confined credential's own context is used automatically without this; supply it to name a context explicitly (required for an unconfined/root credential to reach a context-owned namespace). Rejected if the namespace turns out to be tenant-wide."
12412
12453
  ),
12413
12454
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
12414
- type: import_zod21.z.string().optional().describe('Lookup mode: the identity schema\'s record type (e.g. "person_v1"). Required with `field`.'),
12415
- field: import_zod21.z.string().optional().describe('Lookup mode: name of the schema-declared lookup field to query by (e.g. "email").'),
12416
- value: import_zod21.z.string().optional().describe("Lookup mode (equality): exact-match value for `field`. Works on sensitive fields."),
12417
- from: import_zod21.z.string().optional().describe("Lookup mode (range): inclusive lower bound; requires `to`. Non-sensitive fields only."),
12418
- to: import_zod21.z.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
12419
- prefix: import_zod21.z.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
12420
- order: import_zod21.z.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
12421
- startFrom: import_zod21.z.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
12422
- limit: import_zod21.z.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
12455
+ type: import_zod22.z.string().optional().describe('Lookup mode: the identity schema\'s record type (e.g. "person_v1"). Required with `field`.'),
12456
+ field: import_zod22.z.string().optional().describe('Lookup mode: name of the schema-declared lookup field to query by (e.g. "email").'),
12457
+ value: import_zod22.z.string().optional().describe("Lookup mode (equality): exact-match value for `field`. Works on sensitive fields."),
12458
+ from: import_zod22.z.string().optional().describe("Lookup mode (range): inclusive lower bound; requires `to`. Non-sensitive fields only."),
12459
+ to: import_zod22.z.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
12460
+ prefix: import_zod22.z.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
12461
+ order: import_zod22.z.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
12462
+ startFrom: import_zod22.z.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
12463
+ limit: import_zod22.z.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
12423
12464
  `Max principals per page. Defaults to ${MCP_DEFAULT_LIMIT5}; raise up to ${MCP_MAX_LIMIT5} (the principals API max) in one call when you can accept the payload, or page with \`startFrom\`.`
12424
12465
  )
12425
12466
  };
@@ -12503,11 +12544,11 @@ var lookupPrincipal = ({ client, log }) => ({
12503
12544
  var lookup_principal_default = lookupPrincipal;
12504
12545
 
12505
12546
  // src/tools/version_history.ts
12506
- var import_zod22 = require("zod");
12547
+ var import_zod23 = require("zod");
12507
12548
  var inputSchema23 = {
12508
- resourceType: import_zod22.z.enum(["record", "document"]).describe("Which kind of entity the id refers to: a record or a document."),
12509
- id: import_zod22.z.string().min(1, "id is required").describe("The Vectros id of the record or document whose history you want."),
12510
- startFrom: import_zod22.z.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch older entries.")
12549
+ resourceType: import_zod23.z.enum(["record", "document"]).describe("Which kind of entity the id refers to: a record or a document."),
12550
+ id: import_zod23.z.string().min(1, "id is required").describe("The Vectros id of the record or document whose history you want."),
12551
+ startFrom: import_zod23.z.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch older entries.")
12511
12552
  };
12512
12553
  var versionHistory = ({ client, log }) => ({
12513
12554
  name: "version_history",
@@ -12631,7 +12672,7 @@ var ALL_RESOURCE_FACTORIES = {
12631
12672
  };
12632
12673
 
12633
12674
  // src/zod-to-json-schema.ts
12634
- var import_zod23 = require("zod");
12675
+ var import_zod24 = require("zod");
12635
12676
  function zodShapeToJsonSchema(shape) {
12636
12677
  const properties = {};
12637
12678
  const required = [];
@@ -12646,40 +12687,40 @@ function zodShapeToJsonSchema(shape) {
12646
12687
  function zodTypeToJsonSchema(schema) {
12647
12688
  const description = schema.description;
12648
12689
  let current = schema;
12649
- while (current instanceof import_zod23.z.ZodOptional || current instanceof import_zod23.z.ZodDefault || current instanceof import_zod23.z.ZodNullable) {
12690
+ while (current instanceof import_zod24.z.ZodOptional || current instanceof import_zod24.z.ZodDefault || current instanceof import_zod24.z.ZodNullable) {
12650
12691
  current = current._def.innerType;
12651
12692
  }
12652
12693
  const base = description ? { description } : {};
12653
- if (current instanceof import_zod23.z.ZodString) {
12694
+ if (current instanceof import_zod24.z.ZodString) {
12654
12695
  return { ...base, type: "string" };
12655
12696
  }
12656
- if (current instanceof import_zod23.z.ZodNumber) {
12697
+ if (current instanceof import_zod24.z.ZodNumber) {
12657
12698
  return { ...base, type: "number" };
12658
12699
  }
12659
- if (current instanceof import_zod23.z.ZodBoolean) {
12700
+ if (current instanceof import_zod24.z.ZodBoolean) {
12660
12701
  return { ...base, type: "boolean" };
12661
12702
  }
12662
- if (current instanceof import_zod23.z.ZodEnum) {
12703
+ if (current instanceof import_zod24.z.ZodEnum) {
12663
12704
  return {
12664
12705
  ...base,
12665
12706
  type: "string",
12666
12707
  enum: current.options
12667
12708
  };
12668
12709
  }
12669
- if (current instanceof import_zod23.z.ZodArray) {
12710
+ if (current instanceof import_zod24.z.ZodArray) {
12670
12711
  return {
12671
12712
  ...base,
12672
12713
  type: "array",
12673
12714
  items: zodTypeToJsonSchema(current.element)
12674
12715
  };
12675
12716
  }
12676
- if (current instanceof import_zod23.z.ZodObject) {
12717
+ if (current instanceof import_zod24.z.ZodObject) {
12677
12718
  return {
12678
12719
  ...base,
12679
12720
  ...zodShapeToJsonSchema(current.shape)
12680
12721
  };
12681
12722
  }
12682
- if (current instanceof import_zod23.z.ZodRecord) {
12723
+ if (current instanceof import_zod24.z.ZodRecord) {
12683
12724
  const valueType = current._def.valueType;
12684
12725
  return {
12685
12726
  ...base,
@@ -12808,7 +12849,7 @@ var VectrosMCPServer = class {
12808
12849
  if (!tool) {
12809
12850
  result = toolError(req.params.name ?? "unknown", new Error("No such tool."));
12810
12851
  } else {
12811
- const parsed = import_zod24.z.object(tool.inputSchema).strict().safeParse(req.params.arguments ?? {});
12852
+ const parsed = import_zod25.z.object(tool.inputSchema).strict().safeParse(req.params.arguments ?? {});
12812
12853
  if (!parsed.success) {
12813
12854
  result = toolError(tool.name, new Error(formatArgError(tool, parsed.error)));
12814
12855
  } else {