@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.mjs CHANGED
@@ -652,6 +652,10 @@ var ReadAccessLogResponse;
652
652
  })(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
653
653
  var RecordRequest;
654
654
  ((RecordRequest2) => {
655
+ RecordRequest2.Status = {
656
+ Active: "ACTIVE",
657
+ Archived: "ARCHIVED"
658
+ };
655
659
  RecordRequest2.IndexMode = {
656
660
  Hybrid: "HYBRID",
657
661
  Semantic: "SEMANTIC",
@@ -661,6 +665,10 @@ var RecordRequest;
661
665
  })(RecordRequest || (RecordRequest = {}));
662
666
  var RecordResponse;
663
667
  ((RecordResponse2) => {
668
+ RecordResponse2.Status = {
669
+ Active: "ACTIVE",
670
+ Archived: "ARCHIVED"
671
+ };
664
672
  RecordResponse2.IndexStatus = {
665
673
  PendingIndex: "PENDING_INDEX",
666
674
  Indexed: "INDEXED",
@@ -3135,7 +3143,7 @@ var AuthClient = class {
3135
3143
  * roleId: "engineering-member",
3136
3144
  * name: "Engineering Team Member",
3137
3145
  * scopes: [{
3138
- * allowed_actions: ["read", "write"]
3146
+ * allowed_actions: ["records:cru", "search:r"]
3139
3147
  * }]
3140
3148
  * }
3141
3149
  * })
@@ -3683,7 +3691,7 @@ var AuthClient = class {
3683
3691
  * roleId: "engineering-member",
3684
3692
  * name: "Engineering Team Member",
3685
3693
  * scopes: [{
3686
- * allowed_actions: ["read", "write"]
3694
+ * allowed_actions: ["records:cru", "search:r"]
3687
3695
  * }]
3688
3696
  * }
3689
3697
  * })
@@ -4586,7 +4594,7 @@ var DocumentsClient = class {
4586
4594
  this._options = normalizeClientOptionsWithAuth(options);
4587
4595
  }
4588
4596
  /**
4589
- * 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.
4597
+ * 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.
4590
4598
  *
4591
4599
  * @param {Vectros.ListDocumentsRequest} request
4592
4600
  * @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -4596,6 +4604,7 @@ var DocumentsClient = class {
4596
4604
  * userId: "550e8400-e29b-41d4-a716-446655440000",
4597
4605
  * orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
4598
4606
  * clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
4607
+ * scope: "group:eng-team",
4599
4608
  * folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
4600
4609
  * startFrom: "doc_prev123"
4601
4610
  * })
@@ -4604,11 +4613,12 @@ var DocumentsClient = class {
4604
4613
  return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
4605
4614
  }
4606
4615
  async __listDocuments(request = {}, requestOptions) {
4607
- const { userId, orgId, clientId, folderId, startFrom, limit } = request;
4616
+ const { userId, orgId, clientId, scope, folderId, startFrom, limit } = request;
4608
4617
  const _queryParams = {
4609
4618
  userId,
4610
4619
  orgId,
4611
4620
  clientId,
4621
+ scope,
4612
4622
  folderId,
4613
4623
  startFrom,
4614
4624
  limit
@@ -4666,9 +4676,10 @@ var DocumentsClient = class {
4666
4676
  return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
4667
4677
  }
4668
4678
  async __ingestDocument(request, requestOptions) {
4669
- const { upsert, body: _body } = request;
4679
+ const { upsert, allowClear, body: _body } = request;
4670
4680
  const _queryParams = {
4671
- upsert
4681
+ upsert,
4682
+ allowClear
4672
4683
  };
4673
4684
  const _authRequest = await this._options.authProvider.getAuthRequest();
4674
4685
  const _headers = mergeHeaders(
@@ -4792,7 +4803,10 @@ var DocumentsClient = class {
4792
4803
  return HttpResponsePromise.fromPromise(this.__updateDocument(request, requestOptions));
4793
4804
  }
4794
4805
  async __updateDocument(request, requestOptions) {
4795
- const { id, body: _body } = request;
4806
+ const { id, allowClear, body: _body } = request;
4807
+ const _queryParams = {
4808
+ allowClear
4809
+ };
4796
4810
  const _authRequest = await this._options.authProvider.getAuthRequest();
4797
4811
  const _headers = mergeHeaders(
4798
4812
  _authRequest.headers,
@@ -4807,7 +4821,7 @@ var DocumentsClient = class {
4807
4821
  method: "PUT",
4808
4822
  headers: _headers,
4809
4823
  contentType: "application/json",
4810
- queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
4824
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
4811
4825
  requestType: "json",
4812
4826
  body: _body,
4813
4827
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
@@ -7543,7 +7557,7 @@ var RecordsClient = class {
7543
7557
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch");
7544
7558
  }
7545
7559
  /**
7546
- * 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.
7560
+ * 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.
7547
7561
  *
7548
7562
  * @param {Vectros.ListRecordsRequest} request
7549
7563
  * @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -7555,6 +7569,7 @@ var RecordsClient = class {
7555
7569
  * userId: "550e8400-e29b-41d4-a716-446655440000",
7556
7570
  * orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
7557
7571
  * clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
7572
+ * scope: "group:eng-team",
7558
7573
  * startFrom: "550e8400-e29b-41d4-a716-446655440000"
7559
7574
  * })
7560
7575
  */
@@ -7562,13 +7577,25 @@ var RecordsClient = class {
7562
7577
  return HttpResponsePromise.fromPromise(this.__listRecords(request, requestOptions));
7563
7578
  }
7564
7579
  async __listRecords(request = {}, requestOptions) {
7565
- const { type: type_, folderId, userId, orgId, clientId, startFrom, limit, includePayload, recent } = request;
7580
+ const {
7581
+ type: type_,
7582
+ folderId,
7583
+ userId,
7584
+ orgId,
7585
+ clientId,
7586
+ scope,
7587
+ startFrom,
7588
+ limit,
7589
+ includePayload,
7590
+ recent
7591
+ } = request;
7566
7592
  const _queryParams = {
7567
7593
  type: type_,
7568
7594
  folderId,
7569
7595
  userId,
7570
7596
  orgId,
7571
7597
  clientId,
7598
+ scope,
7572
7599
  startFrom,
7573
7600
  limit,
7574
7601
  includePayload,
@@ -7633,9 +7660,10 @@ var RecordsClient = class {
7633
7660
  return HttpResponsePromise.fromPromise(this.__createRecord(request, requestOptions));
7634
7661
  }
7635
7662
  async __createRecord(request, requestOptions) {
7636
- const { upsert, body: _body } = request;
7663
+ const { upsert, allowClear, body: _body } = request;
7637
7664
  const _queryParams = {
7638
- upsert
7665
+ upsert,
7666
+ allowClear
7639
7667
  };
7640
7668
  const _authRequest = await this._options.authProvider.getAuthRequest();
7641
7669
  const _headers = mergeHeaders(
@@ -7757,7 +7785,10 @@ var RecordsClient = class {
7757
7785
  return HttpResponsePromise.fromPromise(this.__updateRecord(request, requestOptions));
7758
7786
  }
7759
7787
  async __updateRecord(request, requestOptions) {
7760
- const { id, body: _body } = request;
7788
+ const { id, allowClear, body: _body } = request;
7789
+ const _queryParams = {
7790
+ allowClear
7791
+ };
7761
7792
  const _authRequest = await this._options.authProvider.getAuthRequest();
7762
7793
  const _headers = mergeHeaders(
7763
7794
  _authRequest.headers,
@@ -7772,7 +7803,7 @@ var RecordsClient = class {
7772
7803
  method: "PUT",
7773
7804
  headers: _headers,
7774
7805
  contentType: "application/json",
7775
- queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
7806
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
7776
7807
  requestType: "json",
7777
7808
  body: _body,
7778
7809
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
@@ -8764,8 +8795,8 @@ function warnOnSuboptimalKey(key, log) {
8764
8795
 
8765
8796
  // src/build-info.ts
8766
8797
  var BUILD_INFO = {
8767
- mcpServer: true ? "0.7.0" : "dev",
8768
- sdk: true ? "0.33.0" : "dev"
8798
+ mcpServer: true ? "0.9.0" : "dev",
8799
+ sdk: true ? "0.34.0" : "dev"
8769
8800
  };
8770
8801
  function formatBuildInfo() {
8771
8802
  return `vectros-mcp-server ${BUILD_INFO.mcpServer} (sdk ${BUILD_INFO.sdk})`;
@@ -9004,7 +9035,7 @@ ${detail}${pointer}`
9004
9035
 
9005
9036
  // src/tools/hybrid_search.ts
9006
9037
  var MCP_DEFAULT_LIMIT = 3;
9007
- var MCP_MAX_LIMIT = 10;
9038
+ var MCP_MAX_LIMIT = 50;
9008
9039
  var MCP_RESPONSE_CHAR_BUDGET = 24e3;
9009
9040
  var SNIPPET_MAX_CHARS = 160;
9010
9041
  var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["tenantId", "owner_id", "model_type", "rootFolderId", "folderId"]);
@@ -9038,9 +9069,9 @@ var inputSchema = {
9038
9069
  "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.)"
9039
9070
  ),
9040
9071
  limit: z.number().int().min(1).max(MCP_MAX_LIMIT).optional().describe(
9041
- `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.`
9072
+ `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.`
9042
9073
  ),
9043
- offset: z.number().int().min(0).optional().describe("Skip the first N hits \u2014 for pagination."),
9074
+ offset: z.number().int().min(0).max(200).optional().describe("Skip the first N hits \u2014 for pagination (0\u2013200)."),
9044
9075
  folderId: z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
9045
9076
  rootFolderId: z.string().optional().describe("Scope to content under this folder AND all its descendants (subtree-root folder id)."),
9046
9077
  // Content narrowing.
@@ -9055,6 +9086,9 @@ var inputSchema = {
9055
9086
  userId: z.string().optional().describe("Restrict to content owned by this user (Vectros UUID)."),
9056
9087
  orgId: z.string().optional().describe("Restrict to content belonging to this org (Vectros UUID)."),
9057
9088
  clientId: z.string().optional().describe("Restrict to content associated with this client (Vectros UUID)."),
9089
+ scope: z.string().optional().describe(
9090
+ '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.'
9091
+ ),
9058
9092
  // Date window.
9059
9093
  createdAfter: z.string().optional().describe("Restrict to content created at/after this ISO 8601 UTC timestamp."),
9060
9094
  createdBefore: z.string().optional().describe("Restrict to content created at/before this ISO 8601 UTC timestamp."),
@@ -9072,7 +9106,7 @@ var inputSchema = {
9072
9106
  var hybridSearch = ({ client, log }) => ({
9073
9107
  name: "hybrid_search",
9074
9108
  title: "Hybrid search",
9075
- 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".`,
9109
+ 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\".",
9076
9110
  inputSchema,
9077
9111
  handler: async (args) => {
9078
9112
  const limit = args.limit ?? MCP_DEFAULT_LIMIT;
@@ -9092,6 +9126,7 @@ var hybridSearch = ({ client, log }) => ({
9092
9126
  userId: args.userId,
9093
9127
  orgId: args.orgId,
9094
9128
  clientId: args.clientId,
9129
+ scope: args.scope,
9095
9130
  createdAfter: args.createdAfter,
9096
9131
  createdBefore: args.createdBefore,
9097
9132
  minSimilarity: args.minSimilarity,
@@ -9118,6 +9153,12 @@ var hybridSearch = ({ client, log }) => ({
9118
9153
  envelope.returnedResults = shaped.length;
9119
9154
  text = JSON.stringify(envelope, null, 2);
9120
9155
  }
9156
+ const offset = args.offset ?? 0;
9157
+ const totalResults = result.totalResults;
9158
+ if (typeof totalResults === "number" && offset + shaped.length < totalResults) {
9159
+ envelope.hasMore = true;
9160
+ text = JSON.stringify(envelope, null, 2);
9161
+ }
9121
9162
  log.debug(
9122
9163
  { tool: "hybrid_search", mode, limit, returned: shaped.length, textLegEmpty },
9123
9164
  "hybrid_search ok"
@@ -9153,7 +9194,7 @@ async function drainPages(fetchPage) {
9153
9194
 
9154
9195
  // src/tools/record_query.ts
9155
9196
  var MCP_DEFAULT_LIMIT2 = 3;
9156
- var MCP_MAX_LIMIT2 = 10;
9197
+ var MCP_MAX_LIMIT2 = 100;
9157
9198
  var inputSchema2 = {
9158
9199
  type: z2.string().min(1, "type (record type) is required").describe('Record type / schema name (e.g. "patient", "clinical_note").'),
9159
9200
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
@@ -9171,14 +9212,18 @@ var inputSchema2 = {
9171
9212
  userId: z2.string().optional().describe("List mode: scope to records owned by this user."),
9172
9213
  orgId: z2.string().optional().describe("List mode: scope to records owned by this org."),
9173
9214
  clientId: z2.string().optional().describe("List mode: scope to records associated with this client."),
9215
+ scope: z2.string().optional().describe(
9216
+ '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.'
9217
+ ),
9218
+ 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."),
9174
9219
  limit: z2.number().int().min(1).max(MCP_MAX_LIMIT2).optional().describe(
9175
- `Max records to return. MCP-specific cap of ${MCP_MAX_LIMIT2} (vs API max of 100) to protect the agent context window. Default 3.`
9220
+ `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.`
9176
9221
  )
9177
9222
  };
9178
9223
  var recordQuery = ({ client, log }) => ({
9179
9224
  name: "record_query",
9180
9225
  title: "Record query (list or lookup)",
9181
- 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.",
9226
+ 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.",
9182
9227
  inputSchema: inputSchema2,
9183
9228
  handler: async (args) => {
9184
9229
  const limit = args.limit ?? MCP_DEFAULT_LIMIT2;
@@ -9188,8 +9233,9 @@ var recordQuery = ({ client, log }) => ({
9188
9233
  const from = args.from;
9189
9234
  const to = args.to;
9190
9235
  const prefix = args.prefix;
9236
+ const startFrom = args.startFrom;
9191
9237
  try {
9192
- let records;
9238
+ let page;
9193
9239
  if (field) {
9194
9240
  const hasEquality = value !== void 0;
9195
9241
  const hasRange = from !== void 0 || to !== void 0;
@@ -9212,7 +9258,7 @@ var recordQuery = ({ client, log }) => ({
9212
9258
  if (hasRange && !(from !== void 0 && to !== void 0)) {
9213
9259
  return toolError("record_query", new Error("range lookup requires both 'from' and 'to'."));
9214
9260
  }
9215
- const page = await client.records.lookupRecordsByBody({
9261
+ page = await client.records.lookupRecordsByBody({
9216
9262
  type,
9217
9263
  field,
9218
9264
  value,
@@ -9220,29 +9266,32 @@ var recordQuery = ({ client, log }) => ({
9220
9266
  to,
9221
9267
  prefix,
9222
9268
  order: args.order,
9269
+ startFrom,
9223
9270
  limit
9224
9271
  });
9225
- records = pageItems(page);
9226
9272
  log.debug(
9227
- { tool: "record_query", mode: "lookup", type, field, returned: records.length },
9273
+ { tool: "record_query", mode: "lookup", type, field, returned: pageItems(page).length },
9228
9274
  "record_query lookup ok"
9229
9275
  );
9230
9276
  } else {
9231
- const page = await client.records.listRecords({
9277
+ page = await client.records.listRecords({
9232
9278
  type,
9233
9279
  userId: args.userId,
9234
9280
  orgId: args.orgId,
9235
9281
  clientId: args.clientId,
9282
+ scope: args.scope,
9283
+ startFrom,
9236
9284
  limit
9237
9285
  });
9238
- records = pageItems(page);
9239
9286
  log.debug(
9240
- { tool: "record_query", mode: "list", type, limit, returned: records.length },
9287
+ { tool: "record_query", mode: "list", type, limit, returned: pageItems(page).length },
9241
9288
  "record_query list ok"
9242
9289
  );
9243
9290
  }
9291
+ const records = pageItems(page);
9292
+ const nextCursor = page.nextCursor ?? null;
9244
9293
  return {
9245
- content: [{ type: "text", text: JSON.stringify(records, null, 2) }]
9294
+ content: [{ type: "text", text: JSON.stringify({ data: records, nextCursor }, null, 2) }]
9246
9295
  };
9247
9296
  } catch (err) {
9248
9297
  log.warn({ tool: "record_query", err: String(err) }, "record_query failed");
@@ -9365,7 +9414,10 @@ var inputSchema4 = {
9365
9414
  folderId: z4.string().optional().describe("Group this record into a folder."),
9366
9415
  userId: z4.string().optional().describe("Owning user id."),
9367
9416
  orgId: z4.string().optional().describe("Owning organization id."),
9368
- clientId: z4.string().optional().describe("Associated client id.")
9417
+ clientId: z4.string().optional().describe("Associated client id."),
9418
+ scopes: z4.array(z4.string()).optional().describe(
9419
+ '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.'
9420
+ )
9369
9421
  };
9370
9422
  var recordCreate = ({ client, log }) => ({
9371
9423
  name: "record_create",
@@ -9385,7 +9437,8 @@ var recordCreate = ({ client, log }) => ({
9385
9437
  folderId: args.folderId,
9386
9438
  userId: args.userId,
9387
9439
  orgId: args.orgId,
9388
- clientId: args.clientId
9440
+ clientId: args.clientId,
9441
+ scopes: args.scopes
9389
9442
  }
9390
9443
  });
9391
9444
  log.debug({ tool: "record_create", type, id: created.id }, "record_create ok");
@@ -9470,24 +9523,29 @@ var record_delete_default = recordDelete;
9470
9523
  // src/tools/rag_ask.ts
9471
9524
  import { z as z7 } from "zod";
9472
9525
  var SEARCH_MCP_DEFAULT_LIMIT = 5;
9473
- var SEARCH_MCP_MAX_LIMIT = 10;
9526
+ var SEARCH_MCP_MAX_LIMIT = 50;
9474
9527
  var inputSchema7 = {
9475
9528
  query: z7.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
9476
9529
  instructions: z7.string().optional().describe(
9477
9530
  '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.'
9478
9531
  ),
9479
9532
  model: z7.string().optional().describe(
9480
- "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."
9533
+ "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."
9481
9534
  ),
9482
9535
  search: z7.object({
9483
9536
  mode: z7.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
9484
- 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}.`),
9537
+ limit: z7.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
9538
+ `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.`
9539
+ ),
9485
9540
  // Retrieval scoping — parity with hybrid_search so an agent can ground the
9486
9541
  // answer on a specific owner / folder / type / metadata subset, not just the
9487
9542
  // whole-tenant corpus.
9488
9543
  userId: z7.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
9489
9544
  orgId: z7.string().optional().describe("Restrict retrieval to content owned by this org (Vectros UUID)."),
9490
9545
  clientId: z7.string().optional().describe("Restrict retrieval to content tagged with this client (Vectros UUID)."),
9546
+ scope: z7.string().optional().describe(
9547
+ '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.'
9548
+ ),
9491
9549
  folderId: z7.string().optional().describe("Restrict retrieval to this exact folder."),
9492
9550
  rootFolderId: z7.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
9493
9551
  typeName: z7.string().optional().describe("Restrict record retrieval to this schema type."),
@@ -9505,7 +9563,7 @@ var inputSchema7 = {
9505
9563
  var ragAsk = ({ client, log }) => ({
9506
9564
  name: "rag_ask",
9507
9565
  title: "RAG ask (corpus-wide)",
9508
- 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.",
9566
+ 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.",
9509
9567
  inputSchema: inputSchema7,
9510
9568
  handler: async (args, extra) => {
9511
9569
  try {
@@ -9516,6 +9574,7 @@ var ragAsk = ({ client, log }) => ({
9516
9574
  userId: searchArg.userId,
9517
9575
  orgId: searchArg.orgId,
9518
9576
  clientId: searchArg.clientId,
9577
+ scope: searchArg.scope,
9519
9578
  folderId: searchArg.folderId,
9520
9579
  rootFolderId: searchArg.rootFolderId,
9521
9580
  typeName: searchArg.typeName,
@@ -9673,7 +9732,7 @@ var inputSchema9 = {
9673
9732
  var listSchemas = ({ client, log }) => ({
9674
9733
  name: "list_schemas",
9675
9734
  title: "List record schemas",
9676
- 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.',
9735
+ 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.',
9677
9736
  inputSchema: inputSchema9,
9678
9737
  handler: async (args) => {
9679
9738
  const userId = args.userId;
@@ -9795,7 +9854,7 @@ var document_get_default = documentGet;
9795
9854
  // src/tools/document_query.ts
9796
9855
  import { z as z11 } from "zod";
9797
9856
  var MCP_DEFAULT_LIMIT3 = 3;
9798
- var MCP_MAX_LIMIT3 = 10;
9857
+ var MCP_MAX_LIMIT3 = 100;
9799
9858
  var inputSchema11 = {
9800
9859
  // Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
9801
9860
  field: z11.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
@@ -9812,14 +9871,18 @@ var inputSchema11 = {
9812
9871
  userId: z11.string().optional().describe("List mode: scope to documents owned by this user."),
9813
9872
  orgId: z11.string().optional().describe("List mode: scope to documents belonging to this org."),
9814
9873
  clientId: z11.string().optional().describe("List mode: scope to documents associated with this client."),
9874
+ scope: z11.string().optional().describe(
9875
+ '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.'
9876
+ ),
9877
+ 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."),
9815
9878
  limit: z11.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
9816
- `Max documents to return. MCP-specific cap of ${MCP_MAX_LIMIT3} (vs API max of 100) to protect the agent context window. Default 3.`
9879
+ `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.`
9817
9880
  )
9818
9881
  };
9819
9882
  var documentQuery = ({ client, log }) => ({
9820
9883
  name: "document_query",
9821
9884
  title: "Document query (list or lookup)",
9822
- 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.",
9885
+ 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.",
9823
9886
  inputSchema: inputSchema11,
9824
9887
  handler: async (args) => {
9825
9888
  const limit = args.limit ?? MCP_DEFAULT_LIMIT3;
@@ -9829,8 +9892,9 @@ var documentQuery = ({ client, log }) => ({
9829
9892
  const to = args.to;
9830
9893
  const prefix = args.prefix;
9831
9894
  const type = args.type;
9895
+ const startFrom = args.startFrom;
9832
9896
  try {
9833
- let documents;
9897
+ let page;
9834
9898
  if (field) {
9835
9899
  const hasEquality = value !== void 0;
9836
9900
  const hasRange = from !== void 0 || to !== void 0;
@@ -9859,7 +9923,7 @@ var documentQuery = ({ client, log }) => ({
9859
9923
  new Error("lookup requires 'type' (the document's bound schema type) alongside 'field'.")
9860
9924
  );
9861
9925
  }
9862
- const page = await client.documents.lookupDocumentsByBody({
9926
+ page = await client.documents.lookupDocumentsByBody({
9863
9927
  type,
9864
9928
  field,
9865
9929
  value,
@@ -9867,28 +9931,31 @@ var documentQuery = ({ client, log }) => ({
9867
9931
  to,
9868
9932
  prefix,
9869
9933
  order: args.order,
9934
+ startFrom,
9870
9935
  limit
9871
9936
  });
9872
- documents = pageItems(page);
9873
9937
  log.debug(
9874
- { tool: "document_query", mode: "lookup", type, field, returned: documents.length },
9938
+ { tool: "document_query", mode: "lookup", type, field, returned: pageItems(page).length },
9875
9939
  "document_query lookup ok"
9876
9940
  );
9877
9941
  } else {
9878
- const page = await client.documents.listDocuments({
9942
+ page = await client.documents.listDocuments({
9879
9943
  folderId: args.folderId,
9880
9944
  userId: args.userId,
9881
9945
  orgId: args.orgId,
9882
9946
  clientId: args.clientId,
9947
+ scope: args.scope,
9948
+ startFrom,
9883
9949
  limit
9884
9950
  });
9885
- documents = pageItems(page);
9886
9951
  log.debug(
9887
- { tool: "document_query", mode: "list", limit, returned: documents.length },
9952
+ { tool: "document_query", mode: "list", limit, returned: pageItems(page).length },
9888
9953
  "document_query list ok"
9889
9954
  );
9890
9955
  }
9891
- return { content: [{ type: "text", text: JSON.stringify(documents, null, 2) }] };
9956
+ const documents = pageItems(page);
9957
+ const nextCursor = page.nextCursor ?? null;
9958
+ return { content: [{ type: "text", text: JSON.stringify({ data: documents, nextCursor }, null, 2) }] };
9892
9959
  } catch (err) {
9893
9960
  log.warn({ tool: "document_query", err: String(err) }, "document_query failed");
9894
9961
  return toolError("document_query", err);
@@ -9997,7 +10064,7 @@ var document_delete_default = documentDelete;
9997
10064
  // src/tools/folder_query.ts
9998
10065
  import { z as z14 } from "zod";
9999
10066
  var MCP_DEFAULT_LIMIT4 = 10;
10000
- var MCP_MAX_LIMIT4 = 50;
10067
+ var MCP_MAX_LIMIT4 = 100;
10001
10068
  var inputSchema14 = {
10002
10069
  id: z14.string().optional().describe("Get mode: fetch this single folder by id."),
10003
10070
  parentId: z14.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
@@ -10005,12 +10072,14 @@ var inputSchema14 = {
10005
10072
  orgId: z14.string().optional().describe("List mode: scope to folders belonging to this org."),
10006
10073
  clientId: z14.string().optional().describe("List mode: scope to folders associated with this client."),
10007
10074
  startFrom: z14.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
10008
- 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.`)
10075
+ limit: z14.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
10076
+ `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\`.`
10077
+ )
10009
10078
  };
10010
10079
  var folderQuery = ({ client, log }) => ({
10011
10080
  name: "folder_query",
10012
10081
  title: "Folder query (list or get)",
10013
- 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.",
10082
+ 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.",
10014
10083
  inputSchema: inputSchema14,
10015
10084
  handler: async (args) => {
10016
10085
  const id = args.id;
@@ -10050,7 +10119,10 @@ var inputSchema15 = {
10050
10119
  slug: z15.string().optional().describe("Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. Derived from name when omitted."),
10051
10120
  userId: z15.string().optional().describe("Owning user (Vectros UUID)."),
10052
10121
  orgId: z15.string().optional().describe("Owning org (Vectros UUID)."),
10053
- clientId: z15.string().optional().describe("Associated client (Vectros UUID).")
10122
+ clientId: z15.string().optional().describe("Associated client (Vectros UUID)."),
10123
+ scopes: z15.array(z15.string()).optional().describe(
10124
+ '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.'
10125
+ )
10054
10126
  };
10055
10127
  var folderCreate = ({ client, log }) => ({
10056
10128
  name: "folder_create",
@@ -10067,7 +10139,8 @@ var folderCreate = ({ client, log }) => ({
10067
10139
  slug: args.slug,
10068
10140
  userId: args.userId,
10069
10141
  orgId: args.orgId,
10070
- clientId: args.clientId
10142
+ clientId: args.clientId,
10143
+ scopes: args.scopes
10071
10144
  }
10072
10145
  });
10073
10146
  log.debug({ tool: "folder_create", id: folder.id, name: folder.name }, "folder_create ok");
@@ -10279,7 +10352,10 @@ var inputSchema19 = {
10279
10352
  folderId: z18.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
10280
10353
  userId: z18.string().optional().describe("Owning user ID. Optional."),
10281
10354
  orgId: z18.string().optional().describe("Owning org ID. Optional."),
10282
- clientId: z18.string().optional().describe("Owning client ID. Optional.")
10355
+ clientId: z18.string().optional().describe("Owning client ID. Optional."),
10356
+ scopes: z18.array(z18.string()).optional().describe(
10357
+ "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."
10358
+ )
10283
10359
  };
10284
10360
  var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10285
10361
  name: "document_ingest",
@@ -10313,6 +10389,14 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10313
10389
  )
10314
10390
  );
10315
10391
  }
10392
+ if (filePath && args.scopes !== void 0) {
10393
+ return toolError(
10394
+ "document_ingest",
10395
+ new Error(
10396
+ "`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)."
10397
+ )
10398
+ );
10399
+ }
10316
10400
  const common = {
10317
10401
  folderId: args.folderId,
10318
10402
  payload: args.payload,
@@ -10330,6 +10414,9 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
10330
10414
  title,
10331
10415
  text,
10332
10416
  indexMode,
10417
+ // Scope ownership is a TEXT-mode contract only (the upload-init
10418
+ // endpoint has no scopes field — see the gate above).
10419
+ scopes: args.scopes,
10333
10420
  ...common
10334
10421
  }
10335
10422
  });
@@ -10422,7 +10509,7 @@ var document_ingest_default = documentIngest;
10422
10509
  // src/tools/lookup_principal.ts
10423
10510
  import { z as z19 } from "zod";
10424
10511
  var MCP_DEFAULT_LIMIT5 = 10;
10425
- var MCP_MAX_LIMIT5 = 50;
10512
+ var MCP_MAX_LIMIT5 = 100;
10426
10513
  var inputSchema20 = {
10427
10514
  kind: z19.enum(["user", "org", "client"]).describe("Which principal to look up: a user, an organization, or a client."),
10428
10515
  externalId: z19.string().optional().describe(
@@ -10436,12 +10523,15 @@ var inputSchema20 = {
10436
10523
  to: z19.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
10437
10524
  prefix: z19.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
10438
10525
  order: z19.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
10439
- 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}.`)
10526
+ startFrom: z19.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
10527
+ limit: z19.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
10528
+ `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\`.`
10529
+ )
10440
10530
  };
10441
10531
  var lookupPrincipal = ({ client, log }) => ({
10442
10532
  name: "lookup_principal",
10443
10533
  title: "Look up a user / org / client identity",
10444
- 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.",
10534
+ 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.",
10445
10535
  inputSchema: inputSchema20,
10446
10536
  handler: async (args) => {
10447
10537
  const kind = args.kind;
@@ -10454,6 +10544,7 @@ var lookupPrincipal = ({ client, log }) => ({
10454
10544
  const to = args.to;
10455
10545
  const prefix = args.prefix;
10456
10546
  const order = args.order;
10547
+ const startFrom = args.startFrom;
10457
10548
  try {
10458
10549
  let page;
10459
10550
  if (externalId !== void 0) {
@@ -10491,7 +10582,7 @@ var lookupPrincipal = ({ client, log }) => ({
10491
10582
  new Error("lookup requires 'type' (the identity schema's record type) alongside 'field'.")
10492
10583
  );
10493
10584
  }
10494
- const req = { type, field, value, from, to, prefix, order, limit };
10585
+ const req = { type, field, value, from, to, prefix, order, startFrom, limit };
10495
10586
  if (kind === "user") {
10496
10587
  page = await client.identity.lookupUsers(req);
10497
10588
  } else if (kind === "org") {
@@ -10507,7 +10598,8 @@ var lookupPrincipal = ({ client, log }) => ({
10507
10598
  );
10508
10599
  }
10509
10600
  const principals = pageItems(page);
10510
- return { content: [{ type: "text", text: JSON.stringify(principals, null, 2) }] };
10601
+ const nextCursor = page.nextCursor ?? null;
10602
+ return { content: [{ type: "text", text: JSON.stringify({ data: principals, nextCursor }, null, 2) }] };
10511
10603
  } catch (err) {
10512
10604
  log.warn({ tool: "lookup_principal", kind, err: String(err) }, "lookup_principal failed");
10513
10605
  return toolError("lookup_principal", err);