@vectros-ai/mcp-server 0.6.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/CHANGELOG.md +79 -0
- package/README.md +7 -0
- package/dist/cli-http.js +160 -71
- package/dist/cli-http.js.map +1 -1
- package/dist/cli-http.mjs +160 -71
- package/dist/cli-http.mjs.map +1 -1
- package/dist/cli.js +160 -71
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +160 -71
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +160 -71
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +160 -71
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -691,6 +691,10 @@ var ReadAccessLogResponse;
|
|
|
691
691
|
})(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
|
|
692
692
|
var RecordRequest;
|
|
693
693
|
((RecordRequest2) => {
|
|
694
|
+
RecordRequest2.Status = {
|
|
695
|
+
Active: "ACTIVE",
|
|
696
|
+
Archived: "ARCHIVED"
|
|
697
|
+
};
|
|
694
698
|
RecordRequest2.IndexMode = {
|
|
695
699
|
Hybrid: "HYBRID",
|
|
696
700
|
Semantic: "SEMANTIC",
|
|
@@ -700,6 +704,10 @@ var RecordRequest;
|
|
|
700
704
|
})(RecordRequest || (RecordRequest = {}));
|
|
701
705
|
var RecordResponse;
|
|
702
706
|
((RecordResponse2) => {
|
|
707
|
+
RecordResponse2.Status = {
|
|
708
|
+
Active: "ACTIVE",
|
|
709
|
+
Archived: "ARCHIVED"
|
|
710
|
+
};
|
|
703
711
|
RecordResponse2.IndexStatus = {
|
|
704
712
|
PendingIndex: "PENDING_INDEX",
|
|
705
713
|
Indexed: "INDEXED",
|
|
@@ -3174,7 +3182,7 @@ var AuthClient = class {
|
|
|
3174
3182
|
* roleId: "engineering-member",
|
|
3175
3183
|
* name: "Engineering Team Member",
|
|
3176
3184
|
* scopes: [{
|
|
3177
|
-
* allowed_actions: ["
|
|
3185
|
+
* allowed_actions: ["records:cru", "search:r"]
|
|
3178
3186
|
* }]
|
|
3179
3187
|
* }
|
|
3180
3188
|
* })
|
|
@@ -3722,7 +3730,7 @@ var AuthClient = class {
|
|
|
3722
3730
|
* roleId: "engineering-member",
|
|
3723
3731
|
* name: "Engineering Team Member",
|
|
3724
3732
|
* scopes: [{
|
|
3725
|
-
* allowed_actions: ["
|
|
3733
|
+
* allowed_actions: ["records:cru", "search:r"]
|
|
3726
3734
|
* }]
|
|
3727
3735
|
* }
|
|
3728
3736
|
* })
|
|
@@ -4625,7 +4633,7 @@ var DocumentsClient = class {
|
|
|
4625
4633
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
4626
4634
|
}
|
|
4627
4635
|
/**
|
|
4628
|
-
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, or `
|
|
4636
|
+
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, `clientId`, or `scope`). The response is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom` to fetch the next page. Requires the `documents:r` scope.
|
|
4629
4637
|
*
|
|
4630
4638
|
* @param {Vectros.ListDocumentsRequest} request
|
|
4631
4639
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4635,6 +4643,7 @@ var DocumentsClient = class {
|
|
|
4635
4643
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
4636
4644
|
* orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
4637
4645
|
* clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
4646
|
+
* scope: "group:eng-team",
|
|
4638
4647
|
* folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
4639
4648
|
* startFrom: "doc_prev123"
|
|
4640
4649
|
* })
|
|
@@ -4643,11 +4652,12 @@ var DocumentsClient = class {
|
|
|
4643
4652
|
return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
|
|
4644
4653
|
}
|
|
4645
4654
|
async __listDocuments(request = {}, requestOptions) {
|
|
4646
|
-
const { userId, orgId, clientId, folderId, startFrom, limit } = request;
|
|
4655
|
+
const { userId, orgId, clientId, scope, folderId, startFrom, limit } = request;
|
|
4647
4656
|
const _queryParams = {
|
|
4648
4657
|
userId,
|
|
4649
4658
|
orgId,
|
|
4650
4659
|
clientId,
|
|
4660
|
+
scope,
|
|
4651
4661
|
folderId,
|
|
4652
4662
|
startFrom,
|
|
4653
4663
|
limit
|
|
@@ -4705,9 +4715,10 @@ var DocumentsClient = class {
|
|
|
4705
4715
|
return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
|
|
4706
4716
|
}
|
|
4707
4717
|
async __ingestDocument(request, requestOptions) {
|
|
4708
|
-
const { upsert, body: _body } = request;
|
|
4718
|
+
const { upsert, allowClear, body: _body } = request;
|
|
4709
4719
|
const _queryParams = {
|
|
4710
|
-
upsert
|
|
4720
|
+
upsert,
|
|
4721
|
+
allowClear
|
|
4711
4722
|
};
|
|
4712
4723
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4713
4724
|
const _headers = mergeHeaders(
|
|
@@ -4831,7 +4842,10 @@ var DocumentsClient = class {
|
|
|
4831
4842
|
return HttpResponsePromise.fromPromise(this.__updateDocument(request, requestOptions));
|
|
4832
4843
|
}
|
|
4833
4844
|
async __updateDocument(request, requestOptions) {
|
|
4834
|
-
const { id, body: _body } = request;
|
|
4845
|
+
const { id, allowClear, body: _body } = request;
|
|
4846
|
+
const _queryParams = {
|
|
4847
|
+
allowClear
|
|
4848
|
+
};
|
|
4835
4849
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4836
4850
|
const _headers = mergeHeaders(
|
|
4837
4851
|
_authRequest.headers,
|
|
@@ -4846,7 +4860,7 @@ var DocumentsClient = class {
|
|
|
4846
4860
|
method: "PUT",
|
|
4847
4861
|
headers: _headers,
|
|
4848
4862
|
contentType: "application/json",
|
|
4849
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4863
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4850
4864
|
requestType: "json",
|
|
4851
4865
|
body: _body,
|
|
4852
4866
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
@@ -4937,7 +4951,7 @@ var DocumentsClient = class {
|
|
|
4937
4951
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/documents/{id}");
|
|
4938
4952
|
}
|
|
4939
4953
|
/**
|
|
4940
|
-
* Partially updates a document using an RFC 7386 JSON Merge Patch. The `payload` object is deep-merged: keys you send overwrite existing values (recursing into nested objects), a key set to `null` is deleted, and keys you omit are preserved — unlike PUT, which replaces the whole payload. Top-level fields (`title`, `
|
|
4954
|
+
* Partially updates a document using an RFC 7386 JSON Merge Patch. The `payload` object is deep-merged: keys you send overwrite existing values (recursing into nested objects), a key set to `null` is deleted, and keys you omit are preserved — unlike PUT, which replaces the whole payload. Top-level fields (`title`, `folderId`, `schemaId`, ownership) are set when present and left unchanged when omitted; sending a top-level field as `null` is rejected. Supplying `text` re-ingests the document body (same as PUT). `indexMode`, `externalId`, and `storeText` (text retention is fixed at ingest) are immutable and rejected if present. The merged result is validated against the bound schema. Pass `expectedVersion` for optimistic concurrency (409 on conflict). Requires the `documents:u` scope.
|
|
4941
4955
|
*
|
|
4942
4956
|
* @param {Vectros.PatchDocumentRequest} request
|
|
4943
4957
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -5189,7 +5203,7 @@ var DocumentsClient = class {
|
|
|
5189
5203
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/download");
|
|
5190
5204
|
}
|
|
5191
5205
|
/**
|
|
5192
|
-
* Returns the full
|
|
5206
|
+
* Returns the document's full text body when it is retained: always available for text-ingested documents, and for file-uploaded documents unless they were uploaded with `storeText=false` (which discards the extracted text once indexing completes — the original file remains available via `GET /{id}/download`). Returns 404 when the document does not exist, its text was not retained, or extraction has not yet completed. Requires the `documents:r` scope.
|
|
5193
5207
|
*
|
|
5194
5208
|
* @param {Vectros.GetDocumentTextRequest} request
|
|
5195
5209
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7582,7 +7596,7 @@ var RecordsClient = class {
|
|
|
7582
7596
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch");
|
|
7583
7597
|
}
|
|
7584
7598
|
/**
|
|
7585
|
-
* Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
|
|
7599
|
+
* Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`, `scope`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
|
|
7586
7600
|
*
|
|
7587
7601
|
* @param {Vectros.ListRecordsRequest} request
|
|
7588
7602
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7594,6 +7608,7 @@ var RecordsClient = class {
|
|
|
7594
7608
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
7595
7609
|
* orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
7596
7610
|
* clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
7611
|
+
* scope: "group:eng-team",
|
|
7597
7612
|
* startFrom: "550e8400-e29b-41d4-a716-446655440000"
|
|
7598
7613
|
* })
|
|
7599
7614
|
*/
|
|
@@ -7601,13 +7616,25 @@ var RecordsClient = class {
|
|
|
7601
7616
|
return HttpResponsePromise.fromPromise(this.__listRecords(request, requestOptions));
|
|
7602
7617
|
}
|
|
7603
7618
|
async __listRecords(request = {}, requestOptions) {
|
|
7604
|
-
const {
|
|
7619
|
+
const {
|
|
7620
|
+
type: type_,
|
|
7621
|
+
folderId,
|
|
7622
|
+
userId,
|
|
7623
|
+
orgId,
|
|
7624
|
+
clientId,
|
|
7625
|
+
scope,
|
|
7626
|
+
startFrom,
|
|
7627
|
+
limit,
|
|
7628
|
+
includePayload,
|
|
7629
|
+
recent
|
|
7630
|
+
} = request;
|
|
7605
7631
|
const _queryParams = {
|
|
7606
7632
|
type: type_,
|
|
7607
7633
|
folderId,
|
|
7608
7634
|
userId,
|
|
7609
7635
|
orgId,
|
|
7610
7636
|
clientId,
|
|
7637
|
+
scope,
|
|
7611
7638
|
startFrom,
|
|
7612
7639
|
limit,
|
|
7613
7640
|
includePayload,
|
|
@@ -7672,9 +7699,10 @@ var RecordsClient = class {
|
|
|
7672
7699
|
return HttpResponsePromise.fromPromise(this.__createRecord(request, requestOptions));
|
|
7673
7700
|
}
|
|
7674
7701
|
async __createRecord(request, requestOptions) {
|
|
7675
|
-
const { upsert, body: _body } = request;
|
|
7702
|
+
const { upsert, allowClear, body: _body } = request;
|
|
7676
7703
|
const _queryParams = {
|
|
7677
|
-
upsert
|
|
7704
|
+
upsert,
|
|
7705
|
+
allowClear
|
|
7678
7706
|
};
|
|
7679
7707
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7680
7708
|
const _headers = mergeHeaders(
|
|
@@ -7796,7 +7824,10 @@ var RecordsClient = class {
|
|
|
7796
7824
|
return HttpResponsePromise.fromPromise(this.__updateRecord(request, requestOptions));
|
|
7797
7825
|
}
|
|
7798
7826
|
async __updateRecord(request, requestOptions) {
|
|
7799
|
-
const { id, body: _body } = request;
|
|
7827
|
+
const { id, allowClear, body: _body } = request;
|
|
7828
|
+
const _queryParams = {
|
|
7829
|
+
allowClear
|
|
7830
|
+
};
|
|
7800
7831
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7801
7832
|
const _headers = mergeHeaders(
|
|
7802
7833
|
_authRequest.headers,
|
|
@@ -7811,7 +7842,7 @@ var RecordsClient = class {
|
|
|
7811
7842
|
method: "PUT",
|
|
7812
7843
|
headers: _headers,
|
|
7813
7844
|
contentType: "application/json",
|
|
7814
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
7845
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
7815
7846
|
requestType: "json",
|
|
7816
7847
|
body: _body,
|
|
7817
7848
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
@@ -8798,8 +8829,8 @@ function warnOnSuboptimalKey(key, log) {
|
|
|
8798
8829
|
|
|
8799
8830
|
// src/build-info.ts
|
|
8800
8831
|
var BUILD_INFO = {
|
|
8801
|
-
mcpServer: true ? "0.
|
|
8802
|
-
sdk: true ? "0.
|
|
8832
|
+
mcpServer: true ? "0.9.0" : "dev",
|
|
8833
|
+
sdk: true ? "0.34.0" : "dev"
|
|
8803
8834
|
};
|
|
8804
8835
|
|
|
8805
8836
|
// src/log.ts
|
|
@@ -9035,7 +9066,7 @@ ${detail}${pointer}`
|
|
|
9035
9066
|
|
|
9036
9067
|
// src/tools/hybrid_search.ts
|
|
9037
9068
|
var MCP_DEFAULT_LIMIT = 3;
|
|
9038
|
-
var MCP_MAX_LIMIT =
|
|
9069
|
+
var MCP_MAX_LIMIT = 50;
|
|
9039
9070
|
var MCP_RESPONSE_CHAR_BUDGET = 24e3;
|
|
9040
9071
|
var SNIPPET_MAX_CHARS = 160;
|
|
9041
9072
|
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["tenantId", "owner_id", "model_type", "rootFolderId", "folderId"]);
|
|
@@ -9069,9 +9100,9 @@ var inputSchema = {
|
|
|
9069
9100
|
"Keyword-match precision for the BM25 leg (TEXT/HYBRID modes). OR = any term (broad recall), AND = all terms (higher precision), PHRASE = contiguous phrase. Default: PHRASE (slop 3) in HYBRID, OR in TEXT. (The advanced raw-query COMPLEX mode is intentionally not exposed here.)"
|
|
9070
9101
|
),
|
|
9071
9102
|
limit: import_zod.z.number().int().min(1).max(MCP_MAX_LIMIT).optional().describe(
|
|
9072
|
-
`Max hits
|
|
9103
|
+
`Max hits per page. Defaults to a low ${MCP_DEFAULT_LIMIT} to protect the agent context window; raise up to ${MCP_MAX_LIMIT} (the most a single search returns) in one call when you can accept the larger result, or page with \`offset\` instead.`
|
|
9073
9104
|
),
|
|
9074
|
-
offset: import_zod.z.number().int().min(0).optional().describe("Skip the first N hits \u2014 for pagination."),
|
|
9105
|
+
offset: import_zod.z.number().int().min(0).max(200).optional().describe("Skip the first N hits \u2014 for pagination (0\u2013200)."),
|
|
9075
9106
|
folderId: import_zod.z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
|
|
9076
9107
|
rootFolderId: import_zod.z.string().optional().describe("Scope to content under this folder AND all its descendants (subtree-root folder id)."),
|
|
9077
9108
|
// Content narrowing.
|
|
@@ -9086,6 +9117,9 @@ var inputSchema = {
|
|
|
9086
9117
|
userId: import_zod.z.string().optional().describe("Restrict to content owned by this user (Vectros UUID)."),
|
|
9087
9118
|
orgId: import_zod.z.string().optional().describe("Restrict to content belonging to this org (Vectros UUID)."),
|
|
9088
9119
|
clientId: import_zod.z.string().optional().describe("Restrict to content associated with this client (Vectros UUID)."),
|
|
9120
|
+
scope: import_zod.z.string().optional().describe(
|
|
9121
|
+
'Restrict to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9122
|
+
),
|
|
9089
9123
|
// Date window.
|
|
9090
9124
|
createdAfter: import_zod.z.string().optional().describe("Restrict to content created at/after this ISO 8601 UTC timestamp."),
|
|
9091
9125
|
createdBefore: import_zod.z.string().optional().describe("Restrict to content created at/before this ISO 8601 UTC timestamp."),
|
|
@@ -9103,7 +9137,7 @@ var inputSchema = {
|
|
|
9103
9137
|
var hybridSearch = ({ client, log }) => ({
|
|
9104
9138
|
name: "hybrid_search",
|
|
9105
9139
|
title: "Hybrid search",
|
|
9106
|
-
description:
|
|
9140
|
+
description: "Search your tenant's indexed content (documents + structured records) using hybrid BM25 + dense ranking. Returns up to `limit` results (default 3, max 50 \u2014 raise it in one call when you can accept the larger result) with citations and surrounding context for grounding follow-up reasoning. Narrow with contentTypes/typeName/filters, ownership (userId/orgId/clientId), folder scope, a created date window, keyword precision (textMode OR/AND/PHRASE), relevance floors (minSimilarity/minTextRelevance), uniqueDocuments, and requireComplete (fail closed on a degraded backend). Tenant-isolated; the caller's scoped key fully constrains which content is visible. ARCHIVED (soft-retracted) documents never appear in results \u2014 fetch them by id via document_get, or restore them via document_update. To stay within the agent context window each hit returns its matched chunkText + a short snippet by default; set includeContext:true for the broader surrounding passage. If the whole result would be too large it is truncated to the top hits (truncated:true). totalResults is the full matching pool and hasMore:true means results remain past this page \u2014 raise limit or advance offset to fetch them. textLegEmpty:true flags that the keyword leg matched nothing (all textScores 0) \u2014 usually a too-long PHRASE query; retry with textMode:\"OR\".",
|
|
9107
9141
|
inputSchema,
|
|
9108
9142
|
handler: async (args) => {
|
|
9109
9143
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT;
|
|
@@ -9123,6 +9157,7 @@ var hybridSearch = ({ client, log }) => ({
|
|
|
9123
9157
|
userId: args.userId,
|
|
9124
9158
|
orgId: args.orgId,
|
|
9125
9159
|
clientId: args.clientId,
|
|
9160
|
+
scope: args.scope,
|
|
9126
9161
|
createdAfter: args.createdAfter,
|
|
9127
9162
|
createdBefore: args.createdBefore,
|
|
9128
9163
|
minSimilarity: args.minSimilarity,
|
|
@@ -9149,6 +9184,12 @@ var hybridSearch = ({ client, log }) => ({
|
|
|
9149
9184
|
envelope.returnedResults = shaped.length;
|
|
9150
9185
|
text = JSON.stringify(envelope, null, 2);
|
|
9151
9186
|
}
|
|
9187
|
+
const offset = args.offset ?? 0;
|
|
9188
|
+
const totalResults = result.totalResults;
|
|
9189
|
+
if (typeof totalResults === "number" && offset + shaped.length < totalResults) {
|
|
9190
|
+
envelope.hasMore = true;
|
|
9191
|
+
text = JSON.stringify(envelope, null, 2);
|
|
9192
|
+
}
|
|
9152
9193
|
log.debug(
|
|
9153
9194
|
{ tool: "hybrid_search", mode, limit, returned: shaped.length, textLegEmpty },
|
|
9154
9195
|
"hybrid_search ok"
|
|
@@ -9184,7 +9225,7 @@ async function drainPages(fetchPage) {
|
|
|
9184
9225
|
|
|
9185
9226
|
// src/tools/record_query.ts
|
|
9186
9227
|
var MCP_DEFAULT_LIMIT2 = 3;
|
|
9187
|
-
var MCP_MAX_LIMIT2 =
|
|
9228
|
+
var MCP_MAX_LIMIT2 = 100;
|
|
9188
9229
|
var inputSchema2 = {
|
|
9189
9230
|
type: import_zod2.z.string().min(1, "type (record type) is required").describe('Record type / schema name (e.g. "patient", "clinical_note").'),
|
|
9190
9231
|
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
|
|
@@ -9202,14 +9243,18 @@ var inputSchema2 = {
|
|
|
9202
9243
|
userId: import_zod2.z.string().optional().describe("List mode: scope to records owned by this user."),
|
|
9203
9244
|
orgId: import_zod2.z.string().optional().describe("List mode: scope to records owned by this org."),
|
|
9204
9245
|
clientId: import_zod2.z.string().optional().describe("List mode: scope to records associated with this client."),
|
|
9246
|
+
scope: import_zod2.z.string().optional().describe(
|
|
9247
|
+
'List mode: filter to records carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9248
|
+
),
|
|
9249
|
+
startFrom: import_zod2.z.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
|
|
9205
9250
|
limit: import_zod2.z.number().int().min(1).max(MCP_MAX_LIMIT2).optional().describe(
|
|
9206
|
-
`Max records
|
|
9251
|
+
`Max records per page. Defaults to a low ${MCP_DEFAULT_LIMIT2} to protect the agent context window; raise up to ${MCP_MAX_LIMIT2} (the records API max) in one call when you can accept the larger payload, or page with \`startFrom\` instead.`
|
|
9207
9252
|
)
|
|
9208
9253
|
};
|
|
9209
9254
|
var recordQuery = ({ client, log }) => ({
|
|
9210
9255
|
name: "record_query",
|
|
9211
9256
|
title: "Record query (list or lookup)",
|
|
9212
|
-
description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields too.\n \u2022 List by type: omit `field`; optionally filter by `userId`/`orgId`/`clientId`.\nReturns up to
|
|
9257
|
+
description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields too.\n \u2022 List by type: omit `field`; optionally filter by `userId`/`orgId`/`clientId`.\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` records (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected from the arguments present.",
|
|
9213
9258
|
inputSchema: inputSchema2,
|
|
9214
9259
|
handler: async (args) => {
|
|
9215
9260
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT2;
|
|
@@ -9219,8 +9264,9 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9219
9264
|
const from = args.from;
|
|
9220
9265
|
const to = args.to;
|
|
9221
9266
|
const prefix = args.prefix;
|
|
9267
|
+
const startFrom = args.startFrom;
|
|
9222
9268
|
try {
|
|
9223
|
-
let
|
|
9269
|
+
let page;
|
|
9224
9270
|
if (field) {
|
|
9225
9271
|
const hasEquality = value !== void 0;
|
|
9226
9272
|
const hasRange = from !== void 0 || to !== void 0;
|
|
@@ -9243,7 +9289,7 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9243
9289
|
if (hasRange && !(from !== void 0 && to !== void 0)) {
|
|
9244
9290
|
return toolError("record_query", new Error("range lookup requires both 'from' and 'to'."));
|
|
9245
9291
|
}
|
|
9246
|
-
|
|
9292
|
+
page = await client.records.lookupRecordsByBody({
|
|
9247
9293
|
type,
|
|
9248
9294
|
field,
|
|
9249
9295
|
value,
|
|
@@ -9251,29 +9297,32 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9251
9297
|
to,
|
|
9252
9298
|
prefix,
|
|
9253
9299
|
order: args.order,
|
|
9300
|
+
startFrom,
|
|
9254
9301
|
limit
|
|
9255
9302
|
});
|
|
9256
|
-
records = pageItems(page);
|
|
9257
9303
|
log.debug(
|
|
9258
|
-
{ tool: "record_query", mode: "lookup", type, field, returned:
|
|
9304
|
+
{ tool: "record_query", mode: "lookup", type, field, returned: pageItems(page).length },
|
|
9259
9305
|
"record_query lookup ok"
|
|
9260
9306
|
);
|
|
9261
9307
|
} else {
|
|
9262
|
-
|
|
9308
|
+
page = await client.records.listRecords({
|
|
9263
9309
|
type,
|
|
9264
9310
|
userId: args.userId,
|
|
9265
9311
|
orgId: args.orgId,
|
|
9266
9312
|
clientId: args.clientId,
|
|
9313
|
+
scope: args.scope,
|
|
9314
|
+
startFrom,
|
|
9267
9315
|
limit
|
|
9268
9316
|
});
|
|
9269
|
-
records = pageItems(page);
|
|
9270
9317
|
log.debug(
|
|
9271
|
-
{ tool: "record_query", mode: "list", type, limit, returned:
|
|
9318
|
+
{ tool: "record_query", mode: "list", type, limit, returned: pageItems(page).length },
|
|
9272
9319
|
"record_query list ok"
|
|
9273
9320
|
);
|
|
9274
9321
|
}
|
|
9322
|
+
const records = pageItems(page);
|
|
9323
|
+
const nextCursor = page.nextCursor ?? null;
|
|
9275
9324
|
return {
|
|
9276
|
-
content: [{ type: "text", text: JSON.stringify(records, null, 2) }]
|
|
9325
|
+
content: [{ type: "text", text: JSON.stringify({ data: records, nextCursor }, null, 2) }]
|
|
9277
9326
|
};
|
|
9278
9327
|
} catch (err) {
|
|
9279
9328
|
log.warn({ tool: "record_query", err: String(err) }, "record_query failed");
|
|
@@ -9396,7 +9445,10 @@ var inputSchema4 = {
|
|
|
9396
9445
|
folderId: import_zod4.z.string().optional().describe("Group this record into a folder."),
|
|
9397
9446
|
userId: import_zod4.z.string().optional().describe("Owning user id."),
|
|
9398
9447
|
orgId: import_zod4.z.string().optional().describe("Owning organization id."),
|
|
9399
|
-
clientId: import_zod4.z.string().optional().describe("Associated client id.")
|
|
9448
|
+
clientId: import_zod4.z.string().optional().describe("Associated client id."),
|
|
9449
|
+
scopes: import_zod4.z.array(import_zod4.z.string()).optional().describe(
|
|
9450
|
+
'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). This is the record\'s COMPLETE scope declaration and values must come from the credential\'s own identity. An empty array `[]` creates a PRIVATE record owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields.'
|
|
9451
|
+
)
|
|
9400
9452
|
};
|
|
9401
9453
|
var recordCreate = ({ client, log }) => ({
|
|
9402
9454
|
name: "record_create",
|
|
@@ -9416,7 +9468,8 @@ var recordCreate = ({ client, log }) => ({
|
|
|
9416
9468
|
folderId: args.folderId,
|
|
9417
9469
|
userId: args.userId,
|
|
9418
9470
|
orgId: args.orgId,
|
|
9419
|
-
clientId: args.clientId
|
|
9471
|
+
clientId: args.clientId,
|
|
9472
|
+
scopes: args.scopes
|
|
9420
9473
|
}
|
|
9421
9474
|
});
|
|
9422
9475
|
log.debug({ tool: "record_create", type, id: created.id }, "record_create ok");
|
|
@@ -9501,24 +9554,29 @@ var record_delete_default = recordDelete;
|
|
|
9501
9554
|
// src/tools/rag_ask.ts
|
|
9502
9555
|
var import_zod7 = require("zod");
|
|
9503
9556
|
var SEARCH_MCP_DEFAULT_LIMIT = 5;
|
|
9504
|
-
var SEARCH_MCP_MAX_LIMIT =
|
|
9557
|
+
var SEARCH_MCP_MAX_LIMIT = 50;
|
|
9505
9558
|
var inputSchema7 = {
|
|
9506
9559
|
query: import_zod7.z.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
|
|
9507
9560
|
instructions: import_zod7.z.string().optional().describe(
|
|
9508
9561
|
'System prompt that overrides the default for the generation step \u2014 e.g. "answer only from the provided context, cite sources, be concise". Default: a generic answer-from-context instruction.'
|
|
9509
9562
|
),
|
|
9510
9563
|
model: import_zod7.z.string().optional().describe(
|
|
9511
|
-
"Inference model alias (e.g. claude-haiku-4-5, claude-sonnet-
|
|
9564
|
+
"Inference model alias (e.g. claude-haiku-4-5, claude-sonnet-5, claude-opus-4-8). Default = tier-appropriate Haiku. See GET /v1/models for the catalog the calling key can reach."
|
|
9512
9565
|
),
|
|
9513
9566
|
search: import_zod7.z.object({
|
|
9514
9567
|
mode: import_zod7.z.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
|
|
9515
|
-
limit: import_zod7.z.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
|
|
9568
|
+
limit: import_zod7.z.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
|
|
9569
|
+
`Passages to retrieve before generating (the grounding-corpus size). Default ${SEARCH_MCP_DEFAULT_LIMIT}; raise up to ${SEARCH_MCP_MAX_LIMIT} (the RAG search API max) for a broader corpus when the extra grounding is worth the larger prompt.`
|
|
9570
|
+
),
|
|
9516
9571
|
// Retrieval scoping — parity with hybrid_search so an agent can ground the
|
|
9517
9572
|
// answer on a specific owner / folder / type / metadata subset, not just the
|
|
9518
9573
|
// whole-tenant corpus.
|
|
9519
9574
|
userId: import_zod7.z.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
|
|
9520
9575
|
orgId: import_zod7.z.string().optional().describe("Restrict retrieval to content owned by this org (Vectros UUID)."),
|
|
9521
9576
|
clientId: import_zod7.z.string().optional().describe("Restrict retrieval to content tagged with this client (Vectros UUID)."),
|
|
9577
|
+
scope: import_zod7.z.string().optional().describe(
|
|
9578
|
+
'Restrict retrieval to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9579
|
+
),
|
|
9522
9580
|
folderId: import_zod7.z.string().optional().describe("Restrict retrieval to this exact folder."),
|
|
9523
9581
|
rootFolderId: import_zod7.z.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
|
|
9524
9582
|
typeName: import_zod7.z.string().optional().describe("Restrict record retrieval to this schema type."),
|
|
@@ -9536,7 +9594,7 @@ var inputSchema7 = {
|
|
|
9536
9594
|
var ragAsk = ({ client, log }) => ({
|
|
9537
9595
|
name: "rag_ask",
|
|
9538
9596
|
title: "RAG ask (corpus-wide)",
|
|
9539
|
-
description: "Ask a question grounded against
|
|
9597
|
+
description: "Ask a question grounded against your tenant's indexed content. Vectros performs a hybrid search, injects the top-K passages into the prompt, and streams a model answer back. Scope retrieval with `search` (ownership, folder, type, metadata filters, date window) to ground on a subset \u2014 e.g. one patient or one folder \u2014 and steer generation with `instructions` / `temperature`. The full answer is returned as a single response; progress notifications keep the call alive during the 30-45s generation window. Inference runs in-perimeter against AWS Bedrock \u2014 PHI never leaves the BAA boundary.",
|
|
9540
9598
|
inputSchema: inputSchema7,
|
|
9541
9599
|
handler: async (args, extra) => {
|
|
9542
9600
|
try {
|
|
@@ -9547,6 +9605,7 @@ var ragAsk = ({ client, log }) => ({
|
|
|
9547
9605
|
userId: searchArg.userId,
|
|
9548
9606
|
orgId: searchArg.orgId,
|
|
9549
9607
|
clientId: searchArg.clientId,
|
|
9608
|
+
scope: searchArg.scope,
|
|
9550
9609
|
folderId: searchArg.folderId,
|
|
9551
9610
|
rootFolderId: searchArg.rootFolderId,
|
|
9552
9611
|
typeName: searchArg.typeName,
|
|
@@ -9704,7 +9763,7 @@ var inputSchema9 = {
|
|
|
9704
9763
|
var listSchemas = ({ client, log }) => ({
|
|
9705
9764
|
name: "list_schemas",
|
|
9706
9765
|
title: "List record schemas",
|
|
9707
|
-
description: 'List the structured-record schema catalog for
|
|
9766
|
+
description: 'List the structured-record schema catalog for your tenant. Each schema describes a record type (e.g. "patient", "clinical_note") \u2014 its fields, lookup-indexed fields, and capabilities. Use this to discover what record types exist before calling `record_query`. Filter with `surface` (record/document/user/org/client \u2014 e.g. only document types), `recordType` (resolve one schema by its type name), or `userId`/`orgId`; default returns everything the credential can see.',
|
|
9708
9767
|
inputSchema: inputSchema9,
|
|
9709
9768
|
handler: async (args) => {
|
|
9710
9769
|
const userId = args.userId;
|
|
@@ -9758,7 +9817,7 @@ function isNotAvailable(err) {
|
|
|
9758
9817
|
var documentGet = ({ client, log }) => ({
|
|
9759
9818
|
name: "document_get",
|
|
9760
9819
|
title: "Get document metadata (and optionally text)",
|
|
9761
|
-
description: "Fetch metadata for a single document \u2014 select it by its Vectros `documentId` OR by your own `externalId` + `type` (resolved for you). Pass `includeText: true` to also fetch the text body (capped at ~8K tokens; `truncated: true` flag if cut).
|
|
9820
|
+
description: "Fetch metadata for a single document \u2014 select it by its Vectros `documentId` OR by your own `externalId` + `type` (resolved for you). Pass `includeText: true` to also fetch the text body (capped at ~8K tokens; `truncated: true` flag if cut). Text is available for every text-ingested document and for file-ingested documents unless they were uploaded with `storeText: false` (extracted text discarded after indexing) \u2014 then `textAvailable: false` is set; the original file is still retrievable via `includeDownloadUrl`. Pass `includeDownloadUrl: true` to also get a short-lived presigned `downloadUrl` for the original file (file-backed documents only). Returns the full document metadata, including its lifecycle `status` (ACTIVE, or ARCHIVED when soft-retracted from search \u2014 restorable via document_update) and its processing `indexStatus`.",
|
|
9762
9821
|
inputSchema: inputSchema10,
|
|
9763
9822
|
handler: async (args) => {
|
|
9764
9823
|
const documentId = args.documentId;
|
|
@@ -9782,7 +9841,7 @@ var documentGet = ({ client, log }) => ({
|
|
|
9782
9841
|
if (isNotAvailable(textErr)) {
|
|
9783
9842
|
log.debug(
|
|
9784
9843
|
{ tool: "document_get", id },
|
|
9785
|
-
"document_get text unavailable (
|
|
9844
|
+
"document_get text unavailable (not retained \u2014 storeText:false upload)"
|
|
9786
9845
|
);
|
|
9787
9846
|
extra = { ...extra, textAvailable: false };
|
|
9788
9847
|
} else {
|
|
@@ -9826,7 +9885,7 @@ var document_get_default = documentGet;
|
|
|
9826
9885
|
// src/tools/document_query.ts
|
|
9827
9886
|
var import_zod11 = require("zod");
|
|
9828
9887
|
var MCP_DEFAULT_LIMIT3 = 3;
|
|
9829
|
-
var MCP_MAX_LIMIT3 =
|
|
9888
|
+
var MCP_MAX_LIMIT3 = 100;
|
|
9830
9889
|
var inputSchema11 = {
|
|
9831
9890
|
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
|
|
9832
9891
|
field: import_zod11.z.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
|
|
@@ -9843,14 +9902,18 @@ var inputSchema11 = {
|
|
|
9843
9902
|
userId: import_zod11.z.string().optional().describe("List mode: scope to documents owned by this user."),
|
|
9844
9903
|
orgId: import_zod11.z.string().optional().describe("List mode: scope to documents belonging to this org."),
|
|
9845
9904
|
clientId: import_zod11.z.string().optional().describe("List mode: scope to documents associated with this client."),
|
|
9905
|
+
scope: import_zod11.z.string().optional().describe(
|
|
9906
|
+
'List mode: filter to documents carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9907
|
+
),
|
|
9908
|
+
startFrom: import_zod11.z.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
|
|
9846
9909
|
limit: import_zod11.z.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
|
|
9847
|
-
`Max documents
|
|
9910
|
+
`Max documents per page. Defaults to a low ${MCP_DEFAULT_LIMIT3} to protect the agent context window; raise up to ${MCP_MAX_LIMIT3} (the documents API max) in one call when you can accept the larger payload, or page with \`startFrom\` instead.`
|
|
9848
9911
|
)
|
|
9849
9912
|
};
|
|
9850
9913
|
var documentQuery = ({ client, log }) => ({
|
|
9851
9914
|
name: "document_query",
|
|
9852
9915
|
title: "Document query (list or lookup)",
|
|
9853
|
-
description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `folderId`/`userId`/`orgId`/`clientId`.\nReturns up to
|
|
9916
|
+
description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `folderId`/`userId`/`orgId`/`clientId`/`scope`.\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` documents (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected. Use document_get for the full text/download URL of one document.",
|
|
9854
9917
|
inputSchema: inputSchema11,
|
|
9855
9918
|
handler: async (args) => {
|
|
9856
9919
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT3;
|
|
@@ -9860,8 +9923,9 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9860
9923
|
const to = args.to;
|
|
9861
9924
|
const prefix = args.prefix;
|
|
9862
9925
|
const type = args.type;
|
|
9926
|
+
const startFrom = args.startFrom;
|
|
9863
9927
|
try {
|
|
9864
|
-
let
|
|
9928
|
+
let page;
|
|
9865
9929
|
if (field) {
|
|
9866
9930
|
const hasEquality = value !== void 0;
|
|
9867
9931
|
const hasRange = from !== void 0 || to !== void 0;
|
|
@@ -9890,7 +9954,7 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9890
9954
|
new Error("lookup requires 'type' (the document's bound schema type) alongside 'field'.")
|
|
9891
9955
|
);
|
|
9892
9956
|
}
|
|
9893
|
-
|
|
9957
|
+
page = await client.documents.lookupDocumentsByBody({
|
|
9894
9958
|
type,
|
|
9895
9959
|
field,
|
|
9896
9960
|
value,
|
|
@@ -9898,28 +9962,31 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9898
9962
|
to,
|
|
9899
9963
|
prefix,
|
|
9900
9964
|
order: args.order,
|
|
9965
|
+
startFrom,
|
|
9901
9966
|
limit
|
|
9902
9967
|
});
|
|
9903
|
-
documents = pageItems(page);
|
|
9904
9968
|
log.debug(
|
|
9905
|
-
{ tool: "document_query", mode: "lookup", type, field, returned:
|
|
9969
|
+
{ tool: "document_query", mode: "lookup", type, field, returned: pageItems(page).length },
|
|
9906
9970
|
"document_query lookup ok"
|
|
9907
9971
|
);
|
|
9908
9972
|
} else {
|
|
9909
|
-
|
|
9973
|
+
page = await client.documents.listDocuments({
|
|
9910
9974
|
folderId: args.folderId,
|
|
9911
9975
|
userId: args.userId,
|
|
9912
9976
|
orgId: args.orgId,
|
|
9913
9977
|
clientId: args.clientId,
|
|
9978
|
+
scope: args.scope,
|
|
9979
|
+
startFrom,
|
|
9914
9980
|
limit
|
|
9915
9981
|
});
|
|
9916
|
-
documents = pageItems(page);
|
|
9917
9982
|
log.debug(
|
|
9918
|
-
{ tool: "document_query", mode: "list", limit, returned:
|
|
9983
|
+
{ tool: "document_query", mode: "list", limit, returned: pageItems(page).length },
|
|
9919
9984
|
"document_query list ok"
|
|
9920
9985
|
);
|
|
9921
9986
|
}
|
|
9922
|
-
|
|
9987
|
+
const documents = pageItems(page);
|
|
9988
|
+
const nextCursor = page.nextCursor ?? null;
|
|
9989
|
+
return { content: [{ type: "text", text: JSON.stringify({ data: documents, nextCursor }, null, 2) }] };
|
|
9923
9990
|
} catch (err) {
|
|
9924
9991
|
log.warn({ tool: "document_query", err: String(err) }, "document_query failed");
|
|
9925
9992
|
return toolError("document_query", err);
|
|
@@ -9944,7 +10011,6 @@ var inputSchema12 = {
|
|
|
9944
10011
|
"New raw text body. Supplying it re-ingests the document body and re-queues it for indexing, so the search index reflects the edit (text-ingested documents only \u2014 not file-backed ones). Omit to leave the stored text unchanged. This is the in-place alternative to re-ingesting with upsert:true."
|
|
9945
10012
|
),
|
|
9946
10013
|
folderId: import_zod12.z.string().optional().describe("Move the document into this folder (cannot be cleared once set)."),
|
|
9947
|
-
storeText: import_zod12.z.boolean().optional().describe("Whether the raw text is stored + retrievable."),
|
|
9948
10014
|
status: import_zod12.z.enum(["ACTIVE", "ARCHIVED"]).optional().describe(
|
|
9949
10015
|
"Lifecycle status. `ARCHIVED` soft-retracts the document \u2014 it is pulled from search and recall but kept and recoverable; set it back to `ACTIVE` to re-index and restore it. The way to retire superseded content when the credential has no delete authority. Omit to leave unchanged."
|
|
9950
10016
|
),
|
|
@@ -9968,7 +10034,6 @@ var documentUpdate = ({ client, log }) => ({
|
|
|
9968
10034
|
const fields = args.fields;
|
|
9969
10035
|
const text = args.text;
|
|
9970
10036
|
const folderId = args.folderId;
|
|
9971
|
-
const storeText = args.storeText;
|
|
9972
10037
|
const status = args.status;
|
|
9973
10038
|
const userId = args.userId;
|
|
9974
10039
|
const orgId = args.orgId;
|
|
@@ -9982,7 +10047,6 @@ var documentUpdate = ({ client, log }) => ({
|
|
|
9982
10047
|
if (fields !== void 0) body.payload = fields;
|
|
9983
10048
|
if (text !== void 0) body.text = text;
|
|
9984
10049
|
if (folderId !== void 0) body.folderId = folderId;
|
|
9985
|
-
if (storeText !== void 0) body.storeText = storeText;
|
|
9986
10050
|
if (status !== void 0) body.status = status;
|
|
9987
10051
|
if (userId !== void 0) body.userId = userId;
|
|
9988
10052
|
if (orgId !== void 0) body.orgId = orgId;
|
|
@@ -10031,7 +10095,7 @@ var document_delete_default = documentDelete;
|
|
|
10031
10095
|
// src/tools/folder_query.ts
|
|
10032
10096
|
var import_zod14 = require("zod");
|
|
10033
10097
|
var MCP_DEFAULT_LIMIT4 = 10;
|
|
10034
|
-
var MCP_MAX_LIMIT4 =
|
|
10098
|
+
var MCP_MAX_LIMIT4 = 100;
|
|
10035
10099
|
var inputSchema14 = {
|
|
10036
10100
|
id: import_zod14.z.string().optional().describe("Get mode: fetch this single folder by id."),
|
|
10037
10101
|
parentId: import_zod14.z.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
|
|
@@ -10039,12 +10103,14 @@ var inputSchema14 = {
|
|
|
10039
10103
|
orgId: import_zod14.z.string().optional().describe("List mode: scope to folders belonging to this org."),
|
|
10040
10104
|
clientId: import_zod14.z.string().optional().describe("List mode: scope to folders associated with this client."),
|
|
10041
10105
|
startFrom: import_zod14.z.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
|
|
10042
|
-
limit: import_zod14.z.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
|
|
10106
|
+
limit: import_zod14.z.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
|
|
10107
|
+
`List mode: max folders per page. Default 10; raise up to ${MCP_MAX_LIMIT4} (the folders API max) in one call when you can accept the larger page, or page with \`startFrom\`.`
|
|
10108
|
+
)
|
|
10043
10109
|
};
|
|
10044
10110
|
var folderQuery = ({ client, log }) => ({
|
|
10045
10111
|
name: "folder_query",
|
|
10046
10112
|
title: "Folder query (list or get)",
|
|
10047
|
-
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
|
|
10113
|
+
description: "Read folders. Two modes:\n \u2022 Get: pass `id` \u2192 returns the single folder.\n \u2022 List: omit `id`; pass `parentId` for a folder's direct children (tree navigation) or omit for a flat tenant list. Optionally filter by `userId`/`orgId`/`clientId`.\nList mode returns `{ data, nextCursor }` (default 10, max 100 per page \u2014 raise it in one call when you can accept the larger page); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page through all folders. Get mode returns the single folder object.",
|
|
10048
10114
|
inputSchema: inputSchema14,
|
|
10049
10115
|
handler: async (args) => {
|
|
10050
10116
|
const id = args.id;
|
|
@@ -10084,7 +10150,10 @@ var inputSchema15 = {
|
|
|
10084
10150
|
slug: import_zod15.z.string().optional().describe("Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. Derived from name when omitted."),
|
|
10085
10151
|
userId: import_zod15.z.string().optional().describe("Owning user (Vectros UUID)."),
|
|
10086
10152
|
orgId: import_zod15.z.string().optional().describe("Owning org (Vectros UUID)."),
|
|
10087
|
-
clientId: import_zod15.z.string().optional().describe("Associated client (Vectros UUID).")
|
|
10153
|
+
clientId: import_zod15.z.string().optional().describe("Associated client (Vectros UUID)."),
|
|
10154
|
+
scopes: import_zod15.z.array(import_zod15.z.string()).optional().describe(
|
|
10155
|
+
'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). This is the folder\'s COMPLETE scope declaration and values must come from the credential\'s own identity. An empty array `[]` creates a PRIVATE folder owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields.'
|
|
10156
|
+
)
|
|
10088
10157
|
};
|
|
10089
10158
|
var folderCreate = ({ client, log }) => ({
|
|
10090
10159
|
name: "folder_create",
|
|
@@ -10101,7 +10170,8 @@ var folderCreate = ({ client, log }) => ({
|
|
|
10101
10170
|
slug: args.slug,
|
|
10102
10171
|
userId: args.userId,
|
|
10103
10172
|
orgId: args.orgId,
|
|
10104
|
-
clientId: args.clientId
|
|
10173
|
+
clientId: args.clientId,
|
|
10174
|
+
scopes: args.scopes
|
|
10105
10175
|
}
|
|
10106
10176
|
});
|
|
10107
10177
|
log.debug({ tool: "folder_create", id: folder.id, name: folder.name }, "folder_create ok");
|
|
@@ -10302,7 +10372,7 @@ var inputSchema19 = {
|
|
|
10302
10372
|
// Inline text mode (mutually exclusive with filePath):
|
|
10303
10373
|
text: import_zod18.z.string().min(1).optional().describe("Text body to ingest inline. Mutually exclusive with filePath. Either text or filePath is required."),
|
|
10304
10374
|
storeText: import_zod18.z.boolean().optional().describe(
|
|
10305
|
-
"
|
|
10375
|
+
"Whether the document's text is retained after indexing (default true). Applies to BOTH modes and is fixed at ingest time. Text mode: the ingested body is always retained (this flag is effectively always true \u2014 the body is the document). File mode: true keeps the extracted text retrievable via document_get(includeText:true) and usable by document_ask; false discards the extracted text once indexing completes \u2014 search still works and the original file remains downloadable, but includeText returns textAvailable:false and document_ask is unavailable."
|
|
10306
10376
|
),
|
|
10307
10377
|
// File upload mode (mutually exclusive with text; stdio-transport-only):
|
|
10308
10378
|
filePath: import_zod18.z.string().min(1).optional().describe(
|
|
@@ -10313,12 +10383,15 @@ var inputSchema19 = {
|
|
|
10313
10383
|
folderId: import_zod18.z.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
|
|
10314
10384
|
userId: import_zod18.z.string().optional().describe("Owning user ID. Optional."),
|
|
10315
10385
|
orgId: import_zod18.z.string().optional().describe("Owning org ID. Optional."),
|
|
10316
|
-
clientId: import_zod18.z.string().optional().describe("Owning client ID. Optional.")
|
|
10386
|
+
clientId: import_zod18.z.string().optional().describe("Owning client ID. Optional."),
|
|
10387
|
+
scopes: import_zod18.z.array(import_zod18.z.string()).optional().describe(
|
|
10388
|
+
"TEXT MODE ONLY. Scope ownership as `namespace:value` entries, at most 2 (e.g. [\"org:<uuid>\", \"group:eng-team\"]). This is the document's COMPLETE scope declaration and values must come from the credential's own identity. An empty array `[]` creates a PRIVATE document owned by the calling user alone. Omit to stamp the credential's full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields. Rejected with `filePath` (file uploads do not accept a scope declaration): a file-uploaded document is stamped with the credential's full identity."
|
|
10389
|
+
)
|
|
10317
10390
|
};
|
|
10318
10391
|
var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
10319
10392
|
name: "document_ingest",
|
|
10320
10393
|
title: "Ingest a new document (text body or file upload)",
|
|
10321
|
-
description: "Create a new document in your Vectros tenant. Two modes:\n \u2022 Text mode: pass `text` (string body). Single-call. Use for crawled content, notes, generated text.\n \u2022 File mode: pass `filePath` (local path on the MCP server's host machine). STDIO-TRANSPORT ONLY \u2014 rejected on HTTP transport. MCP server reads the bytes, requests a presigned upload URL, and PUTs them. Returns when the upload is accepted (indexStatus: PENDING_INDEX); poll with `document_get` until indexStatus is INDEXED.\nEither `text` or `filePath` must be present; both is an error. Idempotent by `externalId` (re-ingest returns the existing document, not a duplicate). To UPDATE an existing document instead \u2014 re-index an edited body \u2014 pass `upsert:true` with the same `externalId` (and the same `schemaId`); this is the re-sync primitive for keeping a knowledge base current. Pass `schemaId` + `payload` for a typed, lookup-queryable document (records parity). indexMode defaults to HYBRID for untyped documents (omit to inherit a bound schema's default). storeText defaults to true
|
|
10394
|
+
description: "Create a new document in your Vectros tenant. Two modes:\n \u2022 Text mode: pass `text` (string body). Single-call. Use for crawled content, notes, generated text.\n \u2022 File mode: pass `filePath` (local path on the MCP server's host machine). STDIO-TRANSPORT ONLY \u2014 rejected on HTTP transport. MCP server reads the bytes, requests a presigned upload URL, and PUTs them. Returns when the upload is accepted (indexStatus: PENDING_INDEX); poll with `document_get` until indexStatus is INDEXED.\nEither `text` or `filePath` must be present; both is an error. Idempotent by `externalId` (re-ingest returns the existing document, not a duplicate). To UPDATE an existing document instead \u2014 re-index an edited body \u2014 pass `upsert:true` with the same `externalId` (and the same `schemaId`); this is the re-sync primitive for keeping a knowledge base current. Pass `schemaId` + `payload` for a typed, lookup-queryable document (records parity). indexMode defaults to HYBRID for untyped documents (omit to inherit a bound schema's default). storeText defaults to true and is fixed at ingest: in file mode, false discards the extracted text after indexing (search + file download keep working; text retrieval and document_ask do not). Text-mode bodies are always retained.",
|
|
10322
10395
|
inputSchema: inputSchema19,
|
|
10323
10396
|
handler: async (args) => {
|
|
10324
10397
|
const title = args.title;
|
|
@@ -10347,6 +10420,14 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10347
10420
|
)
|
|
10348
10421
|
);
|
|
10349
10422
|
}
|
|
10423
|
+
if (filePath && args.scopes !== void 0) {
|
|
10424
|
+
return toolError(
|
|
10425
|
+
"document_ingest",
|
|
10426
|
+
new Error(
|
|
10427
|
+
"`scopes` is not supported with `filePath` \u2014 file uploads do not accept a scope declaration, and silently ignoring it could make an intended-private document team-visible. Use `text` mode to declare scopes, or omit `scopes` (the upload is stamped with the credential's full identity)."
|
|
10428
|
+
)
|
|
10429
|
+
);
|
|
10430
|
+
}
|
|
10350
10431
|
const common = {
|
|
10351
10432
|
folderId: args.folderId,
|
|
10352
10433
|
payload: args.payload,
|
|
@@ -10358,14 +10439,15 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10358
10439
|
};
|
|
10359
10440
|
try {
|
|
10360
10441
|
if (text) {
|
|
10361
|
-
const storeText = args.storeText ?? true;
|
|
10362
10442
|
const result = await client.documents.ingestDocument({
|
|
10363
10443
|
upsert,
|
|
10364
10444
|
body: {
|
|
10365
10445
|
title,
|
|
10366
10446
|
text,
|
|
10367
10447
|
indexMode,
|
|
10368
|
-
|
|
10448
|
+
// Scope ownership is a TEXT-mode contract only (the upload-init
|
|
10449
|
+
// endpoint has no scopes field — see the gate above).
|
|
10450
|
+
scopes: args.scopes,
|
|
10369
10451
|
...common
|
|
10370
10452
|
}
|
|
10371
10453
|
});
|
|
@@ -10396,11 +10478,13 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10396
10478
|
}
|
|
10397
10479
|
const fileName = (0, import_node_path.basename)(safePath);
|
|
10398
10480
|
const fileType = args.fileType ?? inferMimeType(safePath);
|
|
10481
|
+
const storeTextFile = args.storeText ?? true;
|
|
10399
10482
|
const upload = await client.documents.uploadDocument({
|
|
10400
10483
|
upsert,
|
|
10401
10484
|
fileName,
|
|
10402
10485
|
fileType,
|
|
10403
10486
|
indexMode,
|
|
10487
|
+
storeText: storeTextFile,
|
|
10404
10488
|
...common
|
|
10405
10489
|
});
|
|
10406
10490
|
const uploadUrl = upload?.uploadUrl;
|
|
@@ -10456,7 +10540,7 @@ var document_ingest_default = documentIngest;
|
|
|
10456
10540
|
// src/tools/lookup_principal.ts
|
|
10457
10541
|
var import_zod19 = require("zod");
|
|
10458
10542
|
var MCP_DEFAULT_LIMIT5 = 10;
|
|
10459
|
-
var MCP_MAX_LIMIT5 =
|
|
10543
|
+
var MCP_MAX_LIMIT5 = 100;
|
|
10460
10544
|
var inputSchema20 = {
|
|
10461
10545
|
kind: import_zod19.z.enum(["user", "org", "client"]).describe("Which principal to look up: a user, an organization, or a client."),
|
|
10462
10546
|
externalId: import_zod19.z.string().optional().describe(
|
|
@@ -10470,12 +10554,15 @@ var inputSchema20 = {
|
|
|
10470
10554
|
to: import_zod19.z.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
|
|
10471
10555
|
prefix: import_zod19.z.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
|
|
10472
10556
|
order: import_zod19.z.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
|
|
10473
|
-
|
|
10557
|
+
startFrom: import_zod19.z.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
|
|
10558
|
+
limit: import_zod19.z.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
|
|
10559
|
+
`Max principals per page. Defaults to ${MCP_DEFAULT_LIMIT5}; raise up to ${MCP_MAX_LIMIT5} (the principals API max) in one call when you can accept the payload, or page with \`startFrom\`.`
|
|
10560
|
+
)
|
|
10474
10561
|
};
|
|
10475
10562
|
var lookupPrincipal = ({ client, log }) => ({
|
|
10476
10563
|
name: "lookup_principal",
|
|
10477
10564
|
title: "Look up a user / org / client identity",
|
|
10478
|
-
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
|
|
10565
|
+
description: "Resolve a user, org, or client identity. Two modes:\n \u2022 Resolve by your own id: pass `externalId` \u2192 the matching principal incl. its Vectros UUID (the id the ownership filters on record_query / hybrid_search / rag_ask expect).\n \u2022 Lookup by a schema field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Sensitive-field-safe. (If both `externalId` and a `field` lookup are given, `externalId` wins.) Returns a `{ data, nextCursor }` page (default 10, max 100 \u2014 raise it in one call when you can accept the payload); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom`. Read-only \u2014 does not create or modify identities.",
|
|
10479
10566
|
inputSchema: inputSchema20,
|
|
10480
10567
|
handler: async (args) => {
|
|
10481
10568
|
const kind = args.kind;
|
|
@@ -10488,6 +10575,7 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10488
10575
|
const to = args.to;
|
|
10489
10576
|
const prefix = args.prefix;
|
|
10490
10577
|
const order = args.order;
|
|
10578
|
+
const startFrom = args.startFrom;
|
|
10491
10579
|
try {
|
|
10492
10580
|
let page;
|
|
10493
10581
|
if (externalId !== void 0) {
|
|
@@ -10525,7 +10613,7 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10525
10613
|
new Error("lookup requires 'type' (the identity schema's record type) alongside 'field'.")
|
|
10526
10614
|
);
|
|
10527
10615
|
}
|
|
10528
|
-
const req = { type, field, value, from, to, prefix, order, limit };
|
|
10616
|
+
const req = { type, field, value, from, to, prefix, order, startFrom, limit };
|
|
10529
10617
|
if (kind === "user") {
|
|
10530
10618
|
page = await client.identity.lookupUsers(req);
|
|
10531
10619
|
} else if (kind === "org") {
|
|
@@ -10541,7 +10629,8 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10541
10629
|
);
|
|
10542
10630
|
}
|
|
10543
10631
|
const principals = pageItems(page);
|
|
10544
|
-
|
|
10632
|
+
const nextCursor = page.nextCursor ?? null;
|
|
10633
|
+
return { content: [{ type: "text", text: JSON.stringify({ data: principals, nextCursor }, null, 2) }] };
|
|
10545
10634
|
} catch (err) {
|
|
10546
10635
|
log.warn({ tool: "lookup_principal", kind, err: String(err) }, "lookup_principal failed");
|
|
10547
10636
|
return toolError("lookup_principal", err);
|