@vectros-ai/mcp-server 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -691,6 +691,10 @@ var ReadAccessLogResponse;
691
691
  })(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
692
692
  var RecordRequest;
693
693
  ((RecordRequest2) => {
694
+ RecordRequest2.Status = {
695
+ Active: "ACTIVE",
696
+ Archived: "ARCHIVED"
697
+ };
694
698
  RecordRequest2.IndexMode = {
695
699
  Hybrid: "HYBRID",
696
700
  Semantic: "SEMANTIC",
@@ -700,6 +704,10 @@ var RecordRequest;
700
704
  })(RecordRequest || (RecordRequest = {}));
701
705
  var RecordResponse;
702
706
  ((RecordResponse2) => {
707
+ RecordResponse2.Status = {
708
+ Active: "ACTIVE",
709
+ Archived: "ARCHIVED"
710
+ };
703
711
  RecordResponse2.IndexStatus = {
704
712
  PendingIndex: "PENDING_INDEX",
705
713
  Indexed: "INDEXED",
@@ -3174,7 +3182,7 @@ var AuthClient = class {
3174
3182
  * roleId: "engineering-member",
3175
3183
  * name: "Engineering Team Member",
3176
3184
  * scopes: [{
3177
- * allowed_actions: ["read", "write"]
3185
+ * allowed_actions: ["records:cru", "search:r"]
3178
3186
  * }]
3179
3187
  * }
3180
3188
  * })
@@ -3722,7 +3730,7 @@ var AuthClient = class {
3722
3730
  * roleId: "engineering-member",
3723
3731
  * name: "Engineering Team Member",
3724
3732
  * scopes: [{
3725
- * allowed_actions: ["read", "write"]
3733
+ * allowed_actions: ["records:cru", "search:r"]
3726
3734
  * }]
3727
3735
  * }
3728
3736
  * })
@@ -4625,7 +4633,7 @@ var DocumentsClient = class {
4625
4633
  this._options = normalizeClientOptionsWithAuth(options);
4626
4634
  }
4627
4635
  /**
4628
- * Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, or `clientId`). The response is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom` to fetch the next page. Requires the `documents:r` scope.
4636
+ * Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, `clientId`, or `scope`). The response is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom` to fetch the next page. Requires the `documents:r` scope.
4629
4637
  *
4630
4638
  * @param {Vectros.ListDocumentsRequest} request
4631
4639
  * @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -4635,6 +4643,7 @@ var DocumentsClient = class {
4635
4643
  * userId: "550e8400-e29b-41d4-a716-446655440000",
4636
4644
  * orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
4637
4645
  * clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
4646
+ * scope: "group:eng-team",
4638
4647
  * folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
4639
4648
  * startFrom: "doc_prev123"
4640
4649
  * })
@@ -4643,11 +4652,12 @@ var DocumentsClient = class {
4643
4652
  return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
4644
4653
  }
4645
4654
  async __listDocuments(request = {}, requestOptions) {
4646
- const { userId, orgId, clientId, folderId, startFrom, limit } = request;
4655
+ const { userId, orgId, clientId, scope, folderId, startFrom, limit } = request;
4647
4656
  const _queryParams = {
4648
4657
  userId,
4649
4658
  orgId,
4650
4659
  clientId,
4660
+ scope,
4651
4661
  folderId,
4652
4662
  startFrom,
4653
4663
  limit
@@ -4705,9 +4715,10 @@ var DocumentsClient = class {
4705
4715
  return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
4706
4716
  }
4707
4717
  async __ingestDocument(request, requestOptions) {
4708
- const { upsert, body: _body } = request;
4718
+ const { upsert, allowClear, body: _body } = request;
4709
4719
  const _queryParams = {
4710
- upsert
4720
+ upsert,
4721
+ allowClear
4711
4722
  };
4712
4723
  const _authRequest = await this._options.authProvider.getAuthRequest();
4713
4724
  const _headers = mergeHeaders(
@@ -4831,7 +4842,10 @@ var DocumentsClient = class {
4831
4842
  return HttpResponsePromise.fromPromise(this.__updateDocument(request, requestOptions));
4832
4843
  }
4833
4844
  async __updateDocument(request, requestOptions) {
4834
- const { id, body: _body } = request;
4845
+ const { id, allowClear, body: _body } = request;
4846
+ const _queryParams = {
4847
+ allowClear
4848
+ };
4835
4849
  const _authRequest = await this._options.authProvider.getAuthRequest();
4836
4850
  const _headers = mergeHeaders(
4837
4851
  _authRequest.headers,
@@ -4846,7 +4860,7 @@ var DocumentsClient = class {
4846
4860
  method: "PUT",
4847
4861
  headers: _headers,
4848
4862
  contentType: "application/json",
4849
- queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
4863
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
4850
4864
  requestType: "json",
4851
4865
  body: _body,
4852
4866
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
@@ -7582,7 +7596,7 @@ var RecordsClient = class {
7582
7596
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch");
7583
7597
  }
7584
7598
  /**
7585
- * Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
7599
+ * Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`, `scope`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
7586
7600
  *
7587
7601
  * @param {Vectros.ListRecordsRequest} request
7588
7602
  * @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -7594,6 +7608,7 @@ var RecordsClient = class {
7594
7608
  * userId: "550e8400-e29b-41d4-a716-446655440000",
7595
7609
  * orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
7596
7610
  * clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
7611
+ * scope: "group:eng-team",
7597
7612
  * startFrom: "550e8400-e29b-41d4-a716-446655440000"
7598
7613
  * })
7599
7614
  */
@@ -7601,13 +7616,25 @@ var RecordsClient = class {
7601
7616
  return HttpResponsePromise.fromPromise(this.__listRecords(request, requestOptions));
7602
7617
  }
7603
7618
  async __listRecords(request = {}, requestOptions) {
7604
- const { type: type_, folderId, userId, orgId, clientId, startFrom, limit, includePayload, recent } = request;
7619
+ const {
7620
+ type: type_,
7621
+ folderId,
7622
+ userId,
7623
+ orgId,
7624
+ clientId,
7625
+ scope,
7626
+ startFrom,
7627
+ limit,
7628
+ includePayload,
7629
+ recent
7630
+ } = request;
7605
7631
  const _queryParams = {
7606
7632
  type: type_,
7607
7633
  folderId,
7608
7634
  userId,
7609
7635
  orgId,
7610
7636
  clientId,
7637
+ scope,
7611
7638
  startFrom,
7612
7639
  limit,
7613
7640
  includePayload,
@@ -7672,9 +7699,10 @@ var RecordsClient = class {
7672
7699
  return HttpResponsePromise.fromPromise(this.__createRecord(request, requestOptions));
7673
7700
  }
7674
7701
  async __createRecord(request, requestOptions) {
7675
- const { upsert, body: _body } = request;
7702
+ const { upsert, allowClear, body: _body } = request;
7676
7703
  const _queryParams = {
7677
- upsert
7704
+ upsert,
7705
+ allowClear
7678
7706
  };
7679
7707
  const _authRequest = await this._options.authProvider.getAuthRequest();
7680
7708
  const _headers = mergeHeaders(
@@ -7796,7 +7824,10 @@ var RecordsClient = class {
7796
7824
  return HttpResponsePromise.fromPromise(this.__updateRecord(request, requestOptions));
7797
7825
  }
7798
7826
  async __updateRecord(request, requestOptions) {
7799
- const { id, body: _body } = request;
7827
+ const { id, allowClear, body: _body } = request;
7828
+ const _queryParams = {
7829
+ allowClear
7830
+ };
7800
7831
  const _authRequest = await this._options.authProvider.getAuthRequest();
7801
7832
  const _headers = mergeHeaders(
7802
7833
  _authRequest.headers,
@@ -7811,7 +7842,7 @@ var RecordsClient = class {
7811
7842
  method: "PUT",
7812
7843
  headers: _headers,
7813
7844
  contentType: "application/json",
7814
- queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
7845
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
7815
7846
  requestType: "json",
7816
7847
  body: _body,
7817
7848
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
@@ -8798,8 +8829,8 @@ function warnOnSuboptimalKey(key, log) {
8798
8829
 
8799
8830
  // src/build-info.ts
8800
8831
  var BUILD_INFO = {
8801
- mcpServer: true ? "0.7.0" : "dev",
8802
- sdk: true ? "0.33.0" : "dev"
8832
+ mcpServer: true ? "0.9.0" : "dev",
8833
+ sdk: true ? "0.34.0" : "dev"
8803
8834
  };
8804
8835
 
8805
8836
  // src/log.ts
@@ -9035,7 +9066,7 @@ ${detail}${pointer}`
9035
9066
 
9036
9067
  // src/tools/hybrid_search.ts
9037
9068
  var MCP_DEFAULT_LIMIT = 3;
9038
- var MCP_MAX_LIMIT = 10;
9069
+ var MCP_MAX_LIMIT = 50;
9039
9070
  var MCP_RESPONSE_CHAR_BUDGET = 24e3;
9040
9071
  var SNIPPET_MAX_CHARS = 160;
9041
9072
  var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["tenantId", "owner_id", "model_type", "rootFolderId", "folderId"]);
@@ -9069,9 +9100,9 @@ var inputSchema = {
9069
9100
  "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.)"
9070
9101
  ),
9071
9102
  limit: import_zod.z.number().int().min(1).max(MCP_MAX_LIMIT).optional().describe(
9072
- `Max hits to return. MCP-specific cap of ${MCP_MAX_LIMIT} (vs API max of 50) to protect the agent context window. Default 3. Use offset for pagination.`
9103
+ `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.`
9073
9104
  ),
9074
- offset: import_zod.z.number().int().min(0).optional().describe("Skip the first N hits \u2014 for pagination."),
9105
+ offset: import_zod.z.number().int().min(0).max(200).optional().describe("Skip the first N hits \u2014 for pagination (0\u2013200)."),
9075
9106
  folderId: import_zod.z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
9076
9107
  rootFolderId: import_zod.z.string().optional().describe("Scope to content under this folder AND all its descendants (subtree-root folder id)."),
9077
9108
  // Content narrowing.
@@ -9086,6 +9117,9 @@ var inputSchema = {
9086
9117
  userId: import_zod.z.string().optional().describe("Restrict to content owned by this user (Vectros UUID)."),
9087
9118
  orgId: import_zod.z.string().optional().describe("Restrict to content belonging to this org (Vectros UUID)."),
9088
9119
  clientId: import_zod.z.string().optional().describe("Restrict to content associated with this client (Vectros UUID)."),
9120
+ scope: import_zod.z.string().optional().describe(
9121
+ 'Restrict to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
9122
+ ),
9089
9123
  // Date window.
9090
9124
  createdAfter: import_zod.z.string().optional().describe("Restrict to content created at/after this ISO 8601 UTC timestamp."),
9091
9125
  createdBefore: import_zod.z.string().optional().describe("Restrict to content created at/before this ISO 8601 UTC timestamp."),
@@ -9103,7 +9137,7 @@ var inputSchema = {
9103
9137
  var hybridSearch = ({ client, log }) => ({
9104
9138
  name: "hybrid_search",
9105
9139
  title: "Hybrid search",
9106
- description: `Search the partner tenant's indexed content (documents + structured records) using hybrid BM25 + dense ranking. Returns up to 10 results (default 3) with citations and surrounding context for grounding follow-up reasoning. Narrow with contentTypes/typeName/filters, ownership (userId/orgId/clientId), folder scope, a created date window, keyword precision (textMode OR/AND/PHRASE), relevance floors (minSimilarity/minTextRelevance), uniqueDocuments, and requireComplete (fail closed on a degraded backend). Tenant-isolated; the caller's scoped key fully constrains which content is visible. ARCHIVED (soft-retracted) documents never appear in results \u2014 fetch them by id via document_get, or restore them via document_update. To stay within the agent context window each hit returns its matched chunkText + a short snippet by default; set includeContext:true for the broader surrounding passage. If the whole result would be too large it is truncated to the top hits (truncated:true). textLegEmpty:true flags that the keyword leg matched nothing (all textScores 0) \u2014 usually a too-long PHRASE query; retry with textMode:"OR".`,
9140
+ description: "Search your tenant's indexed content (documents + structured records) using hybrid BM25 + dense ranking. Returns up to `limit` results (default 3, max 50 \u2014 raise it in one call when you can accept the larger result) with citations and surrounding context for grounding follow-up reasoning. Narrow with contentTypes/typeName/filters, ownership (userId/orgId/clientId), folder scope, a created date window, keyword precision (textMode OR/AND/PHRASE), relevance floors (minSimilarity/minTextRelevance), uniqueDocuments, and requireComplete (fail closed on a degraded backend). Tenant-isolated; the caller's scoped key fully constrains which content is visible. ARCHIVED (soft-retracted) documents never appear in results \u2014 fetch them by id via document_get, or restore them via document_update. To stay within the agent context window each hit returns its matched chunkText + a short snippet by default; set includeContext:true for the broader surrounding passage. If the whole result would be too large it is truncated to the top hits (truncated:true). totalResults is the full matching pool and hasMore:true means results remain past this page \u2014 raise limit or advance offset to fetch them. textLegEmpty:true flags that the keyword leg matched nothing (all textScores 0) \u2014 usually a too-long PHRASE query; retry with textMode:\"OR\".",
9107
9141
  inputSchema,
9108
9142
  handler: async (args) => {
9109
9143
  const limit = args.limit ?? MCP_DEFAULT_LIMIT;
@@ -9123,6 +9157,7 @@ var hybridSearch = ({ client, log }) => ({
9123
9157
  userId: args.userId,
9124
9158
  orgId: args.orgId,
9125
9159
  clientId: args.clientId,
9160
+ scope: args.scope,
9126
9161
  createdAfter: args.createdAfter,
9127
9162
  createdBefore: args.createdBefore,
9128
9163
  minSimilarity: args.minSimilarity,
@@ -9149,6 +9184,12 @@ var hybridSearch = ({ client, log }) => ({
9149
9184
  envelope.returnedResults = shaped.length;
9150
9185
  text = JSON.stringify(envelope, null, 2);
9151
9186
  }
9187
+ const offset = args.offset ?? 0;
9188
+ const totalResults = result.totalResults;
9189
+ if (typeof totalResults === "number" && offset + shaped.length < totalResults) {
9190
+ envelope.hasMore = true;
9191
+ text = JSON.stringify(envelope, null, 2);
9192
+ }
9152
9193
  log.debug(
9153
9194
  { tool: "hybrid_search", mode, limit, returned: shaped.length, textLegEmpty },
9154
9195
  "hybrid_search ok"
@@ -9184,7 +9225,7 @@ async function drainPages(fetchPage) {
9184
9225
 
9185
9226
  // src/tools/record_query.ts
9186
9227
  var MCP_DEFAULT_LIMIT2 = 3;
9187
- var MCP_MAX_LIMIT2 = 10;
9228
+ var MCP_MAX_LIMIT2 = 100;
9188
9229
  var inputSchema2 = {
9189
9230
  type: import_zod2.z.string().min(1, "type (record type) is required").describe('Record type / schema name (e.g. "patient", "clinical_note").'),
9190
9231
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
@@ -9202,14 +9243,18 @@ var inputSchema2 = {
9202
9243
  userId: import_zod2.z.string().optional().describe("List mode: scope to records owned by this user."),
9203
9244
  orgId: import_zod2.z.string().optional().describe("List mode: scope to records owned by this org."),
9204
9245
  clientId: import_zod2.z.string().optional().describe("List mode: scope to records associated with this client."),
9246
+ scope: import_zod2.z.string().optional().describe(
9247
+ 'List mode: filter to records carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
9248
+ ),
9249
+ startFrom: import_zod2.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."),
9205
9250
  limit: import_zod2.z.number().int().min(1).max(MCP_MAX_LIMIT2).optional().describe(
9206
- `Max records to return. MCP-specific cap of ${MCP_MAX_LIMIT2} (vs API max of 100) to protect the agent context window. Default 3.`
9251
+ `Max records per page. Defaults to a low ${MCP_DEFAULT_LIMIT2} to protect the agent context window; raise up to ${MCP_MAX_LIMIT2} (the records API max) in one call when you can accept the larger payload, or page with \`startFrom\` instead.`
9207
9252
  )
9208
9253
  };
9209
9254
  var recordQuery = ({ client, log }) => ({
9210
9255
  name: "record_query",
9211
9256
  title: "Record query (list or lookup)",
9212
- description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields too.\n \u2022 List by type: omit `field`; optionally filter by `userId`/`orgId`/`clientId`.\nReturns up to 10 records (default 3). Mode is auto-detected from the arguments present.",
9257
+ description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields too.\n \u2022 List by type: omit `field`; optionally filter by `userId`/`orgId`/`clientId`.\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` records (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected from the arguments present.",
9213
9258
  inputSchema: inputSchema2,
9214
9259
  handler: async (args) => {
9215
9260
  const limit = args.limit ?? MCP_DEFAULT_LIMIT2;
@@ -9219,8 +9264,9 @@ var recordQuery = ({ client, log }) => ({
9219
9264
  const from = args.from;
9220
9265
  const to = args.to;
9221
9266
  const prefix = args.prefix;
9267
+ const startFrom = args.startFrom;
9222
9268
  try {
9223
- let records;
9269
+ let page;
9224
9270
  if (field) {
9225
9271
  const hasEquality = value !== void 0;
9226
9272
  const hasRange = from !== void 0 || to !== void 0;
@@ -9243,7 +9289,7 @@ var recordQuery = ({ client, log }) => ({
9243
9289
  if (hasRange && !(from !== void 0 && to !== void 0)) {
9244
9290
  return toolError("record_query", new Error("range lookup requires both 'from' and 'to'."));
9245
9291
  }
9246
- const page = await client.records.lookupRecordsByBody({
9292
+ page = await client.records.lookupRecordsByBody({
9247
9293
  type,
9248
9294
  field,
9249
9295
  value,
@@ -9251,29 +9297,32 @@ var recordQuery = ({ client, log }) => ({
9251
9297
  to,
9252
9298
  prefix,
9253
9299
  order: args.order,
9300
+ startFrom,
9254
9301
  limit
9255
9302
  });
9256
- records = pageItems(page);
9257
9303
  log.debug(
9258
- { tool: "record_query", mode: "lookup", type, field, returned: records.length },
9304
+ { tool: "record_query", mode: "lookup", type, field, returned: pageItems(page).length },
9259
9305
  "record_query lookup ok"
9260
9306
  );
9261
9307
  } else {
9262
- const page = await client.records.listRecords({
9308
+ page = await client.records.listRecords({
9263
9309
  type,
9264
9310
  userId: args.userId,
9265
9311
  orgId: args.orgId,
9266
9312
  clientId: args.clientId,
9313
+ scope: args.scope,
9314
+ startFrom,
9267
9315
  limit
9268
9316
  });
9269
- records = pageItems(page);
9270
9317
  log.debug(
9271
- { tool: "record_query", mode: "list", type, limit, returned: records.length },
9318
+ { tool: "record_query", mode: "list", type, limit, returned: pageItems(page).length },
9272
9319
  "record_query list ok"
9273
9320
  );
9274
9321
  }
9322
+ const records = pageItems(page);
9323
+ const nextCursor = page.nextCursor ?? null;
9275
9324
  return {
9276
- content: [{ type: "text", text: JSON.stringify(records, null, 2) }]
9325
+ content: [{ type: "text", text: JSON.stringify({ data: records, nextCursor }, null, 2) }]
9277
9326
  };
9278
9327
  } catch (err) {
9279
9328
  log.warn({ tool: "record_query", err: String(err) }, "record_query failed");
@@ -9396,7 +9445,10 @@ var inputSchema4 = {
9396
9445
  folderId: import_zod4.z.string().optional().describe("Group this record into a folder."),
9397
9446
  userId: import_zod4.z.string().optional().describe("Owning user id."),
9398
9447
  orgId: import_zod4.z.string().optional().describe("Owning organization id."),
9399
- clientId: import_zod4.z.string().optional().describe("Associated client id.")
9448
+ clientId: import_zod4.z.string().optional().describe("Associated client id."),
9449
+ scopes: import_zod4.z.array(import_zod4.z.string()).optional().describe(
9450
+ 'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). This is the record\'s COMPLETE scope declaration and values must come from the credential\'s own identity. An empty array `[]` creates a PRIVATE record owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields.'
9451
+ )
9400
9452
  };
9401
9453
  var recordCreate = ({ client, log }) => ({
9402
9454
  name: "record_create",
@@ -9416,7 +9468,8 @@ var recordCreate = ({ client, log }) => ({
9416
9468
  folderId: args.folderId,
9417
9469
  userId: args.userId,
9418
9470
  orgId: args.orgId,
9419
- clientId: args.clientId
9471
+ clientId: args.clientId,
9472
+ scopes: args.scopes
9420
9473
  }
9421
9474
  });
9422
9475
  log.debug({ tool: "record_create", type, id: created.id }, "record_create ok");
@@ -9501,24 +9554,29 @@ var record_delete_default = recordDelete;
9501
9554
  // src/tools/rag_ask.ts
9502
9555
  var import_zod7 = require("zod");
9503
9556
  var SEARCH_MCP_DEFAULT_LIMIT = 5;
9504
- var SEARCH_MCP_MAX_LIMIT = 10;
9557
+ var SEARCH_MCP_MAX_LIMIT = 50;
9505
9558
  var inputSchema7 = {
9506
9559
  query: import_zod7.z.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
9507
9560
  instructions: import_zod7.z.string().optional().describe(
9508
9561
  '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.'
9509
9562
  ),
9510
9563
  model: import_zod7.z.string().optional().describe(
9511
- "Inference model alias (e.g. claude-haiku-4-5, claude-sonnet-4-6, claude-opus-4-8). Default = tier-appropriate Haiku. See GET /v1/models for the catalog the calling key can reach."
9564
+ "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."
9512
9565
  ),
9513
9566
  search: import_zod7.z.object({
9514
9567
  mode: import_zod7.z.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
9515
- limit: import_zod7.z.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(`Passages to retrieve before generating. Default ${SEARCH_MCP_DEFAULT_LIMIT}, max ${SEARCH_MCP_MAX_LIMIT}.`),
9568
+ limit: import_zod7.z.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
9569
+ `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.`
9570
+ ),
9516
9571
  // Retrieval scoping — parity with hybrid_search so an agent can ground the
9517
9572
  // answer on a specific owner / folder / type / metadata subset, not just the
9518
9573
  // whole-tenant corpus.
9519
9574
  userId: import_zod7.z.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
9520
9575
  orgId: import_zod7.z.string().optional().describe("Restrict retrieval to content owned by this org (Vectros UUID)."),
9521
9576
  clientId: import_zod7.z.string().optional().describe("Restrict retrieval to content tagged with this client (Vectros UUID)."),
9577
+ scope: import_zod7.z.string().optional().describe(
9578
+ 'Restrict retrieval to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
9579
+ ),
9522
9580
  folderId: import_zod7.z.string().optional().describe("Restrict retrieval to this exact folder."),
9523
9581
  rootFolderId: import_zod7.z.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
9524
9582
  typeName: import_zod7.z.string().optional().describe("Restrict record retrieval to this schema type."),
@@ -9536,7 +9594,7 @@ var inputSchema7 = {
9536
9594
  var ragAsk = ({ client, log }) => ({
9537
9595
  name: "rag_ask",
9538
9596
  title: "RAG ask (corpus-wide)",
9539
- description: "Ask a question grounded against the partner 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, 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.",
9597
+ 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, 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.",
9540
9598
  inputSchema: inputSchema7,
9541
9599
  handler: async (args, extra) => {
9542
9600
  try {
@@ -9547,6 +9605,7 @@ var ragAsk = ({ client, log }) => ({
9547
9605
  userId: searchArg.userId,
9548
9606
  orgId: searchArg.orgId,
9549
9607
  clientId: searchArg.clientId,
9608
+ scope: searchArg.scope,
9550
9609
  folderId: searchArg.folderId,
9551
9610
  rootFolderId: searchArg.rootFolderId,
9552
9611
  typeName: searchArg.typeName,
@@ -9704,7 +9763,7 @@ var inputSchema9 = {
9704
9763
  var listSchemas = ({ client, log }) => ({
9705
9764
  name: "list_schemas",
9706
9765
  title: "List record schemas",
9707
- description: 'List the structured-record schema catalog for the partner tenant. Each schema describes a record type (e.g. "patient", "clinical_note") \u2014 its fields, lookup-indexed fields, and capabilities. Use this to discover what record types exist before calling `record_query`. Filter with `surface` (record/document/user/org/client \u2014 e.g. only document types), `recordType` (resolve one schema by its type name), or `userId`/`orgId`; default returns everything the credential can see.',
9766
+ description: 'List the structured-record schema catalog for your tenant. Each schema describes a record type (e.g. "patient", "clinical_note") \u2014 its fields, lookup-indexed fields, and capabilities. Use this to discover what record types exist before calling `record_query`. Filter with `surface` (record/document/user/org/client \u2014 e.g. only document types), `recordType` (resolve one schema by its type name), or `userId`/`orgId`; default returns everything the credential can see.',
9708
9767
  inputSchema: inputSchema9,
9709
9768
  handler: async (args) => {
9710
9769
  const userId = args.userId;
@@ -9826,7 +9885,7 @@ var document_get_default = documentGet;
9826
9885
  // src/tools/document_query.ts
9827
9886
  var import_zod11 = require("zod");
9828
9887
  var MCP_DEFAULT_LIMIT3 = 3;
9829
- var MCP_MAX_LIMIT3 = 10;
9888
+ var MCP_MAX_LIMIT3 = 100;
9830
9889
  var inputSchema11 = {
9831
9890
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
9832
9891
  field: import_zod11.z.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
@@ -9843,14 +9902,18 @@ var inputSchema11 = {
9843
9902
  userId: import_zod11.z.string().optional().describe("List mode: scope to documents owned by this user."),
9844
9903
  orgId: import_zod11.z.string().optional().describe("List mode: scope to documents belonging to this org."),
9845
9904
  clientId: import_zod11.z.string().optional().describe("List mode: scope to documents associated with this client."),
9905
+ scope: import_zod11.z.string().optional().describe(
9906
+ 'List mode: filter to documents carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
9907
+ ),
9908
+ startFrom: import_zod11.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."),
9846
9909
  limit: import_zod11.z.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
9847
- `Max documents to return. MCP-specific cap of ${MCP_MAX_LIMIT3} (vs API max of 100) to protect the agent context window. Default 3.`
9910
+ `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.`
9848
9911
  )
9849
9912
  };
9850
9913
  var documentQuery = ({ client, log }) => ({
9851
9914
  name: "document_query",
9852
9915
  title: "Document query (list or lookup)",
9853
- description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `folderId`/`userId`/`orgId`/`clientId`.\nReturns up to 10 documents (default 3) as a bare array. Mode is auto-detected. Use document_get for the full text/download URL of one document.",
9916
+ description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `folderId`/`userId`/`orgId`/`clientId`/`scope`.\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` documents (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected. Use document_get for the full text/download URL of one document.",
9854
9917
  inputSchema: inputSchema11,
9855
9918
  handler: async (args) => {
9856
9919
  const limit = args.limit ?? MCP_DEFAULT_LIMIT3;
@@ -9860,8 +9923,9 @@ var documentQuery = ({ client, log }) => ({
9860
9923
  const to = args.to;
9861
9924
  const prefix = args.prefix;
9862
9925
  const type = args.type;
9926
+ const startFrom = args.startFrom;
9863
9927
  try {
9864
- let documents;
9928
+ let page;
9865
9929
  if (field) {
9866
9930
  const hasEquality = value !== void 0;
9867
9931
  const hasRange = from !== void 0 || to !== void 0;
@@ -9890,7 +9954,7 @@ var documentQuery = ({ client, log }) => ({
9890
9954
  new Error("lookup requires 'type' (the document's bound schema type) alongside 'field'.")
9891
9955
  );
9892
9956
  }
9893
- const page = await client.documents.lookupDocumentsByBody({
9957
+ page = await client.documents.lookupDocumentsByBody({
9894
9958
  type,
9895
9959
  field,
9896
9960
  value,
@@ -9898,28 +9962,31 @@ var documentQuery = ({ client, log }) => ({
9898
9962
  to,
9899
9963
  prefix,
9900
9964
  order: args.order,
9965
+ startFrom,
9901
9966
  limit
9902
9967
  });
9903
- documents = pageItems(page);
9904
9968
  log.debug(
9905
- { tool: "document_query", mode: "lookup", type, field, returned: documents.length },
9969
+ { tool: "document_query", mode: "lookup", type, field, returned: pageItems(page).length },
9906
9970
  "document_query lookup ok"
9907
9971
  );
9908
9972
  } else {
9909
- const page = await client.documents.listDocuments({
9973
+ page = await client.documents.listDocuments({
9910
9974
  folderId: args.folderId,
9911
9975
  userId: args.userId,
9912
9976
  orgId: args.orgId,
9913
9977
  clientId: args.clientId,
9978
+ scope: args.scope,
9979
+ startFrom,
9914
9980
  limit
9915
9981
  });
9916
- documents = pageItems(page);
9917
9982
  log.debug(
9918
- { tool: "document_query", mode: "list", limit, returned: documents.length },
9983
+ { tool: "document_query", mode: "list", limit, returned: pageItems(page).length },
9919
9984
  "document_query list ok"
9920
9985
  );
9921
9986
  }
9922
- return { content: [{ type: "text", text: JSON.stringify(documents, null, 2) }] };
9987
+ const documents = pageItems(page);
9988
+ const nextCursor = page.nextCursor ?? null;
9989
+ return { content: [{ type: "text", text: JSON.stringify({ data: documents, nextCursor }, null, 2) }] };
9923
9990
  } catch (err) {
9924
9991
  log.warn({ tool: "document_query", err: String(err) }, "document_query failed");
9925
9992
  return toolError("document_query", err);
@@ -10028,7 +10095,7 @@ var document_delete_default = documentDelete;
10028
10095
  // src/tools/folder_query.ts
10029
10096
  var import_zod14 = require("zod");
10030
10097
  var MCP_DEFAULT_LIMIT4 = 10;
10031
- var MCP_MAX_LIMIT4 = 50;
10098
+ var MCP_MAX_LIMIT4 = 100;
10032
10099
  var inputSchema14 = {
10033
10100
  id: import_zod14.z.string().optional().describe("Get mode: fetch this single folder by id."),
10034
10101
  parentId: import_zod14.z.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
@@ -10036,12 +10103,14 @@ var inputSchema14 = {
10036
10103
  orgId: import_zod14.z.string().optional().describe("List mode: scope to folders belonging to this org."),
10037
10104
  clientId: import_zod14.z.string().optional().describe("List mode: scope to folders associated with this client."),
10038
10105
  startFrom: import_zod14.z.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
10039
- limit: import_zod14.z.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(`List mode: max folders to return. MCP cap of ${MCP_MAX_LIMIT4} (vs API 100). Default 10.`)
10106
+ limit: import_zod14.z.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
10107
+ `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\`.`
10108
+ )
10040
10109
  };
10041
10110
  var folderQuery = ({ client, log }) => ({
10042
10111
  name: "folder_query",
10043
10112
  title: "Folder query (list or get)",
10044
- description: "Read folders. Two modes:\n \u2022 Get: pass `id` \u2192 returns the single folder.\n \u2022 List: omit `id`; pass `parentId` for a folder's direct children (tree navigation) or omit for a flat tenant list. Optionally filter by `userId`/`orgId`/`clientId`.\nList mode returns `{ data, nextCursor }` (default 10, max 50 per page); pass `nextCursor` back as `startFrom` to page through all folders. Get mode returns the single folder object.",
10113
+ description: "Read folders. Two modes:\n \u2022 Get: pass `id` \u2192 returns the single folder.\n \u2022 List: omit `id`; pass `parentId` for a folder's direct children (tree navigation) or omit for a flat tenant list. Optionally filter by `userId`/`orgId`/`clientId`.\nList mode returns `{ data, nextCursor }` (default 10, max 100 per page \u2014 raise it in one call when you can accept the larger page); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page through all folders. Get mode returns the single folder object.",
10045
10114
  inputSchema: inputSchema14,
10046
10115
  handler: async (args) => {
10047
10116
  const id = args.id;
@@ -10081,7 +10150,10 @@ var inputSchema15 = {
10081
10150
  slug: import_zod15.z.string().optional().describe("Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. Derived from name when omitted."),
10082
10151
  userId: import_zod15.z.string().optional().describe("Owning user (Vectros UUID)."),
10083
10152
  orgId: import_zod15.z.string().optional().describe("Owning org (Vectros UUID)."),
10084
- clientId: import_zod15.z.string().optional().describe("Associated client (Vectros UUID).")
10153
+ clientId: import_zod15.z.string().optional().describe("Associated client (Vectros UUID)."),
10154
+ scopes: import_zod15.z.array(import_zod15.z.string()).optional().describe(
10155
+ 'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). 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. `org:`/`client:` entries are equivalent to the orgId/clientId fields.'
10156
+ )
10085
10157
  };
10086
10158
  var folderCreate = ({ client, log }) => ({
10087
10159
  name: "folder_create",
@@ -10098,7 +10170,8 @@ var folderCreate = ({ client, log }) => ({
10098
10170
  slug: args.slug,
10099
10171
  userId: args.userId,
10100
10172
  orgId: args.orgId,
10101
- clientId: args.clientId
10173
+ clientId: args.clientId,
10174
+ scopes: args.scopes
10102
10175
  }
10103
10176
  });
10104
10177
  log.debug({ tool: "folder_create", id: folder.id, name: folder.name }, "folder_create ok");
@@ -10310,7 +10383,10 @@ var inputSchema19 = {
10310
10383
  folderId: import_zod18.z.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
10311
10384
  userId: import_zod18.z.string().optional().describe("Owning user ID. Optional."),
10312
10385
  orgId: import_zod18.z.string().optional().describe("Owning org ID. Optional."),
10313
- clientId: import_zod18.z.string().optional().describe("Owning client ID. Optional.")
10386
+ clientId: import_zod18.z.string().optional().describe("Owning client ID. Optional."),
10387
+ scopes: import_zod18.z.array(import_zod18.z.string()).optional().describe(
10388
+ "TEXT MODE ONLY. Scope ownership as `namespace:value` entries, at most 2 (e.g. [\"org:<uuid>\", \"group:eng-team\"]). 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. `org:`/`client:` entries are equivalent to the orgId/clientId fields. Rejected with `filePath` (file uploads do not accept a scope declaration): a file-uploaded document is stamped with the credential's full identity."
10389
+ )
10314
10390
  };
10315
10391
  var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10316
10392
  name: "document_ingest",
@@ -10344,6 +10420,14 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10344
10420
  )
10345
10421
  );
10346
10422
  }
10423
+ if (filePath && args.scopes !== void 0) {
10424
+ return toolError(
10425
+ "document_ingest",
10426
+ new Error(
10427
+ "`scopes` is not supported with `filePath` \u2014 file uploads do not accept a scope declaration, and silently ignoring it could make an intended-private document team-visible. Use `text` mode to declare scopes, or omit `scopes` (the upload is stamped with the credential's full identity)."
10428
+ )
10429
+ );
10430
+ }
10347
10431
  const common = {
10348
10432
  folderId: args.folderId,
10349
10433
  payload: args.payload,
@@ -10361,6 +10445,9 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10361
10445
  title,
10362
10446
  text,
10363
10447
  indexMode,
10448
+ // Scope ownership is a TEXT-mode contract only (the upload-init
10449
+ // endpoint has no scopes field — see the gate above).
10450
+ scopes: args.scopes,
10364
10451
  ...common
10365
10452
  }
10366
10453
  });
@@ -10453,7 +10540,7 @@ var document_ingest_default = documentIngest;
10453
10540
  // src/tools/lookup_principal.ts
10454
10541
  var import_zod19 = require("zod");
10455
10542
  var MCP_DEFAULT_LIMIT5 = 10;
10456
- var MCP_MAX_LIMIT5 = 50;
10543
+ var MCP_MAX_LIMIT5 = 100;
10457
10544
  var inputSchema20 = {
10458
10545
  kind: import_zod19.z.enum(["user", "org", "client"]).describe("Which principal to look up: a user, an organization, or a client."),
10459
10546
  externalId: import_zod19.z.string().optional().describe(
@@ -10467,12 +10554,15 @@ var inputSchema20 = {
10467
10554
  to: import_zod19.z.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
10468
10555
  prefix: import_zod19.z.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
10469
10556
  order: import_zod19.z.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
10470
- limit: import_zod19.z.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(`Max principals to return. MCP cap of ${MCP_MAX_LIMIT5} (vs API 100). Default ${MCP_DEFAULT_LIMIT5}.`)
10557
+ startFrom: import_zod19.z.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
10558
+ limit: import_zod19.z.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
10559
+ `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\`.`
10560
+ )
10471
10561
  };
10472
10562
  var lookupPrincipal = ({ client, log }) => ({
10473
10563
  name: "lookup_principal",
10474
10564
  title: "Look up a user / org / client identity",
10475
- description: "Resolve a user, org, or client identity. Two modes:\n \u2022 Resolve by your own id: pass `externalId` \u2192 the matching principal incl. its Vectros UUID (the id the ownership filters on record_query / hybrid_search / rag_ask expect).\n \u2022 Lookup by a schema field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Sensitive-field-safe. (If both `externalId` and a `field` lookup are given, `externalId` wins.) Returns up to 50 principals (default 10) as a bare array. Read-only \u2014 does not create or modify identities.",
10565
+ description: "Resolve a user, org, or client identity. Two modes:\n \u2022 Resolve by your own id: pass `externalId` \u2192 the matching principal incl. its Vectros UUID (the id the ownership filters on record_query / hybrid_search / rag_ask expect).\n \u2022 Lookup by a schema field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Sensitive-field-safe. (If both `externalId` and a `field` lookup are given, `externalId` wins.) Returns a `{ data, nextCursor }` page (default 10, max 100 \u2014 raise it in one call when you can accept the payload); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom`. Read-only \u2014 does not create or modify identities.",
10476
10566
  inputSchema: inputSchema20,
10477
10567
  handler: async (args) => {
10478
10568
  const kind = args.kind;
@@ -10485,6 +10575,7 @@ var lookupPrincipal = ({ client, log }) => ({
10485
10575
  const to = args.to;
10486
10576
  const prefix = args.prefix;
10487
10577
  const order = args.order;
10578
+ const startFrom = args.startFrom;
10488
10579
  try {
10489
10580
  let page;
10490
10581
  if (externalId !== void 0) {
@@ -10522,7 +10613,7 @@ var lookupPrincipal = ({ client, log }) => ({
10522
10613
  new Error("lookup requires 'type' (the identity schema's record type) alongside 'field'.")
10523
10614
  );
10524
10615
  }
10525
- const req = { type, field, value, from, to, prefix, order, limit };
10616
+ const req = { type, field, value, from, to, prefix, order, startFrom, limit };
10526
10617
  if (kind === "user") {
10527
10618
  page = await client.identity.lookupUsers(req);
10528
10619
  } else if (kind === "org") {
@@ -10538,7 +10629,8 @@ var lookupPrincipal = ({ client, log }) => ({
10538
10629
  );
10539
10630
  }
10540
10631
  const principals = pageItems(page);
10541
- return { content: [{ type: "text", text: JSON.stringify(principals, null, 2) }] };
10632
+ const nextCursor = page.nextCursor ?? null;
10633
+ return { content: [{ type: "text", text: JSON.stringify({ data: principals, nextCursor }, null, 2) }] };
10542
10634
  } catch (err) {
10543
10635
  log.warn({ tool: "lookup_principal", kind, err: String(err) }, "lookup_principal failed");
10544
10636
  return toolError("lookup_principal", err);