@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.mjs CHANGED
@@ -650,6 +650,10 @@ var ReadAccessLogResponse;
650
650
  })(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
651
651
  var RecordRequest;
652
652
  ((RecordRequest2) => {
653
+ RecordRequest2.Status = {
654
+ Active: "ACTIVE",
655
+ Archived: "ARCHIVED"
656
+ };
653
657
  RecordRequest2.IndexMode = {
654
658
  Hybrid: "HYBRID",
655
659
  Semantic: "SEMANTIC",
@@ -659,6 +663,10 @@ var RecordRequest;
659
663
  })(RecordRequest || (RecordRequest = {}));
660
664
  var RecordResponse;
661
665
  ((RecordResponse2) => {
666
+ RecordResponse2.Status = {
667
+ Active: "ACTIVE",
668
+ Archived: "ARCHIVED"
669
+ };
662
670
  RecordResponse2.IndexStatus = {
663
671
  PendingIndex: "PENDING_INDEX",
664
672
  Indexed: "INDEXED",
@@ -3133,7 +3141,7 @@ var AuthClient = class {
3133
3141
  * roleId: "engineering-member",
3134
3142
  * name: "Engineering Team Member",
3135
3143
  * scopes: [{
3136
- * allowed_actions: ["read", "write"]
3144
+ * allowed_actions: ["records:cru", "search:r"]
3137
3145
  * }]
3138
3146
  * }
3139
3147
  * })
@@ -3681,7 +3689,7 @@ var AuthClient = class {
3681
3689
  * roleId: "engineering-member",
3682
3690
  * name: "Engineering Team Member",
3683
3691
  * scopes: [{
3684
- * allowed_actions: ["read", "write"]
3692
+ * allowed_actions: ["records:cru", "search:r"]
3685
3693
  * }]
3686
3694
  * }
3687
3695
  * })
@@ -4584,7 +4592,7 @@ var DocumentsClient = class {
4584
4592
  this._options = normalizeClientOptionsWithAuth(options);
4585
4593
  }
4586
4594
  /**
4587
- * 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.
4595
+ * 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.
4588
4596
  *
4589
4597
  * @param {Vectros.ListDocumentsRequest} request
4590
4598
  * @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -4594,6 +4602,7 @@ var DocumentsClient = class {
4594
4602
  * userId: "550e8400-e29b-41d4-a716-446655440000",
4595
4603
  * orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
4596
4604
  * clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
4605
+ * scope: "group:eng-team",
4597
4606
  * folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
4598
4607
  * startFrom: "doc_prev123"
4599
4608
  * })
@@ -4602,11 +4611,12 @@ var DocumentsClient = class {
4602
4611
  return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
4603
4612
  }
4604
4613
  async __listDocuments(request = {}, requestOptions) {
4605
- const { userId, orgId, clientId, folderId, startFrom, limit } = request;
4614
+ const { userId, orgId, clientId, scope, folderId, startFrom, limit } = request;
4606
4615
  const _queryParams = {
4607
4616
  userId,
4608
4617
  orgId,
4609
4618
  clientId,
4619
+ scope,
4610
4620
  folderId,
4611
4621
  startFrom,
4612
4622
  limit
@@ -4664,9 +4674,10 @@ var DocumentsClient = class {
4664
4674
  return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
4665
4675
  }
4666
4676
  async __ingestDocument(request, requestOptions) {
4667
- const { upsert, body: _body } = request;
4677
+ const { upsert, allowClear, body: _body } = request;
4668
4678
  const _queryParams = {
4669
- upsert
4679
+ upsert,
4680
+ allowClear
4670
4681
  };
4671
4682
  const _authRequest = await this._options.authProvider.getAuthRequest();
4672
4683
  const _headers = mergeHeaders(
@@ -4790,7 +4801,10 @@ var DocumentsClient = class {
4790
4801
  return HttpResponsePromise.fromPromise(this.__updateDocument(request, requestOptions));
4791
4802
  }
4792
4803
  async __updateDocument(request, requestOptions) {
4793
- const { id, body: _body } = request;
4804
+ const { id, allowClear, body: _body } = request;
4805
+ const _queryParams = {
4806
+ allowClear
4807
+ };
4794
4808
  const _authRequest = await this._options.authProvider.getAuthRequest();
4795
4809
  const _headers = mergeHeaders(
4796
4810
  _authRequest.headers,
@@ -4805,7 +4819,7 @@ var DocumentsClient = class {
4805
4819
  method: "PUT",
4806
4820
  headers: _headers,
4807
4821
  contentType: "application/json",
4808
- queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
4822
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
4809
4823
  requestType: "json",
4810
4824
  body: _body,
4811
4825
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
@@ -7541,7 +7555,7 @@ var RecordsClient = class {
7541
7555
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch");
7542
7556
  }
7543
7557
  /**
7544
- * 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.
7558
+ * 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.
7545
7559
  *
7546
7560
  * @param {Vectros.ListRecordsRequest} request
7547
7561
  * @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -7553,6 +7567,7 @@ var RecordsClient = class {
7553
7567
  * userId: "550e8400-e29b-41d4-a716-446655440000",
7554
7568
  * orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
7555
7569
  * clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
7570
+ * scope: "group:eng-team",
7556
7571
  * startFrom: "550e8400-e29b-41d4-a716-446655440000"
7557
7572
  * })
7558
7573
  */
@@ -7560,13 +7575,25 @@ var RecordsClient = class {
7560
7575
  return HttpResponsePromise.fromPromise(this.__listRecords(request, requestOptions));
7561
7576
  }
7562
7577
  async __listRecords(request = {}, requestOptions) {
7563
- const { type: type_, folderId, userId, orgId, clientId, startFrom, limit, includePayload, recent } = request;
7578
+ const {
7579
+ type: type_,
7580
+ folderId,
7581
+ userId,
7582
+ orgId,
7583
+ clientId,
7584
+ scope,
7585
+ startFrom,
7586
+ limit,
7587
+ includePayload,
7588
+ recent
7589
+ } = request;
7564
7590
  const _queryParams = {
7565
7591
  type: type_,
7566
7592
  folderId,
7567
7593
  userId,
7568
7594
  orgId,
7569
7595
  clientId,
7596
+ scope,
7570
7597
  startFrom,
7571
7598
  limit,
7572
7599
  includePayload,
@@ -7631,9 +7658,10 @@ var RecordsClient = class {
7631
7658
  return HttpResponsePromise.fromPromise(this.__createRecord(request, requestOptions));
7632
7659
  }
7633
7660
  async __createRecord(request, requestOptions) {
7634
- const { upsert, body: _body } = request;
7661
+ const { upsert, allowClear, body: _body } = request;
7635
7662
  const _queryParams = {
7636
- upsert
7663
+ upsert,
7664
+ allowClear
7637
7665
  };
7638
7666
  const _authRequest = await this._options.authProvider.getAuthRequest();
7639
7667
  const _headers = mergeHeaders(
@@ -7755,7 +7783,10 @@ var RecordsClient = class {
7755
7783
  return HttpResponsePromise.fromPromise(this.__updateRecord(request, requestOptions));
7756
7784
  }
7757
7785
  async __updateRecord(request, requestOptions) {
7758
- const { id, body: _body } = request;
7786
+ const { id, allowClear, body: _body } = request;
7787
+ const _queryParams = {
7788
+ allowClear
7789
+ };
7759
7790
  const _authRequest = await this._options.authProvider.getAuthRequest();
7760
7791
  const _headers = mergeHeaders(
7761
7792
  _authRequest.headers,
@@ -7770,7 +7801,7 @@ var RecordsClient = class {
7770
7801
  method: "PUT",
7771
7802
  headers: _headers,
7772
7803
  contentType: "application/json",
7773
- queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
7804
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
7774
7805
  requestType: "json",
7775
7806
  body: _body,
7776
7807
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
@@ -8762,8 +8793,8 @@ function warnOnSuboptimalKey(key, log) {
8762
8793
 
8763
8794
  // src/build-info.ts
8764
8795
  var BUILD_INFO = {
8765
- mcpServer: true ? "0.7.0" : "dev",
8766
- sdk: true ? "0.33.0" : "dev"
8796
+ mcpServer: true ? "0.9.0" : "dev",
8797
+ sdk: true ? "0.34.0" : "dev"
8767
8798
  };
8768
8799
 
8769
8800
  // src/log.ts
@@ -8999,7 +9030,7 @@ ${detail}${pointer}`
8999
9030
 
9000
9031
  // src/tools/hybrid_search.ts
9001
9032
  var MCP_DEFAULT_LIMIT = 3;
9002
- var MCP_MAX_LIMIT = 10;
9033
+ var MCP_MAX_LIMIT = 50;
9003
9034
  var MCP_RESPONSE_CHAR_BUDGET = 24e3;
9004
9035
  var SNIPPET_MAX_CHARS = 160;
9005
9036
  var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["tenantId", "owner_id", "model_type", "rootFolderId", "folderId"]);
@@ -9033,9 +9064,9 @@ var inputSchema = {
9033
9064
  "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.)"
9034
9065
  ),
9035
9066
  limit: z.number().int().min(1).max(MCP_MAX_LIMIT).optional().describe(
9036
- `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.`
9067
+ `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.`
9037
9068
  ),
9038
- offset: z.number().int().min(0).optional().describe("Skip the first N hits \u2014 for pagination."),
9069
+ offset: z.number().int().min(0).max(200).optional().describe("Skip the first N hits \u2014 for pagination (0\u2013200)."),
9039
9070
  folderId: z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
9040
9071
  rootFolderId: z.string().optional().describe("Scope to content under this folder AND all its descendants (subtree-root folder id)."),
9041
9072
  // Content narrowing.
@@ -9050,6 +9081,9 @@ var inputSchema = {
9050
9081
  userId: z.string().optional().describe("Restrict to content owned by this user (Vectros UUID)."),
9051
9082
  orgId: z.string().optional().describe("Restrict to content belonging to this org (Vectros UUID)."),
9052
9083
  clientId: z.string().optional().describe("Restrict to content associated with this client (Vectros UUID)."),
9084
+ scope: z.string().optional().describe(
9085
+ '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.'
9086
+ ),
9053
9087
  // Date window.
9054
9088
  createdAfter: z.string().optional().describe("Restrict to content created at/after this ISO 8601 UTC timestamp."),
9055
9089
  createdBefore: z.string().optional().describe("Restrict to content created at/before this ISO 8601 UTC timestamp."),
@@ -9067,7 +9101,7 @@ var inputSchema = {
9067
9101
  var hybridSearch = ({ client, log }) => ({
9068
9102
  name: "hybrid_search",
9069
9103
  title: "Hybrid search",
9070
- 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".`,
9104
+ 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\".",
9071
9105
  inputSchema,
9072
9106
  handler: async (args) => {
9073
9107
  const limit = args.limit ?? MCP_DEFAULT_LIMIT;
@@ -9087,6 +9121,7 @@ var hybridSearch = ({ client, log }) => ({
9087
9121
  userId: args.userId,
9088
9122
  orgId: args.orgId,
9089
9123
  clientId: args.clientId,
9124
+ scope: args.scope,
9090
9125
  createdAfter: args.createdAfter,
9091
9126
  createdBefore: args.createdBefore,
9092
9127
  minSimilarity: args.minSimilarity,
@@ -9113,6 +9148,12 @@ var hybridSearch = ({ client, log }) => ({
9113
9148
  envelope.returnedResults = shaped.length;
9114
9149
  text = JSON.stringify(envelope, null, 2);
9115
9150
  }
9151
+ const offset = args.offset ?? 0;
9152
+ const totalResults = result.totalResults;
9153
+ if (typeof totalResults === "number" && offset + shaped.length < totalResults) {
9154
+ envelope.hasMore = true;
9155
+ text = JSON.stringify(envelope, null, 2);
9156
+ }
9116
9157
  log.debug(
9117
9158
  { tool: "hybrid_search", mode, limit, returned: shaped.length, textLegEmpty },
9118
9159
  "hybrid_search ok"
@@ -9148,7 +9189,7 @@ async function drainPages(fetchPage) {
9148
9189
 
9149
9190
  // src/tools/record_query.ts
9150
9191
  var MCP_DEFAULT_LIMIT2 = 3;
9151
- var MCP_MAX_LIMIT2 = 10;
9192
+ var MCP_MAX_LIMIT2 = 100;
9152
9193
  var inputSchema2 = {
9153
9194
  type: z2.string().min(1, "type (record type) is required").describe('Record type / schema name (e.g. "patient", "clinical_note").'),
9154
9195
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
@@ -9166,14 +9207,18 @@ var inputSchema2 = {
9166
9207
  userId: z2.string().optional().describe("List mode: scope to records owned by this user."),
9167
9208
  orgId: z2.string().optional().describe("List mode: scope to records owned by this org."),
9168
9209
  clientId: z2.string().optional().describe("List mode: scope to records associated with this client."),
9210
+ scope: z2.string().optional().describe(
9211
+ '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.'
9212
+ ),
9213
+ startFrom: z2.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
9169
9214
  limit: z2.number().int().min(1).max(MCP_MAX_LIMIT2).optional().describe(
9170
- `Max records to return. MCP-specific cap of ${MCP_MAX_LIMIT2} (vs API max of 100) to protect the agent context window. Default 3.`
9215
+ `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.`
9171
9216
  )
9172
9217
  };
9173
9218
  var recordQuery = ({ client, log }) => ({
9174
9219
  name: "record_query",
9175
9220
  title: "Record query (list or lookup)",
9176
- 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.",
9221
+ 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.",
9177
9222
  inputSchema: inputSchema2,
9178
9223
  handler: async (args) => {
9179
9224
  const limit = args.limit ?? MCP_DEFAULT_LIMIT2;
@@ -9183,8 +9228,9 @@ var recordQuery = ({ client, log }) => ({
9183
9228
  const from = args.from;
9184
9229
  const to = args.to;
9185
9230
  const prefix = args.prefix;
9231
+ const startFrom = args.startFrom;
9186
9232
  try {
9187
- let records;
9233
+ let page;
9188
9234
  if (field) {
9189
9235
  const hasEquality = value !== void 0;
9190
9236
  const hasRange = from !== void 0 || to !== void 0;
@@ -9207,7 +9253,7 @@ var recordQuery = ({ client, log }) => ({
9207
9253
  if (hasRange && !(from !== void 0 && to !== void 0)) {
9208
9254
  return toolError("record_query", new Error("range lookup requires both 'from' and 'to'."));
9209
9255
  }
9210
- const page = await client.records.lookupRecordsByBody({
9256
+ page = await client.records.lookupRecordsByBody({
9211
9257
  type,
9212
9258
  field,
9213
9259
  value,
@@ -9215,29 +9261,32 @@ var recordQuery = ({ client, log }) => ({
9215
9261
  to,
9216
9262
  prefix,
9217
9263
  order: args.order,
9264
+ startFrom,
9218
9265
  limit
9219
9266
  });
9220
- records = pageItems(page);
9221
9267
  log.debug(
9222
- { tool: "record_query", mode: "lookup", type, field, returned: records.length },
9268
+ { tool: "record_query", mode: "lookup", type, field, returned: pageItems(page).length },
9223
9269
  "record_query lookup ok"
9224
9270
  );
9225
9271
  } else {
9226
- const page = await client.records.listRecords({
9272
+ page = await client.records.listRecords({
9227
9273
  type,
9228
9274
  userId: args.userId,
9229
9275
  orgId: args.orgId,
9230
9276
  clientId: args.clientId,
9277
+ scope: args.scope,
9278
+ startFrom,
9231
9279
  limit
9232
9280
  });
9233
- records = pageItems(page);
9234
9281
  log.debug(
9235
- { tool: "record_query", mode: "list", type, limit, returned: records.length },
9282
+ { tool: "record_query", mode: "list", type, limit, returned: pageItems(page).length },
9236
9283
  "record_query list ok"
9237
9284
  );
9238
9285
  }
9286
+ const records = pageItems(page);
9287
+ const nextCursor = page.nextCursor ?? null;
9239
9288
  return {
9240
- content: [{ type: "text", text: JSON.stringify(records, null, 2) }]
9289
+ content: [{ type: "text", text: JSON.stringify({ data: records, nextCursor }, null, 2) }]
9241
9290
  };
9242
9291
  } catch (err) {
9243
9292
  log.warn({ tool: "record_query", err: String(err) }, "record_query failed");
@@ -9360,7 +9409,10 @@ var inputSchema4 = {
9360
9409
  folderId: z4.string().optional().describe("Group this record into a folder."),
9361
9410
  userId: z4.string().optional().describe("Owning user id."),
9362
9411
  orgId: z4.string().optional().describe("Owning organization id."),
9363
- clientId: z4.string().optional().describe("Associated client id.")
9412
+ clientId: z4.string().optional().describe("Associated client id."),
9413
+ scopes: z4.array(z4.string()).optional().describe(
9414
+ '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.'
9415
+ )
9364
9416
  };
9365
9417
  var recordCreate = ({ client, log }) => ({
9366
9418
  name: "record_create",
@@ -9380,7 +9432,8 @@ var recordCreate = ({ client, log }) => ({
9380
9432
  folderId: args.folderId,
9381
9433
  userId: args.userId,
9382
9434
  orgId: args.orgId,
9383
- clientId: args.clientId
9435
+ clientId: args.clientId,
9436
+ scopes: args.scopes
9384
9437
  }
9385
9438
  });
9386
9439
  log.debug({ tool: "record_create", type, id: created.id }, "record_create ok");
@@ -9465,24 +9518,29 @@ var record_delete_default = recordDelete;
9465
9518
  // src/tools/rag_ask.ts
9466
9519
  import { z as z7 } from "zod";
9467
9520
  var SEARCH_MCP_DEFAULT_LIMIT = 5;
9468
- var SEARCH_MCP_MAX_LIMIT = 10;
9521
+ var SEARCH_MCP_MAX_LIMIT = 50;
9469
9522
  var inputSchema7 = {
9470
9523
  query: z7.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
9471
9524
  instructions: z7.string().optional().describe(
9472
9525
  '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.'
9473
9526
  ),
9474
9527
  model: z7.string().optional().describe(
9475
- "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."
9528
+ "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."
9476
9529
  ),
9477
9530
  search: z7.object({
9478
9531
  mode: z7.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
9479
- limit: z7.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}.`),
9532
+ limit: z7.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
9533
+ `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.`
9534
+ ),
9480
9535
  // Retrieval scoping — parity with hybrid_search so an agent can ground the
9481
9536
  // answer on a specific owner / folder / type / metadata subset, not just the
9482
9537
  // whole-tenant corpus.
9483
9538
  userId: z7.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
9484
9539
  orgId: z7.string().optional().describe("Restrict retrieval to content owned by this org (Vectros UUID)."),
9485
9540
  clientId: z7.string().optional().describe("Restrict retrieval to content tagged with this client (Vectros UUID)."),
9541
+ scope: z7.string().optional().describe(
9542
+ '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.'
9543
+ ),
9486
9544
  folderId: z7.string().optional().describe("Restrict retrieval to this exact folder."),
9487
9545
  rootFolderId: z7.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
9488
9546
  typeName: z7.string().optional().describe("Restrict record retrieval to this schema type."),
@@ -9500,7 +9558,7 @@ var inputSchema7 = {
9500
9558
  var ragAsk = ({ client, log }) => ({
9501
9559
  name: "rag_ask",
9502
9560
  title: "RAG ask (corpus-wide)",
9503
- 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.",
9561
+ 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.",
9504
9562
  inputSchema: inputSchema7,
9505
9563
  handler: async (args, extra) => {
9506
9564
  try {
@@ -9511,6 +9569,7 @@ var ragAsk = ({ client, log }) => ({
9511
9569
  userId: searchArg.userId,
9512
9570
  orgId: searchArg.orgId,
9513
9571
  clientId: searchArg.clientId,
9572
+ scope: searchArg.scope,
9514
9573
  folderId: searchArg.folderId,
9515
9574
  rootFolderId: searchArg.rootFolderId,
9516
9575
  typeName: searchArg.typeName,
@@ -9668,7 +9727,7 @@ var inputSchema9 = {
9668
9727
  var listSchemas = ({ client, log }) => ({
9669
9728
  name: "list_schemas",
9670
9729
  title: "List record schemas",
9671
- 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.',
9730
+ 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.',
9672
9731
  inputSchema: inputSchema9,
9673
9732
  handler: async (args) => {
9674
9733
  const userId = args.userId;
@@ -9790,7 +9849,7 @@ var document_get_default = documentGet;
9790
9849
  // src/tools/document_query.ts
9791
9850
  import { z as z11 } from "zod";
9792
9851
  var MCP_DEFAULT_LIMIT3 = 3;
9793
- var MCP_MAX_LIMIT3 = 10;
9852
+ var MCP_MAX_LIMIT3 = 100;
9794
9853
  var inputSchema11 = {
9795
9854
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
9796
9855
  field: z11.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
@@ -9807,14 +9866,18 @@ var inputSchema11 = {
9807
9866
  userId: z11.string().optional().describe("List mode: scope to documents owned by this user."),
9808
9867
  orgId: z11.string().optional().describe("List mode: scope to documents belonging to this org."),
9809
9868
  clientId: z11.string().optional().describe("List mode: scope to documents associated with this client."),
9869
+ scope: z11.string().optional().describe(
9870
+ '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.'
9871
+ ),
9872
+ startFrom: z11.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
9810
9873
  limit: z11.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
9811
- `Max documents to return. MCP-specific cap of ${MCP_MAX_LIMIT3} (vs API max of 100) to protect the agent context window. Default 3.`
9874
+ `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.`
9812
9875
  )
9813
9876
  };
9814
9877
  var documentQuery = ({ client, log }) => ({
9815
9878
  name: "document_query",
9816
9879
  title: "Document query (list or lookup)",
9817
- 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.",
9880
+ 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.",
9818
9881
  inputSchema: inputSchema11,
9819
9882
  handler: async (args) => {
9820
9883
  const limit = args.limit ?? MCP_DEFAULT_LIMIT3;
@@ -9824,8 +9887,9 @@ var documentQuery = ({ client, log }) => ({
9824
9887
  const to = args.to;
9825
9888
  const prefix = args.prefix;
9826
9889
  const type = args.type;
9890
+ const startFrom = args.startFrom;
9827
9891
  try {
9828
- let documents;
9892
+ let page;
9829
9893
  if (field) {
9830
9894
  const hasEquality = value !== void 0;
9831
9895
  const hasRange = from !== void 0 || to !== void 0;
@@ -9854,7 +9918,7 @@ var documentQuery = ({ client, log }) => ({
9854
9918
  new Error("lookup requires 'type' (the document's bound schema type) alongside 'field'.")
9855
9919
  );
9856
9920
  }
9857
- const page = await client.documents.lookupDocumentsByBody({
9921
+ page = await client.documents.lookupDocumentsByBody({
9858
9922
  type,
9859
9923
  field,
9860
9924
  value,
@@ -9862,28 +9926,31 @@ var documentQuery = ({ client, log }) => ({
9862
9926
  to,
9863
9927
  prefix,
9864
9928
  order: args.order,
9929
+ startFrom,
9865
9930
  limit
9866
9931
  });
9867
- documents = pageItems(page);
9868
9932
  log.debug(
9869
- { tool: "document_query", mode: "lookup", type, field, returned: documents.length },
9933
+ { tool: "document_query", mode: "lookup", type, field, returned: pageItems(page).length },
9870
9934
  "document_query lookup ok"
9871
9935
  );
9872
9936
  } else {
9873
- const page = await client.documents.listDocuments({
9937
+ page = await client.documents.listDocuments({
9874
9938
  folderId: args.folderId,
9875
9939
  userId: args.userId,
9876
9940
  orgId: args.orgId,
9877
9941
  clientId: args.clientId,
9942
+ scope: args.scope,
9943
+ startFrom,
9878
9944
  limit
9879
9945
  });
9880
- documents = pageItems(page);
9881
9946
  log.debug(
9882
- { tool: "document_query", mode: "list", limit, returned: documents.length },
9947
+ { tool: "document_query", mode: "list", limit, returned: pageItems(page).length },
9883
9948
  "document_query list ok"
9884
9949
  );
9885
9950
  }
9886
- return { content: [{ type: "text", text: JSON.stringify(documents, null, 2) }] };
9951
+ const documents = pageItems(page);
9952
+ const nextCursor = page.nextCursor ?? null;
9953
+ return { content: [{ type: "text", text: JSON.stringify({ data: documents, nextCursor }, null, 2) }] };
9887
9954
  } catch (err) {
9888
9955
  log.warn({ tool: "document_query", err: String(err) }, "document_query failed");
9889
9956
  return toolError("document_query", err);
@@ -9992,7 +10059,7 @@ var document_delete_default = documentDelete;
9992
10059
  // src/tools/folder_query.ts
9993
10060
  import { z as z14 } from "zod";
9994
10061
  var MCP_DEFAULT_LIMIT4 = 10;
9995
- var MCP_MAX_LIMIT4 = 50;
10062
+ var MCP_MAX_LIMIT4 = 100;
9996
10063
  var inputSchema14 = {
9997
10064
  id: z14.string().optional().describe("Get mode: fetch this single folder by id."),
9998
10065
  parentId: z14.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
@@ -10000,12 +10067,14 @@ var inputSchema14 = {
10000
10067
  orgId: z14.string().optional().describe("List mode: scope to folders belonging to this org."),
10001
10068
  clientId: z14.string().optional().describe("List mode: scope to folders associated with this client."),
10002
10069
  startFrom: z14.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
10003
- limit: z14.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.`)
10070
+ limit: z14.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
10071
+ `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\`.`
10072
+ )
10004
10073
  };
10005
10074
  var folderQuery = ({ client, log }) => ({
10006
10075
  name: "folder_query",
10007
10076
  title: "Folder query (list or get)",
10008
- 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.",
10077
+ 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.",
10009
10078
  inputSchema: inputSchema14,
10010
10079
  handler: async (args) => {
10011
10080
  const id = args.id;
@@ -10045,7 +10114,10 @@ var inputSchema15 = {
10045
10114
  slug: z15.string().optional().describe("Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. Derived from name when omitted."),
10046
10115
  userId: z15.string().optional().describe("Owning user (Vectros UUID)."),
10047
10116
  orgId: z15.string().optional().describe("Owning org (Vectros UUID)."),
10048
- clientId: z15.string().optional().describe("Associated client (Vectros UUID).")
10117
+ clientId: z15.string().optional().describe("Associated client (Vectros UUID)."),
10118
+ scopes: z15.array(z15.string()).optional().describe(
10119
+ '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.'
10120
+ )
10049
10121
  };
10050
10122
  var folderCreate = ({ client, log }) => ({
10051
10123
  name: "folder_create",
@@ -10062,7 +10134,8 @@ var folderCreate = ({ client, log }) => ({
10062
10134
  slug: args.slug,
10063
10135
  userId: args.userId,
10064
10136
  orgId: args.orgId,
10065
- clientId: args.clientId
10137
+ clientId: args.clientId,
10138
+ scopes: args.scopes
10066
10139
  }
10067
10140
  });
10068
10141
  log.debug({ tool: "folder_create", id: folder.id, name: folder.name }, "folder_create ok");
@@ -10274,7 +10347,10 @@ var inputSchema19 = {
10274
10347
  folderId: z18.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
10275
10348
  userId: z18.string().optional().describe("Owning user ID. Optional."),
10276
10349
  orgId: z18.string().optional().describe("Owning org ID. Optional."),
10277
- clientId: z18.string().optional().describe("Owning client ID. Optional.")
10350
+ clientId: z18.string().optional().describe("Owning client ID. Optional."),
10351
+ scopes: z18.array(z18.string()).optional().describe(
10352
+ "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."
10353
+ )
10278
10354
  };
10279
10355
  var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10280
10356
  name: "document_ingest",
@@ -10308,6 +10384,14 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10308
10384
  )
10309
10385
  );
10310
10386
  }
10387
+ if (filePath && args.scopes !== void 0) {
10388
+ return toolError(
10389
+ "document_ingest",
10390
+ new Error(
10391
+ "`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)."
10392
+ )
10393
+ );
10394
+ }
10311
10395
  const common = {
10312
10396
  folderId: args.folderId,
10313
10397
  payload: args.payload,
@@ -10325,6 +10409,9 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10325
10409
  title,
10326
10410
  text,
10327
10411
  indexMode,
10412
+ // Scope ownership is a TEXT-mode contract only (the upload-init
10413
+ // endpoint has no scopes field — see the gate above).
10414
+ scopes: args.scopes,
10328
10415
  ...common
10329
10416
  }
10330
10417
  });
@@ -10417,7 +10504,7 @@ var document_ingest_default = documentIngest;
10417
10504
  // src/tools/lookup_principal.ts
10418
10505
  import { z as z19 } from "zod";
10419
10506
  var MCP_DEFAULT_LIMIT5 = 10;
10420
- var MCP_MAX_LIMIT5 = 50;
10507
+ var MCP_MAX_LIMIT5 = 100;
10421
10508
  var inputSchema20 = {
10422
10509
  kind: z19.enum(["user", "org", "client"]).describe("Which principal to look up: a user, an organization, or a client."),
10423
10510
  externalId: z19.string().optional().describe(
@@ -10431,12 +10518,15 @@ var inputSchema20 = {
10431
10518
  to: z19.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
10432
10519
  prefix: z19.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
10433
10520
  order: z19.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
10434
- limit: z19.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}.`)
10521
+ startFrom: z19.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
10522
+ limit: z19.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
10523
+ `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\`.`
10524
+ )
10435
10525
  };
10436
10526
  var lookupPrincipal = ({ client, log }) => ({
10437
10527
  name: "lookup_principal",
10438
10528
  title: "Look up a user / org / client identity",
10439
- 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.",
10529
+ 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.",
10440
10530
  inputSchema: inputSchema20,
10441
10531
  handler: async (args) => {
10442
10532
  const kind = args.kind;
@@ -10449,6 +10539,7 @@ var lookupPrincipal = ({ client, log }) => ({
10449
10539
  const to = args.to;
10450
10540
  const prefix = args.prefix;
10451
10541
  const order = args.order;
10542
+ const startFrom = args.startFrom;
10452
10543
  try {
10453
10544
  let page;
10454
10545
  if (externalId !== void 0) {
@@ -10486,7 +10577,7 @@ var lookupPrincipal = ({ client, log }) => ({
10486
10577
  new Error("lookup requires 'type' (the identity schema's record type) alongside 'field'.")
10487
10578
  );
10488
10579
  }
10489
- const req = { type, field, value, from, to, prefix, order, limit };
10580
+ const req = { type, field, value, from, to, prefix, order, startFrom, limit };
10490
10581
  if (kind === "user") {
10491
10582
  page = await client.identity.lookupUsers(req);
10492
10583
  } else if (kind === "org") {
@@ -10502,7 +10593,8 @@ var lookupPrincipal = ({ client, log }) => ({
10502
10593
  );
10503
10594
  }
10504
10595
  const principals = pageItems(page);
10505
- return { content: [{ type: "text", text: JSON.stringify(principals, null, 2) }] };
10596
+ const nextCursor = page.nextCursor ?? null;
10597
+ return { content: [{ type: "text", text: JSON.stringify({ data: principals, nextCursor }, null, 2) }] };
10506
10598
  } catch (err) {
10507
10599
  log.warn({ tool: "lookup_principal", kind, err: String(err) }, "lookup_principal failed");
10508
10600
  return toolError("lookup_principal", err);