@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/cli-http.js CHANGED
@@ -675,6 +675,10 @@ var ReadAccessLogResponse;
675
675
  })(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
676
676
  var RecordRequest;
677
677
  ((RecordRequest2) => {
678
+ RecordRequest2.Status = {
679
+ Active: "ACTIVE",
680
+ Archived: "ARCHIVED"
681
+ };
678
682
  RecordRequest2.IndexMode = {
679
683
  Hybrid: "HYBRID",
680
684
  Semantic: "SEMANTIC",
@@ -684,6 +688,10 @@ var RecordRequest;
684
688
  })(RecordRequest || (RecordRequest = {}));
685
689
  var RecordResponse;
686
690
  ((RecordResponse2) => {
691
+ RecordResponse2.Status = {
692
+ Active: "ACTIVE",
693
+ Archived: "ARCHIVED"
694
+ };
687
695
  RecordResponse2.IndexStatus = {
688
696
  PendingIndex: "PENDING_INDEX",
689
697
  Indexed: "INDEXED",
@@ -3158,7 +3166,7 @@ var AuthClient = class {
3158
3166
  * roleId: "engineering-member",
3159
3167
  * name: "Engineering Team Member",
3160
3168
  * scopes: [{
3161
- * allowed_actions: ["read", "write"]
3169
+ * allowed_actions: ["records:cru", "search:r"]
3162
3170
  * }]
3163
3171
  * }
3164
3172
  * })
@@ -3706,7 +3714,7 @@ var AuthClient = class {
3706
3714
  * roleId: "engineering-member",
3707
3715
  * name: "Engineering Team Member",
3708
3716
  * scopes: [{
3709
- * allowed_actions: ["read", "write"]
3717
+ * allowed_actions: ["records:cru", "search:r"]
3710
3718
  * }]
3711
3719
  * }
3712
3720
  * })
@@ -4609,7 +4617,7 @@ var DocumentsClient = class {
4609
4617
  this._options = normalizeClientOptionsWithAuth(options);
4610
4618
  }
4611
4619
  /**
4612
- * 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.
4620
+ * 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.
4613
4621
  *
4614
4622
  * @param {Vectros.ListDocumentsRequest} request
4615
4623
  * @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -4619,6 +4627,7 @@ var DocumentsClient = class {
4619
4627
  * userId: "550e8400-e29b-41d4-a716-446655440000",
4620
4628
  * orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
4621
4629
  * clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
4630
+ * scope: "group:eng-team",
4622
4631
  * folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
4623
4632
  * startFrom: "doc_prev123"
4624
4633
  * })
@@ -4627,11 +4636,12 @@ var DocumentsClient = class {
4627
4636
  return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
4628
4637
  }
4629
4638
  async __listDocuments(request = {}, requestOptions) {
4630
- const { userId, orgId, clientId, folderId, startFrom, limit } = request;
4639
+ const { userId, orgId, clientId, scope, folderId, startFrom, limit } = request;
4631
4640
  const _queryParams = {
4632
4641
  userId,
4633
4642
  orgId,
4634
4643
  clientId,
4644
+ scope,
4635
4645
  folderId,
4636
4646
  startFrom,
4637
4647
  limit
@@ -4689,9 +4699,10 @@ var DocumentsClient = class {
4689
4699
  return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
4690
4700
  }
4691
4701
  async __ingestDocument(request, requestOptions) {
4692
- const { upsert, body: _body } = request;
4702
+ const { upsert, allowClear, body: _body } = request;
4693
4703
  const _queryParams = {
4694
- upsert
4704
+ upsert,
4705
+ allowClear
4695
4706
  };
4696
4707
  const _authRequest = await this._options.authProvider.getAuthRequest();
4697
4708
  const _headers = mergeHeaders(
@@ -4815,7 +4826,10 @@ var DocumentsClient = class {
4815
4826
  return HttpResponsePromise.fromPromise(this.__updateDocument(request, requestOptions));
4816
4827
  }
4817
4828
  async __updateDocument(request, requestOptions) {
4818
- const { id, body: _body } = request;
4829
+ const { id, allowClear, body: _body } = request;
4830
+ const _queryParams = {
4831
+ allowClear
4832
+ };
4819
4833
  const _authRequest = await this._options.authProvider.getAuthRequest();
4820
4834
  const _headers = mergeHeaders(
4821
4835
  _authRequest.headers,
@@ -4830,7 +4844,7 @@ var DocumentsClient = class {
4830
4844
  method: "PUT",
4831
4845
  headers: _headers,
4832
4846
  contentType: "application/json",
4833
- queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
4847
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
4834
4848
  requestType: "json",
4835
4849
  body: _body,
4836
4850
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
@@ -7566,7 +7580,7 @@ var RecordsClient = class {
7566
7580
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch");
7567
7581
  }
7568
7582
  /**
7569
- * 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.
7583
+ * 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.
7570
7584
  *
7571
7585
  * @param {Vectros.ListRecordsRequest} request
7572
7586
  * @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -7578,6 +7592,7 @@ var RecordsClient = class {
7578
7592
  * userId: "550e8400-e29b-41d4-a716-446655440000",
7579
7593
  * orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
7580
7594
  * clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
7595
+ * scope: "group:eng-team",
7581
7596
  * startFrom: "550e8400-e29b-41d4-a716-446655440000"
7582
7597
  * })
7583
7598
  */
@@ -7585,13 +7600,25 @@ var RecordsClient = class {
7585
7600
  return HttpResponsePromise.fromPromise(this.__listRecords(request, requestOptions));
7586
7601
  }
7587
7602
  async __listRecords(request = {}, requestOptions) {
7588
- const { type: type_, folderId, userId, orgId, clientId, startFrom, limit, includePayload, recent } = request;
7603
+ const {
7604
+ type: type_,
7605
+ folderId,
7606
+ userId,
7607
+ orgId,
7608
+ clientId,
7609
+ scope,
7610
+ startFrom,
7611
+ limit,
7612
+ includePayload,
7613
+ recent
7614
+ } = request;
7589
7615
  const _queryParams = {
7590
7616
  type: type_,
7591
7617
  folderId,
7592
7618
  userId,
7593
7619
  orgId,
7594
7620
  clientId,
7621
+ scope,
7595
7622
  startFrom,
7596
7623
  limit,
7597
7624
  includePayload,
@@ -7656,9 +7683,10 @@ var RecordsClient = class {
7656
7683
  return HttpResponsePromise.fromPromise(this.__createRecord(request, requestOptions));
7657
7684
  }
7658
7685
  async __createRecord(request, requestOptions) {
7659
- const { upsert, body: _body } = request;
7686
+ const { upsert, allowClear, body: _body } = request;
7660
7687
  const _queryParams = {
7661
- upsert
7688
+ upsert,
7689
+ allowClear
7662
7690
  };
7663
7691
  const _authRequest = await this._options.authProvider.getAuthRequest();
7664
7692
  const _headers = mergeHeaders(
@@ -7780,7 +7808,10 @@ var RecordsClient = class {
7780
7808
  return HttpResponsePromise.fromPromise(this.__updateRecord(request, requestOptions));
7781
7809
  }
7782
7810
  async __updateRecord(request, requestOptions) {
7783
- const { id, body: _body } = request;
7811
+ const { id, allowClear, body: _body } = request;
7812
+ const _queryParams = {
7813
+ allowClear
7814
+ };
7784
7815
  const _authRequest = await this._options.authProvider.getAuthRequest();
7785
7816
  const _headers = mergeHeaders(
7786
7817
  _authRequest.headers,
@@ -7795,7 +7826,7 @@ var RecordsClient = class {
7795
7826
  method: "PUT",
7796
7827
  headers: _headers,
7797
7828
  contentType: "application/json",
7798
- queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
7829
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
7799
7830
  requestType: "json",
7800
7831
  body: _body,
7801
7832
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
@@ -8782,8 +8813,8 @@ function warnOnSuboptimalKey(key, log) {
8782
8813
 
8783
8814
  // src/build-info.ts
8784
8815
  var BUILD_INFO = {
8785
- mcpServer: true ? "0.7.0" : "dev",
8786
- sdk: true ? "0.33.0" : "dev"
8816
+ mcpServer: true ? "0.9.0" : "dev",
8817
+ sdk: true ? "0.34.0" : "dev"
8787
8818
  };
8788
8819
 
8789
8820
  // src/log.ts
@@ -9019,7 +9050,7 @@ ${detail}${pointer}`
9019
9050
 
9020
9051
  // src/tools/hybrid_search.ts
9021
9052
  var MCP_DEFAULT_LIMIT = 3;
9022
- var MCP_MAX_LIMIT = 10;
9053
+ var MCP_MAX_LIMIT = 50;
9023
9054
  var MCP_RESPONSE_CHAR_BUDGET = 24e3;
9024
9055
  var SNIPPET_MAX_CHARS = 160;
9025
9056
  var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["tenantId", "owner_id", "model_type", "rootFolderId", "folderId"]);
@@ -9053,9 +9084,9 @@ var inputSchema = {
9053
9084
  "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.)"
9054
9085
  ),
9055
9086
  limit: import_zod.z.number().int().min(1).max(MCP_MAX_LIMIT).optional().describe(
9056
- `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.`
9087
+ `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.`
9057
9088
  ),
9058
- offset: import_zod.z.number().int().min(0).optional().describe("Skip the first N hits \u2014 for pagination."),
9089
+ offset: import_zod.z.number().int().min(0).max(200).optional().describe("Skip the first N hits \u2014 for pagination (0\u2013200)."),
9059
9090
  folderId: import_zod.z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
9060
9091
  rootFolderId: import_zod.z.string().optional().describe("Scope to content under this folder AND all its descendants (subtree-root folder id)."),
9061
9092
  // Content narrowing.
@@ -9070,6 +9101,9 @@ var inputSchema = {
9070
9101
  userId: import_zod.z.string().optional().describe("Restrict to content owned by this user (Vectros UUID)."),
9071
9102
  orgId: import_zod.z.string().optional().describe("Restrict to content belonging to this org (Vectros UUID)."),
9072
9103
  clientId: import_zod.z.string().optional().describe("Restrict to content associated with this client (Vectros UUID)."),
9104
+ scope: import_zod.z.string().optional().describe(
9105
+ '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.'
9106
+ ),
9073
9107
  // Date window.
9074
9108
  createdAfter: import_zod.z.string().optional().describe("Restrict to content created at/after this ISO 8601 UTC timestamp."),
9075
9109
  createdBefore: import_zod.z.string().optional().describe("Restrict to content created at/before this ISO 8601 UTC timestamp."),
@@ -9087,7 +9121,7 @@ var inputSchema = {
9087
9121
  var hybridSearch = ({ client, log }) => ({
9088
9122
  name: "hybrid_search",
9089
9123
  title: "Hybrid search",
9090
- 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".`,
9124
+ 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\".",
9091
9125
  inputSchema,
9092
9126
  handler: async (args) => {
9093
9127
  const limit = args.limit ?? MCP_DEFAULT_LIMIT;
@@ -9107,6 +9141,7 @@ var hybridSearch = ({ client, log }) => ({
9107
9141
  userId: args.userId,
9108
9142
  orgId: args.orgId,
9109
9143
  clientId: args.clientId,
9144
+ scope: args.scope,
9110
9145
  createdAfter: args.createdAfter,
9111
9146
  createdBefore: args.createdBefore,
9112
9147
  minSimilarity: args.minSimilarity,
@@ -9133,6 +9168,12 @@ var hybridSearch = ({ client, log }) => ({
9133
9168
  envelope.returnedResults = shaped.length;
9134
9169
  text = JSON.stringify(envelope, null, 2);
9135
9170
  }
9171
+ const offset = args.offset ?? 0;
9172
+ const totalResults = result.totalResults;
9173
+ if (typeof totalResults === "number" && offset + shaped.length < totalResults) {
9174
+ envelope.hasMore = true;
9175
+ text = JSON.stringify(envelope, null, 2);
9176
+ }
9136
9177
  log.debug(
9137
9178
  { tool: "hybrid_search", mode, limit, returned: shaped.length, textLegEmpty },
9138
9179
  "hybrid_search ok"
@@ -9168,7 +9209,7 @@ async function drainPages(fetchPage) {
9168
9209
 
9169
9210
  // src/tools/record_query.ts
9170
9211
  var MCP_DEFAULT_LIMIT2 = 3;
9171
- var MCP_MAX_LIMIT2 = 10;
9212
+ var MCP_MAX_LIMIT2 = 100;
9172
9213
  var inputSchema2 = {
9173
9214
  type: import_zod2.z.string().min(1, "type (record type) is required").describe('Record type / schema name (e.g. "patient", "clinical_note").'),
9174
9215
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
@@ -9186,14 +9227,18 @@ var inputSchema2 = {
9186
9227
  userId: import_zod2.z.string().optional().describe("List mode: scope to records owned by this user."),
9187
9228
  orgId: import_zod2.z.string().optional().describe("List mode: scope to records owned by this org."),
9188
9229
  clientId: import_zod2.z.string().optional().describe("List mode: scope to records associated with this client."),
9230
+ scope: import_zod2.z.string().optional().describe(
9231
+ '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.'
9232
+ ),
9233
+ 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."),
9189
9234
  limit: import_zod2.z.number().int().min(1).max(MCP_MAX_LIMIT2).optional().describe(
9190
- `Max records to return. MCP-specific cap of ${MCP_MAX_LIMIT2} (vs API max of 100) to protect the agent context window. Default 3.`
9235
+ `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.`
9191
9236
  )
9192
9237
  };
9193
9238
  var recordQuery = ({ client, log }) => ({
9194
9239
  name: "record_query",
9195
9240
  title: "Record query (list or lookup)",
9196
- 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.",
9241
+ 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.",
9197
9242
  inputSchema: inputSchema2,
9198
9243
  handler: async (args) => {
9199
9244
  const limit = args.limit ?? MCP_DEFAULT_LIMIT2;
@@ -9203,8 +9248,9 @@ var recordQuery = ({ client, log }) => ({
9203
9248
  const from = args.from;
9204
9249
  const to = args.to;
9205
9250
  const prefix = args.prefix;
9251
+ const startFrom = args.startFrom;
9206
9252
  try {
9207
- let records;
9253
+ let page;
9208
9254
  if (field) {
9209
9255
  const hasEquality = value !== void 0;
9210
9256
  const hasRange = from !== void 0 || to !== void 0;
@@ -9227,7 +9273,7 @@ var recordQuery = ({ client, log }) => ({
9227
9273
  if (hasRange && !(from !== void 0 && to !== void 0)) {
9228
9274
  return toolError("record_query", new Error("range lookup requires both 'from' and 'to'."));
9229
9275
  }
9230
- const page = await client.records.lookupRecordsByBody({
9276
+ page = await client.records.lookupRecordsByBody({
9231
9277
  type,
9232
9278
  field,
9233
9279
  value,
@@ -9235,29 +9281,32 @@ var recordQuery = ({ client, log }) => ({
9235
9281
  to,
9236
9282
  prefix,
9237
9283
  order: args.order,
9284
+ startFrom,
9238
9285
  limit
9239
9286
  });
9240
- records = pageItems(page);
9241
9287
  log.debug(
9242
- { tool: "record_query", mode: "lookup", type, field, returned: records.length },
9288
+ { tool: "record_query", mode: "lookup", type, field, returned: pageItems(page).length },
9243
9289
  "record_query lookup ok"
9244
9290
  );
9245
9291
  } else {
9246
- const page = await client.records.listRecords({
9292
+ page = await client.records.listRecords({
9247
9293
  type,
9248
9294
  userId: args.userId,
9249
9295
  orgId: args.orgId,
9250
9296
  clientId: args.clientId,
9297
+ scope: args.scope,
9298
+ startFrom,
9251
9299
  limit
9252
9300
  });
9253
- records = pageItems(page);
9254
9301
  log.debug(
9255
- { tool: "record_query", mode: "list", type, limit, returned: records.length },
9302
+ { tool: "record_query", mode: "list", type, limit, returned: pageItems(page).length },
9256
9303
  "record_query list ok"
9257
9304
  );
9258
9305
  }
9306
+ const records = pageItems(page);
9307
+ const nextCursor = page.nextCursor ?? null;
9259
9308
  return {
9260
- content: [{ type: "text", text: JSON.stringify(records, null, 2) }]
9309
+ content: [{ type: "text", text: JSON.stringify({ data: records, nextCursor }, null, 2) }]
9261
9310
  };
9262
9311
  } catch (err) {
9263
9312
  log.warn({ tool: "record_query", err: String(err) }, "record_query failed");
@@ -9380,7 +9429,10 @@ var inputSchema4 = {
9380
9429
  folderId: import_zod4.z.string().optional().describe("Group this record into a folder."),
9381
9430
  userId: import_zod4.z.string().optional().describe("Owning user id."),
9382
9431
  orgId: import_zod4.z.string().optional().describe("Owning organization id."),
9383
- clientId: import_zod4.z.string().optional().describe("Associated client id.")
9432
+ clientId: import_zod4.z.string().optional().describe("Associated client id."),
9433
+ scopes: import_zod4.z.array(import_zod4.z.string()).optional().describe(
9434
+ '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.'
9435
+ )
9384
9436
  };
9385
9437
  var recordCreate = ({ client, log }) => ({
9386
9438
  name: "record_create",
@@ -9400,7 +9452,8 @@ var recordCreate = ({ client, log }) => ({
9400
9452
  folderId: args.folderId,
9401
9453
  userId: args.userId,
9402
9454
  orgId: args.orgId,
9403
- clientId: args.clientId
9455
+ clientId: args.clientId,
9456
+ scopes: args.scopes
9404
9457
  }
9405
9458
  });
9406
9459
  log.debug({ tool: "record_create", type, id: created.id }, "record_create ok");
@@ -9485,24 +9538,29 @@ var record_delete_default = recordDelete;
9485
9538
  // src/tools/rag_ask.ts
9486
9539
  var import_zod7 = require("zod");
9487
9540
  var SEARCH_MCP_DEFAULT_LIMIT = 5;
9488
- var SEARCH_MCP_MAX_LIMIT = 10;
9541
+ var SEARCH_MCP_MAX_LIMIT = 50;
9489
9542
  var inputSchema7 = {
9490
9543
  query: import_zod7.z.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
9491
9544
  instructions: import_zod7.z.string().optional().describe(
9492
9545
  '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.'
9493
9546
  ),
9494
9547
  model: import_zod7.z.string().optional().describe(
9495
- "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."
9548
+ "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."
9496
9549
  ),
9497
9550
  search: import_zod7.z.object({
9498
9551
  mode: import_zod7.z.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
9499
- 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}.`),
9552
+ limit: import_zod7.z.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
9553
+ `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.`
9554
+ ),
9500
9555
  // Retrieval scoping — parity with hybrid_search so an agent can ground the
9501
9556
  // answer on a specific owner / folder / type / metadata subset, not just the
9502
9557
  // whole-tenant corpus.
9503
9558
  userId: import_zod7.z.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
9504
9559
  orgId: import_zod7.z.string().optional().describe("Restrict retrieval to content owned by this org (Vectros UUID)."),
9505
9560
  clientId: import_zod7.z.string().optional().describe("Restrict retrieval to content tagged with this client (Vectros UUID)."),
9561
+ scope: import_zod7.z.string().optional().describe(
9562
+ '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.'
9563
+ ),
9506
9564
  folderId: import_zod7.z.string().optional().describe("Restrict retrieval to this exact folder."),
9507
9565
  rootFolderId: import_zod7.z.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
9508
9566
  typeName: import_zod7.z.string().optional().describe("Restrict record retrieval to this schema type."),
@@ -9520,7 +9578,7 @@ var inputSchema7 = {
9520
9578
  var ragAsk = ({ client, log }) => ({
9521
9579
  name: "rag_ask",
9522
9580
  title: "RAG ask (corpus-wide)",
9523
- 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.",
9581
+ 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.",
9524
9582
  inputSchema: inputSchema7,
9525
9583
  handler: async (args, extra) => {
9526
9584
  try {
@@ -9531,6 +9589,7 @@ var ragAsk = ({ client, log }) => ({
9531
9589
  userId: searchArg.userId,
9532
9590
  orgId: searchArg.orgId,
9533
9591
  clientId: searchArg.clientId,
9592
+ scope: searchArg.scope,
9534
9593
  folderId: searchArg.folderId,
9535
9594
  rootFolderId: searchArg.rootFolderId,
9536
9595
  typeName: searchArg.typeName,
@@ -9688,7 +9747,7 @@ var inputSchema9 = {
9688
9747
  var listSchemas = ({ client, log }) => ({
9689
9748
  name: "list_schemas",
9690
9749
  title: "List record schemas",
9691
- 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.',
9750
+ 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.',
9692
9751
  inputSchema: inputSchema9,
9693
9752
  handler: async (args) => {
9694
9753
  const userId = args.userId;
@@ -9810,7 +9869,7 @@ var document_get_default = documentGet;
9810
9869
  // src/tools/document_query.ts
9811
9870
  var import_zod11 = require("zod");
9812
9871
  var MCP_DEFAULT_LIMIT3 = 3;
9813
- var MCP_MAX_LIMIT3 = 10;
9872
+ var MCP_MAX_LIMIT3 = 100;
9814
9873
  var inputSchema11 = {
9815
9874
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
9816
9875
  field: import_zod11.z.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
@@ -9827,14 +9886,18 @@ var inputSchema11 = {
9827
9886
  userId: import_zod11.z.string().optional().describe("List mode: scope to documents owned by this user."),
9828
9887
  orgId: import_zod11.z.string().optional().describe("List mode: scope to documents belonging to this org."),
9829
9888
  clientId: import_zod11.z.string().optional().describe("List mode: scope to documents associated with this client."),
9889
+ scope: import_zod11.z.string().optional().describe(
9890
+ '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.'
9891
+ ),
9892
+ 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."),
9830
9893
  limit: import_zod11.z.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
9831
- `Max documents to return. MCP-specific cap of ${MCP_MAX_LIMIT3} (vs API max of 100) to protect the agent context window. Default 3.`
9894
+ `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.`
9832
9895
  )
9833
9896
  };
9834
9897
  var documentQuery = ({ client, log }) => ({
9835
9898
  name: "document_query",
9836
9899
  title: "Document query (list or lookup)",
9837
- 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.",
9900
+ 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.",
9838
9901
  inputSchema: inputSchema11,
9839
9902
  handler: async (args) => {
9840
9903
  const limit = args.limit ?? MCP_DEFAULT_LIMIT3;
@@ -9844,8 +9907,9 @@ var documentQuery = ({ client, log }) => ({
9844
9907
  const to = args.to;
9845
9908
  const prefix = args.prefix;
9846
9909
  const type = args.type;
9910
+ const startFrom = args.startFrom;
9847
9911
  try {
9848
- let documents;
9912
+ let page;
9849
9913
  if (field) {
9850
9914
  const hasEquality = value !== void 0;
9851
9915
  const hasRange = from !== void 0 || to !== void 0;
@@ -9874,7 +9938,7 @@ var documentQuery = ({ client, log }) => ({
9874
9938
  new Error("lookup requires 'type' (the document's bound schema type) alongside 'field'.")
9875
9939
  );
9876
9940
  }
9877
- const page = await client.documents.lookupDocumentsByBody({
9941
+ page = await client.documents.lookupDocumentsByBody({
9878
9942
  type,
9879
9943
  field,
9880
9944
  value,
@@ -9882,28 +9946,31 @@ var documentQuery = ({ client, log }) => ({
9882
9946
  to,
9883
9947
  prefix,
9884
9948
  order: args.order,
9949
+ startFrom,
9885
9950
  limit
9886
9951
  });
9887
- documents = pageItems(page);
9888
9952
  log.debug(
9889
- { tool: "document_query", mode: "lookup", type, field, returned: documents.length },
9953
+ { tool: "document_query", mode: "lookup", type, field, returned: pageItems(page).length },
9890
9954
  "document_query lookup ok"
9891
9955
  );
9892
9956
  } else {
9893
- const page = await client.documents.listDocuments({
9957
+ page = await client.documents.listDocuments({
9894
9958
  folderId: args.folderId,
9895
9959
  userId: args.userId,
9896
9960
  orgId: args.orgId,
9897
9961
  clientId: args.clientId,
9962
+ scope: args.scope,
9963
+ startFrom,
9898
9964
  limit
9899
9965
  });
9900
- documents = pageItems(page);
9901
9966
  log.debug(
9902
- { tool: "document_query", mode: "list", limit, returned: documents.length },
9967
+ { tool: "document_query", mode: "list", limit, returned: pageItems(page).length },
9903
9968
  "document_query list ok"
9904
9969
  );
9905
9970
  }
9906
- return { content: [{ type: "text", text: JSON.stringify(documents, null, 2) }] };
9971
+ const documents = pageItems(page);
9972
+ const nextCursor = page.nextCursor ?? null;
9973
+ return { content: [{ type: "text", text: JSON.stringify({ data: documents, nextCursor }, null, 2) }] };
9907
9974
  } catch (err) {
9908
9975
  log.warn({ tool: "document_query", err: String(err) }, "document_query failed");
9909
9976
  return toolError("document_query", err);
@@ -10012,7 +10079,7 @@ var document_delete_default = documentDelete;
10012
10079
  // src/tools/folder_query.ts
10013
10080
  var import_zod14 = require("zod");
10014
10081
  var MCP_DEFAULT_LIMIT4 = 10;
10015
- var MCP_MAX_LIMIT4 = 50;
10082
+ var MCP_MAX_LIMIT4 = 100;
10016
10083
  var inputSchema14 = {
10017
10084
  id: import_zod14.z.string().optional().describe("Get mode: fetch this single folder by id."),
10018
10085
  parentId: import_zod14.z.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
@@ -10020,12 +10087,14 @@ var inputSchema14 = {
10020
10087
  orgId: import_zod14.z.string().optional().describe("List mode: scope to folders belonging to this org."),
10021
10088
  clientId: import_zod14.z.string().optional().describe("List mode: scope to folders associated with this client."),
10022
10089
  startFrom: import_zod14.z.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
10023
- 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.`)
10090
+ limit: import_zod14.z.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
10091
+ `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\`.`
10092
+ )
10024
10093
  };
10025
10094
  var folderQuery = ({ client, log }) => ({
10026
10095
  name: "folder_query",
10027
10096
  title: "Folder query (list or get)",
10028
- 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.",
10097
+ 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.",
10029
10098
  inputSchema: inputSchema14,
10030
10099
  handler: async (args) => {
10031
10100
  const id = args.id;
@@ -10065,7 +10134,10 @@ var inputSchema15 = {
10065
10134
  slug: import_zod15.z.string().optional().describe("Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. Derived from name when omitted."),
10066
10135
  userId: import_zod15.z.string().optional().describe("Owning user (Vectros UUID)."),
10067
10136
  orgId: import_zod15.z.string().optional().describe("Owning org (Vectros UUID)."),
10068
- clientId: import_zod15.z.string().optional().describe("Associated client (Vectros UUID).")
10137
+ clientId: import_zod15.z.string().optional().describe("Associated client (Vectros UUID)."),
10138
+ scopes: import_zod15.z.array(import_zod15.z.string()).optional().describe(
10139
+ '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.'
10140
+ )
10069
10141
  };
10070
10142
  var folderCreate = ({ client, log }) => ({
10071
10143
  name: "folder_create",
@@ -10082,7 +10154,8 @@ var folderCreate = ({ client, log }) => ({
10082
10154
  slug: args.slug,
10083
10155
  userId: args.userId,
10084
10156
  orgId: args.orgId,
10085
- clientId: args.clientId
10157
+ clientId: args.clientId,
10158
+ scopes: args.scopes
10086
10159
  }
10087
10160
  });
10088
10161
  log.debug({ tool: "folder_create", id: folder.id, name: folder.name }, "folder_create ok");
@@ -10294,7 +10367,10 @@ var inputSchema19 = {
10294
10367
  folderId: import_zod18.z.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
10295
10368
  userId: import_zod18.z.string().optional().describe("Owning user ID. Optional."),
10296
10369
  orgId: import_zod18.z.string().optional().describe("Owning org ID. Optional."),
10297
- clientId: import_zod18.z.string().optional().describe("Owning client ID. Optional.")
10370
+ clientId: import_zod18.z.string().optional().describe("Owning client ID. Optional."),
10371
+ scopes: import_zod18.z.array(import_zod18.z.string()).optional().describe(
10372
+ "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."
10373
+ )
10298
10374
  };
10299
10375
  var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10300
10376
  name: "document_ingest",
@@ -10328,6 +10404,14 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10328
10404
  )
10329
10405
  );
10330
10406
  }
10407
+ if (filePath && args.scopes !== void 0) {
10408
+ return toolError(
10409
+ "document_ingest",
10410
+ new Error(
10411
+ "`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)."
10412
+ )
10413
+ );
10414
+ }
10331
10415
  const common = {
10332
10416
  folderId: args.folderId,
10333
10417
  payload: args.payload,
@@ -10345,6 +10429,9 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10345
10429
  title,
10346
10430
  text,
10347
10431
  indexMode,
10432
+ // Scope ownership is a TEXT-mode contract only (the upload-init
10433
+ // endpoint has no scopes field — see the gate above).
10434
+ scopes: args.scopes,
10348
10435
  ...common
10349
10436
  }
10350
10437
  });
@@ -10437,7 +10524,7 @@ var document_ingest_default = documentIngest;
10437
10524
  // src/tools/lookup_principal.ts
10438
10525
  var import_zod19 = require("zod");
10439
10526
  var MCP_DEFAULT_LIMIT5 = 10;
10440
- var MCP_MAX_LIMIT5 = 50;
10527
+ var MCP_MAX_LIMIT5 = 100;
10441
10528
  var inputSchema20 = {
10442
10529
  kind: import_zod19.z.enum(["user", "org", "client"]).describe("Which principal to look up: a user, an organization, or a client."),
10443
10530
  externalId: import_zod19.z.string().optional().describe(
@@ -10451,12 +10538,15 @@ var inputSchema20 = {
10451
10538
  to: import_zod19.z.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
10452
10539
  prefix: import_zod19.z.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
10453
10540
  order: import_zod19.z.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
10454
- 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}.`)
10541
+ startFrom: import_zod19.z.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
10542
+ limit: import_zod19.z.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
10543
+ `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\`.`
10544
+ )
10455
10545
  };
10456
10546
  var lookupPrincipal = ({ client, log }) => ({
10457
10547
  name: "lookup_principal",
10458
10548
  title: "Look up a user / org / client identity",
10459
- 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.",
10549
+ 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.",
10460
10550
  inputSchema: inputSchema20,
10461
10551
  handler: async (args) => {
10462
10552
  const kind = args.kind;
@@ -10469,6 +10559,7 @@ var lookupPrincipal = ({ client, log }) => ({
10469
10559
  const to = args.to;
10470
10560
  const prefix = args.prefix;
10471
10561
  const order = args.order;
10562
+ const startFrom = args.startFrom;
10472
10563
  try {
10473
10564
  let page;
10474
10565
  if (externalId !== void 0) {
@@ -10506,7 +10597,7 @@ var lookupPrincipal = ({ client, log }) => ({
10506
10597
  new Error("lookup requires 'type' (the identity schema's record type) alongside 'field'.")
10507
10598
  );
10508
10599
  }
10509
- const req = { type, field, value, from, to, prefix, order, limit };
10600
+ const req = { type, field, value, from, to, prefix, order, startFrom, limit };
10510
10601
  if (kind === "user") {
10511
10602
  page = await client.identity.lookupUsers(req);
10512
10603
  } else if (kind === "org") {
@@ -10522,7 +10613,8 @@ var lookupPrincipal = ({ client, log }) => ({
10522
10613
  );
10523
10614
  }
10524
10615
  const principals = pageItems(page);
10525
- return { content: [{ type: "text", text: JSON.stringify(principals, null, 2) }] };
10616
+ const nextCursor = page.nextCursor ?? null;
10617
+ return { content: [{ type: "text", text: JSON.stringify({ data: principals, nextCursor }, null, 2) }] };
10526
10618
  } catch (err) {
10527
10619
  log.warn({ tool: "lookup_principal", kind, err: String(err) }, "lookup_principal failed");
10528
10620
  return toolError("lookup_principal", err);