@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/cli-http.js
CHANGED
|
@@ -675,6 +675,10 @@ var ReadAccessLogResponse;
|
|
|
675
675
|
})(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
|
|
676
676
|
var RecordRequest;
|
|
677
677
|
((RecordRequest2) => {
|
|
678
|
+
RecordRequest2.Status = {
|
|
679
|
+
Active: "ACTIVE",
|
|
680
|
+
Archived: "ARCHIVED"
|
|
681
|
+
};
|
|
678
682
|
RecordRequest2.IndexMode = {
|
|
679
683
|
Hybrid: "HYBRID",
|
|
680
684
|
Semantic: "SEMANTIC",
|
|
@@ -684,6 +688,10 @@ var RecordRequest;
|
|
|
684
688
|
})(RecordRequest || (RecordRequest = {}));
|
|
685
689
|
var RecordResponse;
|
|
686
690
|
((RecordResponse2) => {
|
|
691
|
+
RecordResponse2.Status = {
|
|
692
|
+
Active: "ACTIVE",
|
|
693
|
+
Archived: "ARCHIVED"
|
|
694
|
+
};
|
|
687
695
|
RecordResponse2.IndexStatus = {
|
|
688
696
|
PendingIndex: "PENDING_INDEX",
|
|
689
697
|
Indexed: "INDEXED",
|
|
@@ -3158,7 +3166,7 @@ var AuthClient = class {
|
|
|
3158
3166
|
* roleId: "engineering-member",
|
|
3159
3167
|
* name: "Engineering Team Member",
|
|
3160
3168
|
* scopes: [{
|
|
3161
|
-
* allowed_actions: ["
|
|
3169
|
+
* allowed_actions: ["records:cru", "search:r"]
|
|
3162
3170
|
* }]
|
|
3163
3171
|
* }
|
|
3164
3172
|
* })
|
|
@@ -3706,7 +3714,7 @@ var AuthClient = class {
|
|
|
3706
3714
|
* roleId: "engineering-member",
|
|
3707
3715
|
* name: "Engineering Team Member",
|
|
3708
3716
|
* scopes: [{
|
|
3709
|
-
* allowed_actions: ["
|
|
3717
|
+
* allowed_actions: ["records:cru", "search:r"]
|
|
3710
3718
|
* }]
|
|
3711
3719
|
* }
|
|
3712
3720
|
* })
|
|
@@ -4609,7 +4617,7 @@ var DocumentsClient = class {
|
|
|
4609
4617
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
4610
4618
|
}
|
|
4611
4619
|
/**
|
|
4612
|
-
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, or `
|
|
4620
|
+
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, `clientId`, or `scope`). The response is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom` to fetch the next page. Requires the `documents:r` scope.
|
|
4613
4621
|
*
|
|
4614
4622
|
* @param {Vectros.ListDocumentsRequest} request
|
|
4615
4623
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4619,6 +4627,7 @@ var DocumentsClient = class {
|
|
|
4619
4627
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
4620
4628
|
* orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
4621
4629
|
* clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
4630
|
+
* scope: "group:eng-team",
|
|
4622
4631
|
* folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
4623
4632
|
* startFrom: "doc_prev123"
|
|
4624
4633
|
* })
|
|
@@ -4627,11 +4636,12 @@ var DocumentsClient = class {
|
|
|
4627
4636
|
return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
|
|
4628
4637
|
}
|
|
4629
4638
|
async __listDocuments(request = {}, requestOptions) {
|
|
4630
|
-
const { userId, orgId, clientId, folderId, startFrom, limit } = request;
|
|
4639
|
+
const { userId, orgId, clientId, scope, folderId, startFrom, limit } = request;
|
|
4631
4640
|
const _queryParams = {
|
|
4632
4641
|
userId,
|
|
4633
4642
|
orgId,
|
|
4634
4643
|
clientId,
|
|
4644
|
+
scope,
|
|
4635
4645
|
folderId,
|
|
4636
4646
|
startFrom,
|
|
4637
4647
|
limit
|
|
@@ -4689,9 +4699,10 @@ var DocumentsClient = class {
|
|
|
4689
4699
|
return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
|
|
4690
4700
|
}
|
|
4691
4701
|
async __ingestDocument(request, requestOptions) {
|
|
4692
|
-
const { upsert, body: _body } = request;
|
|
4702
|
+
const { upsert, allowClear, body: _body } = request;
|
|
4693
4703
|
const _queryParams = {
|
|
4694
|
-
upsert
|
|
4704
|
+
upsert,
|
|
4705
|
+
allowClear
|
|
4695
4706
|
};
|
|
4696
4707
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4697
4708
|
const _headers = mergeHeaders(
|
|
@@ -4815,7 +4826,10 @@ var DocumentsClient = class {
|
|
|
4815
4826
|
return HttpResponsePromise.fromPromise(this.__updateDocument(request, requestOptions));
|
|
4816
4827
|
}
|
|
4817
4828
|
async __updateDocument(request, requestOptions) {
|
|
4818
|
-
const { id, body: _body } = request;
|
|
4829
|
+
const { id, allowClear, body: _body } = request;
|
|
4830
|
+
const _queryParams = {
|
|
4831
|
+
allowClear
|
|
4832
|
+
};
|
|
4819
4833
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4820
4834
|
const _headers = mergeHeaders(
|
|
4821
4835
|
_authRequest.headers,
|
|
@@ -4830,7 +4844,7 @@ var DocumentsClient = class {
|
|
|
4830
4844
|
method: "PUT",
|
|
4831
4845
|
headers: _headers,
|
|
4832
4846
|
contentType: "application/json",
|
|
4833
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4847
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4834
4848
|
requestType: "json",
|
|
4835
4849
|
body: _body,
|
|
4836
4850
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
@@ -4921,7 +4935,7 @@ var DocumentsClient = class {
|
|
|
4921
4935
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/documents/{id}");
|
|
4922
4936
|
}
|
|
4923
4937
|
/**
|
|
4924
|
-
* 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`, `
|
|
4938
|
+
* 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.
|
|
4925
4939
|
*
|
|
4926
4940
|
* @param {Vectros.PatchDocumentRequest} request
|
|
4927
4941
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -5173,7 +5187,7 @@ var DocumentsClient = class {
|
|
|
5173
5187
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/download");
|
|
5174
5188
|
}
|
|
5175
5189
|
/**
|
|
5176
|
-
* Returns the full
|
|
5190
|
+
* 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.
|
|
5177
5191
|
*
|
|
5178
5192
|
* @param {Vectros.GetDocumentTextRequest} request
|
|
5179
5193
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7566,7 +7580,7 @@ var RecordsClient = class {
|
|
|
7566
7580
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch");
|
|
7567
7581
|
}
|
|
7568
7582
|
/**
|
|
7569
|
-
* Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
|
|
7583
|
+
* Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`, `scope`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
|
|
7570
7584
|
*
|
|
7571
7585
|
* @param {Vectros.ListRecordsRequest} request
|
|
7572
7586
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7578,6 +7592,7 @@ var RecordsClient = class {
|
|
|
7578
7592
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
7579
7593
|
* orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
7580
7594
|
* clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
7595
|
+
* scope: "group:eng-team",
|
|
7581
7596
|
* startFrom: "550e8400-e29b-41d4-a716-446655440000"
|
|
7582
7597
|
* })
|
|
7583
7598
|
*/
|
|
@@ -7585,13 +7600,25 @@ var RecordsClient = class {
|
|
|
7585
7600
|
return HttpResponsePromise.fromPromise(this.__listRecords(request, requestOptions));
|
|
7586
7601
|
}
|
|
7587
7602
|
async __listRecords(request = {}, requestOptions) {
|
|
7588
|
-
const {
|
|
7603
|
+
const {
|
|
7604
|
+
type: type_,
|
|
7605
|
+
folderId,
|
|
7606
|
+
userId,
|
|
7607
|
+
orgId,
|
|
7608
|
+
clientId,
|
|
7609
|
+
scope,
|
|
7610
|
+
startFrom,
|
|
7611
|
+
limit,
|
|
7612
|
+
includePayload,
|
|
7613
|
+
recent
|
|
7614
|
+
} = request;
|
|
7589
7615
|
const _queryParams = {
|
|
7590
7616
|
type: type_,
|
|
7591
7617
|
folderId,
|
|
7592
7618
|
userId,
|
|
7593
7619
|
orgId,
|
|
7594
7620
|
clientId,
|
|
7621
|
+
scope,
|
|
7595
7622
|
startFrom,
|
|
7596
7623
|
limit,
|
|
7597
7624
|
includePayload,
|
|
@@ -7656,9 +7683,10 @@ var RecordsClient = class {
|
|
|
7656
7683
|
return HttpResponsePromise.fromPromise(this.__createRecord(request, requestOptions));
|
|
7657
7684
|
}
|
|
7658
7685
|
async __createRecord(request, requestOptions) {
|
|
7659
|
-
const { upsert, body: _body } = request;
|
|
7686
|
+
const { upsert, allowClear, body: _body } = request;
|
|
7660
7687
|
const _queryParams = {
|
|
7661
|
-
upsert
|
|
7688
|
+
upsert,
|
|
7689
|
+
allowClear
|
|
7662
7690
|
};
|
|
7663
7691
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7664
7692
|
const _headers = mergeHeaders(
|
|
@@ -7780,7 +7808,10 @@ var RecordsClient = class {
|
|
|
7780
7808
|
return HttpResponsePromise.fromPromise(this.__updateRecord(request, requestOptions));
|
|
7781
7809
|
}
|
|
7782
7810
|
async __updateRecord(request, requestOptions) {
|
|
7783
|
-
const { id, body: _body } = request;
|
|
7811
|
+
const { id, allowClear, body: _body } = request;
|
|
7812
|
+
const _queryParams = {
|
|
7813
|
+
allowClear
|
|
7814
|
+
};
|
|
7784
7815
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7785
7816
|
const _headers = mergeHeaders(
|
|
7786
7817
|
_authRequest.headers,
|
|
@@ -7795,7 +7826,7 @@ var RecordsClient = class {
|
|
|
7795
7826
|
method: "PUT",
|
|
7796
7827
|
headers: _headers,
|
|
7797
7828
|
contentType: "application/json",
|
|
7798
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
7829
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
7799
7830
|
requestType: "json",
|
|
7800
7831
|
body: _body,
|
|
7801
7832
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
@@ -8782,8 +8813,8 @@ function warnOnSuboptimalKey(key, log) {
|
|
|
8782
8813
|
|
|
8783
8814
|
// src/build-info.ts
|
|
8784
8815
|
var BUILD_INFO = {
|
|
8785
|
-
mcpServer: true ? "0.
|
|
8786
|
-
sdk: true ? "0.
|
|
8816
|
+
mcpServer: true ? "0.9.0" : "dev",
|
|
8817
|
+
sdk: true ? "0.34.0" : "dev"
|
|
8787
8818
|
};
|
|
8788
8819
|
|
|
8789
8820
|
// src/log.ts
|
|
@@ -9019,7 +9050,7 @@ ${detail}${pointer}`
|
|
|
9019
9050
|
|
|
9020
9051
|
// src/tools/hybrid_search.ts
|
|
9021
9052
|
var MCP_DEFAULT_LIMIT = 3;
|
|
9022
|
-
var MCP_MAX_LIMIT =
|
|
9053
|
+
var MCP_MAX_LIMIT = 50;
|
|
9023
9054
|
var MCP_RESPONSE_CHAR_BUDGET = 24e3;
|
|
9024
9055
|
var SNIPPET_MAX_CHARS = 160;
|
|
9025
9056
|
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["tenantId", "owner_id", "model_type", "rootFolderId", "folderId"]);
|
|
@@ -9053,9 +9084,9 @@ var inputSchema = {
|
|
|
9053
9084
|
"Keyword-match precision for the BM25 leg (TEXT/HYBRID modes). OR = any term (broad recall), AND = all terms (higher precision), PHRASE = contiguous phrase. Default: PHRASE (slop 3) in HYBRID, OR in TEXT. (The advanced raw-query COMPLEX mode is intentionally not exposed here.)"
|
|
9054
9085
|
),
|
|
9055
9086
|
limit: import_zod.z.number().int().min(1).max(MCP_MAX_LIMIT).optional().describe(
|
|
9056
|
-
`Max hits
|
|
9087
|
+
`Max hits per page. Defaults to a low ${MCP_DEFAULT_LIMIT} to protect the agent context window; raise up to ${MCP_MAX_LIMIT} (the most a single search returns) in one call when you can accept the larger result, or page with \`offset\` instead.`
|
|
9057
9088
|
),
|
|
9058
|
-
offset: import_zod.z.number().int().min(0).optional().describe("Skip the first N hits \u2014 for pagination."),
|
|
9089
|
+
offset: import_zod.z.number().int().min(0).max(200).optional().describe("Skip the first N hits \u2014 for pagination (0\u2013200)."),
|
|
9059
9090
|
folderId: import_zod.z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
|
|
9060
9091
|
rootFolderId: import_zod.z.string().optional().describe("Scope to content under this folder AND all its descendants (subtree-root folder id)."),
|
|
9061
9092
|
// Content narrowing.
|
|
@@ -9070,6 +9101,9 @@ var inputSchema = {
|
|
|
9070
9101
|
userId: import_zod.z.string().optional().describe("Restrict to content owned by this user (Vectros UUID)."),
|
|
9071
9102
|
orgId: import_zod.z.string().optional().describe("Restrict to content belonging to this org (Vectros UUID)."),
|
|
9072
9103
|
clientId: import_zod.z.string().optional().describe("Restrict to content associated with this client (Vectros UUID)."),
|
|
9104
|
+
scope: import_zod.z.string().optional().describe(
|
|
9105
|
+
'Restrict to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9106
|
+
),
|
|
9073
9107
|
// Date window.
|
|
9074
9108
|
createdAfter: import_zod.z.string().optional().describe("Restrict to content created at/after this ISO 8601 UTC timestamp."),
|
|
9075
9109
|
createdBefore: import_zod.z.string().optional().describe("Restrict to content created at/before this ISO 8601 UTC timestamp."),
|
|
@@ -9087,7 +9121,7 @@ var inputSchema = {
|
|
|
9087
9121
|
var hybridSearch = ({ client, log }) => ({
|
|
9088
9122
|
name: "hybrid_search",
|
|
9089
9123
|
title: "Hybrid search",
|
|
9090
|
-
description:
|
|
9124
|
+
description: "Search your tenant's indexed content (documents + structured records) using hybrid BM25 + dense ranking. Returns up to `limit` results (default 3, max 50 \u2014 raise it in one call when you can accept the larger result) with citations and surrounding context for grounding follow-up reasoning. Narrow with contentTypes/typeName/filters, ownership (userId/orgId/clientId), folder scope, a created date window, keyword precision (textMode OR/AND/PHRASE), relevance floors (minSimilarity/minTextRelevance), uniqueDocuments, and requireComplete (fail closed on a degraded backend). Tenant-isolated; the caller's scoped key fully constrains which content is visible. ARCHIVED (soft-retracted) documents never appear in results \u2014 fetch them by id via document_get, or restore them via document_update. To stay within the agent context window each hit returns its matched chunkText + a short snippet by default; set includeContext:true for the broader surrounding passage. If the whole result would be too large it is truncated to the top hits (truncated:true). totalResults is the full matching pool and hasMore:true means results remain past this page \u2014 raise limit or advance offset to fetch them. textLegEmpty:true flags that the keyword leg matched nothing (all textScores 0) \u2014 usually a too-long PHRASE query; retry with textMode:\"OR\".",
|
|
9091
9125
|
inputSchema,
|
|
9092
9126
|
handler: async (args) => {
|
|
9093
9127
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT;
|
|
@@ -9107,6 +9141,7 @@ var hybridSearch = ({ client, log }) => ({
|
|
|
9107
9141
|
userId: args.userId,
|
|
9108
9142
|
orgId: args.orgId,
|
|
9109
9143
|
clientId: args.clientId,
|
|
9144
|
+
scope: args.scope,
|
|
9110
9145
|
createdAfter: args.createdAfter,
|
|
9111
9146
|
createdBefore: args.createdBefore,
|
|
9112
9147
|
minSimilarity: args.minSimilarity,
|
|
@@ -9133,6 +9168,12 @@ var hybridSearch = ({ client, log }) => ({
|
|
|
9133
9168
|
envelope.returnedResults = shaped.length;
|
|
9134
9169
|
text = JSON.stringify(envelope, null, 2);
|
|
9135
9170
|
}
|
|
9171
|
+
const offset = args.offset ?? 0;
|
|
9172
|
+
const totalResults = result.totalResults;
|
|
9173
|
+
if (typeof totalResults === "number" && offset + shaped.length < totalResults) {
|
|
9174
|
+
envelope.hasMore = true;
|
|
9175
|
+
text = JSON.stringify(envelope, null, 2);
|
|
9176
|
+
}
|
|
9136
9177
|
log.debug(
|
|
9137
9178
|
{ tool: "hybrid_search", mode, limit, returned: shaped.length, textLegEmpty },
|
|
9138
9179
|
"hybrid_search ok"
|
|
@@ -9168,7 +9209,7 @@ async function drainPages(fetchPage) {
|
|
|
9168
9209
|
|
|
9169
9210
|
// src/tools/record_query.ts
|
|
9170
9211
|
var MCP_DEFAULT_LIMIT2 = 3;
|
|
9171
|
-
var MCP_MAX_LIMIT2 =
|
|
9212
|
+
var MCP_MAX_LIMIT2 = 100;
|
|
9172
9213
|
var inputSchema2 = {
|
|
9173
9214
|
type: import_zod2.z.string().min(1, "type (record type) is required").describe('Record type / schema name (e.g. "patient", "clinical_note").'),
|
|
9174
9215
|
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
|
|
@@ -9186,14 +9227,18 @@ var inputSchema2 = {
|
|
|
9186
9227
|
userId: import_zod2.z.string().optional().describe("List mode: scope to records owned by this user."),
|
|
9187
9228
|
orgId: import_zod2.z.string().optional().describe("List mode: scope to records owned by this org."),
|
|
9188
9229
|
clientId: import_zod2.z.string().optional().describe("List mode: scope to records associated with this client."),
|
|
9230
|
+
scope: import_zod2.z.string().optional().describe(
|
|
9231
|
+
'List mode: filter to records carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9232
|
+
),
|
|
9233
|
+
startFrom: import_zod2.z.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
|
|
9189
9234
|
limit: import_zod2.z.number().int().min(1).max(MCP_MAX_LIMIT2).optional().describe(
|
|
9190
|
-
`Max records
|
|
9235
|
+
`Max records per page. Defaults to a low ${MCP_DEFAULT_LIMIT2} to protect the agent context window; raise up to ${MCP_MAX_LIMIT2} (the records API max) in one call when you can accept the larger payload, or page with \`startFrom\` instead.`
|
|
9191
9236
|
)
|
|
9192
9237
|
};
|
|
9193
9238
|
var recordQuery = ({ client, log }) => ({
|
|
9194
9239
|
name: "record_query",
|
|
9195
9240
|
title: "Record query (list or lookup)",
|
|
9196
|
-
description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields too.\n \u2022 List by type: omit `field`; optionally filter by `userId`/`orgId`/`clientId`.\nReturns up to
|
|
9241
|
+
description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields too.\n \u2022 List by type: omit `field`; optionally filter by `userId`/`orgId`/`clientId`.\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` records (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected from the arguments present.",
|
|
9197
9242
|
inputSchema: inputSchema2,
|
|
9198
9243
|
handler: async (args) => {
|
|
9199
9244
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT2;
|
|
@@ -9203,8 +9248,9 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9203
9248
|
const from = args.from;
|
|
9204
9249
|
const to = args.to;
|
|
9205
9250
|
const prefix = args.prefix;
|
|
9251
|
+
const startFrom = args.startFrom;
|
|
9206
9252
|
try {
|
|
9207
|
-
let
|
|
9253
|
+
let page;
|
|
9208
9254
|
if (field) {
|
|
9209
9255
|
const hasEquality = value !== void 0;
|
|
9210
9256
|
const hasRange = from !== void 0 || to !== void 0;
|
|
@@ -9227,7 +9273,7 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9227
9273
|
if (hasRange && !(from !== void 0 && to !== void 0)) {
|
|
9228
9274
|
return toolError("record_query", new Error("range lookup requires both 'from' and 'to'."));
|
|
9229
9275
|
}
|
|
9230
|
-
|
|
9276
|
+
page = await client.records.lookupRecordsByBody({
|
|
9231
9277
|
type,
|
|
9232
9278
|
field,
|
|
9233
9279
|
value,
|
|
@@ -9235,29 +9281,32 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9235
9281
|
to,
|
|
9236
9282
|
prefix,
|
|
9237
9283
|
order: args.order,
|
|
9284
|
+
startFrom,
|
|
9238
9285
|
limit
|
|
9239
9286
|
});
|
|
9240
|
-
records = pageItems(page);
|
|
9241
9287
|
log.debug(
|
|
9242
|
-
{ tool: "record_query", mode: "lookup", type, field, returned:
|
|
9288
|
+
{ tool: "record_query", mode: "lookup", type, field, returned: pageItems(page).length },
|
|
9243
9289
|
"record_query lookup ok"
|
|
9244
9290
|
);
|
|
9245
9291
|
} else {
|
|
9246
|
-
|
|
9292
|
+
page = await client.records.listRecords({
|
|
9247
9293
|
type,
|
|
9248
9294
|
userId: args.userId,
|
|
9249
9295
|
orgId: args.orgId,
|
|
9250
9296
|
clientId: args.clientId,
|
|
9297
|
+
scope: args.scope,
|
|
9298
|
+
startFrom,
|
|
9251
9299
|
limit
|
|
9252
9300
|
});
|
|
9253
|
-
records = pageItems(page);
|
|
9254
9301
|
log.debug(
|
|
9255
|
-
{ tool: "record_query", mode: "list", type, limit, returned:
|
|
9302
|
+
{ tool: "record_query", mode: "list", type, limit, returned: pageItems(page).length },
|
|
9256
9303
|
"record_query list ok"
|
|
9257
9304
|
);
|
|
9258
9305
|
}
|
|
9306
|
+
const records = pageItems(page);
|
|
9307
|
+
const nextCursor = page.nextCursor ?? null;
|
|
9259
9308
|
return {
|
|
9260
|
-
content: [{ type: "text", text: JSON.stringify(records, null, 2) }]
|
|
9309
|
+
content: [{ type: "text", text: JSON.stringify({ data: records, nextCursor }, null, 2) }]
|
|
9261
9310
|
};
|
|
9262
9311
|
} catch (err) {
|
|
9263
9312
|
log.warn({ tool: "record_query", err: String(err) }, "record_query failed");
|
|
@@ -9380,7 +9429,10 @@ var inputSchema4 = {
|
|
|
9380
9429
|
folderId: import_zod4.z.string().optional().describe("Group this record into a folder."),
|
|
9381
9430
|
userId: import_zod4.z.string().optional().describe("Owning user id."),
|
|
9382
9431
|
orgId: import_zod4.z.string().optional().describe("Owning organization id."),
|
|
9383
|
-
clientId: import_zod4.z.string().optional().describe("Associated client id.")
|
|
9432
|
+
clientId: import_zod4.z.string().optional().describe("Associated client id."),
|
|
9433
|
+
scopes: import_zod4.z.array(import_zod4.z.string()).optional().describe(
|
|
9434
|
+
'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). This is the record\'s COMPLETE scope declaration and values must come from the credential\'s own identity. An empty array `[]` creates a PRIVATE record owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields.'
|
|
9435
|
+
)
|
|
9384
9436
|
};
|
|
9385
9437
|
var recordCreate = ({ client, log }) => ({
|
|
9386
9438
|
name: "record_create",
|
|
@@ -9400,7 +9452,8 @@ var recordCreate = ({ client, log }) => ({
|
|
|
9400
9452
|
folderId: args.folderId,
|
|
9401
9453
|
userId: args.userId,
|
|
9402
9454
|
orgId: args.orgId,
|
|
9403
|
-
clientId: args.clientId
|
|
9455
|
+
clientId: args.clientId,
|
|
9456
|
+
scopes: args.scopes
|
|
9404
9457
|
}
|
|
9405
9458
|
});
|
|
9406
9459
|
log.debug({ tool: "record_create", type, id: created.id }, "record_create ok");
|
|
@@ -9485,24 +9538,29 @@ var record_delete_default = recordDelete;
|
|
|
9485
9538
|
// src/tools/rag_ask.ts
|
|
9486
9539
|
var import_zod7 = require("zod");
|
|
9487
9540
|
var SEARCH_MCP_DEFAULT_LIMIT = 5;
|
|
9488
|
-
var SEARCH_MCP_MAX_LIMIT =
|
|
9541
|
+
var SEARCH_MCP_MAX_LIMIT = 50;
|
|
9489
9542
|
var inputSchema7 = {
|
|
9490
9543
|
query: import_zod7.z.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
|
|
9491
9544
|
instructions: import_zod7.z.string().optional().describe(
|
|
9492
9545
|
'System prompt that overrides the default for the generation step \u2014 e.g. "answer only from the provided context, cite sources, be concise". Default: a generic answer-from-context instruction.'
|
|
9493
9546
|
),
|
|
9494
9547
|
model: import_zod7.z.string().optional().describe(
|
|
9495
|
-
"Inference model alias (e.g. claude-haiku-4-5, claude-sonnet-
|
|
9548
|
+
"Inference model alias (e.g. claude-haiku-4-5, claude-sonnet-5, claude-opus-4-8). Default = tier-appropriate Haiku. See GET /v1/models for the catalog the calling key can reach."
|
|
9496
9549
|
),
|
|
9497
9550
|
search: import_zod7.z.object({
|
|
9498
9551
|
mode: import_zod7.z.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
|
|
9499
|
-
limit: import_zod7.z.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
|
|
9552
|
+
limit: import_zod7.z.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
|
|
9553
|
+
`Passages to retrieve before generating (the grounding-corpus size). Default ${SEARCH_MCP_DEFAULT_LIMIT}; raise up to ${SEARCH_MCP_MAX_LIMIT} (the RAG search API max) for a broader corpus when the extra grounding is worth the larger prompt.`
|
|
9554
|
+
),
|
|
9500
9555
|
// Retrieval scoping — parity with hybrid_search so an agent can ground the
|
|
9501
9556
|
// answer on a specific owner / folder / type / metadata subset, not just the
|
|
9502
9557
|
// whole-tenant corpus.
|
|
9503
9558
|
userId: import_zod7.z.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
|
|
9504
9559
|
orgId: import_zod7.z.string().optional().describe("Restrict retrieval to content owned by this org (Vectros UUID)."),
|
|
9505
9560
|
clientId: import_zod7.z.string().optional().describe("Restrict retrieval to content tagged with this client (Vectros UUID)."),
|
|
9561
|
+
scope: import_zod7.z.string().optional().describe(
|
|
9562
|
+
'Restrict retrieval to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9563
|
+
),
|
|
9506
9564
|
folderId: import_zod7.z.string().optional().describe("Restrict retrieval to this exact folder."),
|
|
9507
9565
|
rootFolderId: import_zod7.z.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
|
|
9508
9566
|
typeName: import_zod7.z.string().optional().describe("Restrict record retrieval to this schema type."),
|
|
@@ -9520,7 +9578,7 @@ var inputSchema7 = {
|
|
|
9520
9578
|
var ragAsk = ({ client, log }) => ({
|
|
9521
9579
|
name: "rag_ask",
|
|
9522
9580
|
title: "RAG ask (corpus-wide)",
|
|
9523
|
-
description: "Ask a question grounded against
|
|
9581
|
+
description: "Ask a question grounded against your tenant's indexed content. Vectros performs a hybrid search, injects the top-K passages into the prompt, and streams a model answer back. Scope retrieval with `search` (ownership, folder, type, metadata filters, date window) to ground on a subset \u2014 e.g. one patient or one folder \u2014 and steer generation with `instructions` / `temperature`. The full answer is returned as a single response; progress notifications keep the call alive during the 30-45s generation window. Inference runs in-perimeter against AWS Bedrock \u2014 PHI never leaves the BAA boundary.",
|
|
9524
9582
|
inputSchema: inputSchema7,
|
|
9525
9583
|
handler: async (args, extra) => {
|
|
9526
9584
|
try {
|
|
@@ -9531,6 +9589,7 @@ var ragAsk = ({ client, log }) => ({
|
|
|
9531
9589
|
userId: searchArg.userId,
|
|
9532
9590
|
orgId: searchArg.orgId,
|
|
9533
9591
|
clientId: searchArg.clientId,
|
|
9592
|
+
scope: searchArg.scope,
|
|
9534
9593
|
folderId: searchArg.folderId,
|
|
9535
9594
|
rootFolderId: searchArg.rootFolderId,
|
|
9536
9595
|
typeName: searchArg.typeName,
|
|
@@ -9688,7 +9747,7 @@ var inputSchema9 = {
|
|
|
9688
9747
|
var listSchemas = ({ client, log }) => ({
|
|
9689
9748
|
name: "list_schemas",
|
|
9690
9749
|
title: "List record schemas",
|
|
9691
|
-
description: 'List the structured-record schema catalog for
|
|
9750
|
+
description: 'List the structured-record schema catalog for your tenant. Each schema describes a record type (e.g. "patient", "clinical_note") \u2014 its fields, lookup-indexed fields, and capabilities. Use this to discover what record types exist before calling `record_query`. Filter with `surface` (record/document/user/org/client \u2014 e.g. only document types), `recordType` (resolve one schema by its type name), or `userId`/`orgId`; default returns everything the credential can see.',
|
|
9692
9751
|
inputSchema: inputSchema9,
|
|
9693
9752
|
handler: async (args) => {
|
|
9694
9753
|
const userId = args.userId;
|
|
@@ -9742,7 +9801,7 @@ function isNotAvailable(err) {
|
|
|
9742
9801
|
var documentGet = ({ client, log }) => ({
|
|
9743
9802
|
name: "document_get",
|
|
9744
9803
|
title: "Get document metadata (and optionally text)",
|
|
9745
|
-
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).
|
|
9804
|
+
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`.",
|
|
9746
9805
|
inputSchema: inputSchema10,
|
|
9747
9806
|
handler: async (args) => {
|
|
9748
9807
|
const documentId = args.documentId;
|
|
@@ -9766,7 +9825,7 @@ var documentGet = ({ client, log }) => ({
|
|
|
9766
9825
|
if (isNotAvailable(textErr)) {
|
|
9767
9826
|
log.debug(
|
|
9768
9827
|
{ tool: "document_get", id },
|
|
9769
|
-
"document_get text unavailable (
|
|
9828
|
+
"document_get text unavailable (not retained \u2014 storeText:false upload)"
|
|
9770
9829
|
);
|
|
9771
9830
|
extra = { ...extra, textAvailable: false };
|
|
9772
9831
|
} else {
|
|
@@ -9810,7 +9869,7 @@ var document_get_default = documentGet;
|
|
|
9810
9869
|
// src/tools/document_query.ts
|
|
9811
9870
|
var import_zod11 = require("zod");
|
|
9812
9871
|
var MCP_DEFAULT_LIMIT3 = 3;
|
|
9813
|
-
var MCP_MAX_LIMIT3 =
|
|
9872
|
+
var MCP_MAX_LIMIT3 = 100;
|
|
9814
9873
|
var inputSchema11 = {
|
|
9815
9874
|
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
|
|
9816
9875
|
field: import_zod11.z.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
|
|
@@ -9827,14 +9886,18 @@ var inputSchema11 = {
|
|
|
9827
9886
|
userId: import_zod11.z.string().optional().describe("List mode: scope to documents owned by this user."),
|
|
9828
9887
|
orgId: import_zod11.z.string().optional().describe("List mode: scope to documents belonging to this org."),
|
|
9829
9888
|
clientId: import_zod11.z.string().optional().describe("List mode: scope to documents associated with this client."),
|
|
9889
|
+
scope: import_zod11.z.string().optional().describe(
|
|
9890
|
+
'List mode: filter to documents carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9891
|
+
),
|
|
9892
|
+
startFrom: import_zod11.z.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
|
|
9830
9893
|
limit: import_zod11.z.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
|
|
9831
|
-
`Max documents
|
|
9894
|
+
`Max documents per page. Defaults to a low ${MCP_DEFAULT_LIMIT3} to protect the agent context window; raise up to ${MCP_MAX_LIMIT3} (the documents API max) in one call when you can accept the larger payload, or page with \`startFrom\` instead.`
|
|
9832
9895
|
)
|
|
9833
9896
|
};
|
|
9834
9897
|
var documentQuery = ({ client, log }) => ({
|
|
9835
9898
|
name: "document_query",
|
|
9836
9899
|
title: "Document query (list or lookup)",
|
|
9837
|
-
description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `folderId`/`userId`/`orgId`/`clientId`.\nReturns up to
|
|
9900
|
+
description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `folderId`/`userId`/`orgId`/`clientId`/`scope`.\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` documents (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected. Use document_get for the full text/download URL of one document.",
|
|
9838
9901
|
inputSchema: inputSchema11,
|
|
9839
9902
|
handler: async (args) => {
|
|
9840
9903
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT3;
|
|
@@ -9844,8 +9907,9 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9844
9907
|
const to = args.to;
|
|
9845
9908
|
const prefix = args.prefix;
|
|
9846
9909
|
const type = args.type;
|
|
9910
|
+
const startFrom = args.startFrom;
|
|
9847
9911
|
try {
|
|
9848
|
-
let
|
|
9912
|
+
let page;
|
|
9849
9913
|
if (field) {
|
|
9850
9914
|
const hasEquality = value !== void 0;
|
|
9851
9915
|
const hasRange = from !== void 0 || to !== void 0;
|
|
@@ -9874,7 +9938,7 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9874
9938
|
new Error("lookup requires 'type' (the document's bound schema type) alongside 'field'.")
|
|
9875
9939
|
);
|
|
9876
9940
|
}
|
|
9877
|
-
|
|
9941
|
+
page = await client.documents.lookupDocumentsByBody({
|
|
9878
9942
|
type,
|
|
9879
9943
|
field,
|
|
9880
9944
|
value,
|
|
@@ -9882,28 +9946,31 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9882
9946
|
to,
|
|
9883
9947
|
prefix,
|
|
9884
9948
|
order: args.order,
|
|
9949
|
+
startFrom,
|
|
9885
9950
|
limit
|
|
9886
9951
|
});
|
|
9887
|
-
documents = pageItems(page);
|
|
9888
9952
|
log.debug(
|
|
9889
|
-
{ tool: "document_query", mode: "lookup", type, field, returned:
|
|
9953
|
+
{ tool: "document_query", mode: "lookup", type, field, returned: pageItems(page).length },
|
|
9890
9954
|
"document_query lookup ok"
|
|
9891
9955
|
);
|
|
9892
9956
|
} else {
|
|
9893
|
-
|
|
9957
|
+
page = await client.documents.listDocuments({
|
|
9894
9958
|
folderId: args.folderId,
|
|
9895
9959
|
userId: args.userId,
|
|
9896
9960
|
orgId: args.orgId,
|
|
9897
9961
|
clientId: args.clientId,
|
|
9962
|
+
scope: args.scope,
|
|
9963
|
+
startFrom,
|
|
9898
9964
|
limit
|
|
9899
9965
|
});
|
|
9900
|
-
documents = pageItems(page);
|
|
9901
9966
|
log.debug(
|
|
9902
|
-
{ tool: "document_query", mode: "list", limit, returned:
|
|
9967
|
+
{ tool: "document_query", mode: "list", limit, returned: pageItems(page).length },
|
|
9903
9968
|
"document_query list ok"
|
|
9904
9969
|
);
|
|
9905
9970
|
}
|
|
9906
|
-
|
|
9971
|
+
const documents = pageItems(page);
|
|
9972
|
+
const nextCursor = page.nextCursor ?? null;
|
|
9973
|
+
return { content: [{ type: "text", text: JSON.stringify({ data: documents, nextCursor }, null, 2) }] };
|
|
9907
9974
|
} catch (err) {
|
|
9908
9975
|
log.warn({ tool: "document_query", err: String(err) }, "document_query failed");
|
|
9909
9976
|
return toolError("document_query", err);
|
|
@@ -9928,7 +9995,6 @@ var inputSchema12 = {
|
|
|
9928
9995
|
"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."
|
|
9929
9996
|
),
|
|
9930
9997
|
folderId: import_zod12.z.string().optional().describe("Move the document into this folder (cannot be cleared once set)."),
|
|
9931
|
-
storeText: import_zod12.z.boolean().optional().describe("Whether the raw text is stored + retrievable."),
|
|
9932
9998
|
status: import_zod12.z.enum(["ACTIVE", "ARCHIVED"]).optional().describe(
|
|
9933
9999
|
"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."
|
|
9934
10000
|
),
|
|
@@ -9952,7 +10018,6 @@ var documentUpdate = ({ client, log }) => ({
|
|
|
9952
10018
|
const fields = args.fields;
|
|
9953
10019
|
const text = args.text;
|
|
9954
10020
|
const folderId = args.folderId;
|
|
9955
|
-
const storeText = args.storeText;
|
|
9956
10021
|
const status = args.status;
|
|
9957
10022
|
const userId = args.userId;
|
|
9958
10023
|
const orgId = args.orgId;
|
|
@@ -9966,7 +10031,6 @@ var documentUpdate = ({ client, log }) => ({
|
|
|
9966
10031
|
if (fields !== void 0) body.payload = fields;
|
|
9967
10032
|
if (text !== void 0) body.text = text;
|
|
9968
10033
|
if (folderId !== void 0) body.folderId = folderId;
|
|
9969
|
-
if (storeText !== void 0) body.storeText = storeText;
|
|
9970
10034
|
if (status !== void 0) body.status = status;
|
|
9971
10035
|
if (userId !== void 0) body.userId = userId;
|
|
9972
10036
|
if (orgId !== void 0) body.orgId = orgId;
|
|
@@ -10015,7 +10079,7 @@ var document_delete_default = documentDelete;
|
|
|
10015
10079
|
// src/tools/folder_query.ts
|
|
10016
10080
|
var import_zod14 = require("zod");
|
|
10017
10081
|
var MCP_DEFAULT_LIMIT4 = 10;
|
|
10018
|
-
var MCP_MAX_LIMIT4 =
|
|
10082
|
+
var MCP_MAX_LIMIT4 = 100;
|
|
10019
10083
|
var inputSchema14 = {
|
|
10020
10084
|
id: import_zod14.z.string().optional().describe("Get mode: fetch this single folder by id."),
|
|
10021
10085
|
parentId: import_zod14.z.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
|
|
@@ -10023,12 +10087,14 @@ var inputSchema14 = {
|
|
|
10023
10087
|
orgId: import_zod14.z.string().optional().describe("List mode: scope to folders belonging to this org."),
|
|
10024
10088
|
clientId: import_zod14.z.string().optional().describe("List mode: scope to folders associated with this client."),
|
|
10025
10089
|
startFrom: import_zod14.z.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
|
|
10026
|
-
limit: import_zod14.z.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
|
|
10090
|
+
limit: import_zod14.z.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
|
|
10091
|
+
`List mode: max folders per page. Default 10; raise up to ${MCP_MAX_LIMIT4} (the folders API max) in one call when you can accept the larger page, or page with \`startFrom\`.`
|
|
10092
|
+
)
|
|
10027
10093
|
};
|
|
10028
10094
|
var folderQuery = ({ client, log }) => ({
|
|
10029
10095
|
name: "folder_query",
|
|
10030
10096
|
title: "Folder query (list or get)",
|
|
10031
|
-
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
|
|
10097
|
+
description: "Read folders. Two modes:\n \u2022 Get: pass `id` \u2192 returns the single folder.\n \u2022 List: omit `id`; pass `parentId` for a folder's direct children (tree navigation) or omit for a flat tenant list. Optionally filter by `userId`/`orgId`/`clientId`.\nList mode returns `{ data, nextCursor }` (default 10, max 100 per page \u2014 raise it in one call when you can accept the larger page); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page through all folders. Get mode returns the single folder object.",
|
|
10032
10098
|
inputSchema: inputSchema14,
|
|
10033
10099
|
handler: async (args) => {
|
|
10034
10100
|
const id = args.id;
|
|
@@ -10068,7 +10134,10 @@ var inputSchema15 = {
|
|
|
10068
10134
|
slug: import_zod15.z.string().optional().describe("Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. Derived from name when omitted."),
|
|
10069
10135
|
userId: import_zod15.z.string().optional().describe("Owning user (Vectros UUID)."),
|
|
10070
10136
|
orgId: import_zod15.z.string().optional().describe("Owning org (Vectros UUID)."),
|
|
10071
|
-
clientId: import_zod15.z.string().optional().describe("Associated client (Vectros UUID).")
|
|
10137
|
+
clientId: import_zod15.z.string().optional().describe("Associated client (Vectros UUID)."),
|
|
10138
|
+
scopes: import_zod15.z.array(import_zod15.z.string()).optional().describe(
|
|
10139
|
+
'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). This is the folder\'s COMPLETE scope declaration and values must come from the credential\'s own identity. An empty array `[]` creates a PRIVATE folder owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields.'
|
|
10140
|
+
)
|
|
10072
10141
|
};
|
|
10073
10142
|
var folderCreate = ({ client, log }) => ({
|
|
10074
10143
|
name: "folder_create",
|
|
@@ -10085,7 +10154,8 @@ var folderCreate = ({ client, log }) => ({
|
|
|
10085
10154
|
slug: args.slug,
|
|
10086
10155
|
userId: args.userId,
|
|
10087
10156
|
orgId: args.orgId,
|
|
10088
|
-
clientId: args.clientId
|
|
10157
|
+
clientId: args.clientId,
|
|
10158
|
+
scopes: args.scopes
|
|
10089
10159
|
}
|
|
10090
10160
|
});
|
|
10091
10161
|
log.debug({ tool: "folder_create", id: folder.id, name: folder.name }, "folder_create ok");
|
|
@@ -10286,7 +10356,7 @@ var inputSchema19 = {
|
|
|
10286
10356
|
// Inline text mode (mutually exclusive with filePath):
|
|
10287
10357
|
text: import_zod18.z.string().min(1).optional().describe("Text body to ingest inline. Mutually exclusive with filePath. Either text or filePath is required."),
|
|
10288
10358
|
storeText: import_zod18.z.boolean().optional().describe(
|
|
10289
|
-
"
|
|
10359
|
+
"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."
|
|
10290
10360
|
),
|
|
10291
10361
|
// File upload mode (mutually exclusive with text; stdio-transport-only):
|
|
10292
10362
|
filePath: import_zod18.z.string().min(1).optional().describe(
|
|
@@ -10297,12 +10367,15 @@ var inputSchema19 = {
|
|
|
10297
10367
|
folderId: import_zod18.z.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
|
|
10298
10368
|
userId: import_zod18.z.string().optional().describe("Owning user ID. Optional."),
|
|
10299
10369
|
orgId: import_zod18.z.string().optional().describe("Owning org ID. Optional."),
|
|
10300
|
-
clientId: import_zod18.z.string().optional().describe("Owning client ID. Optional.")
|
|
10370
|
+
clientId: import_zod18.z.string().optional().describe("Owning client ID. Optional."),
|
|
10371
|
+
scopes: import_zod18.z.array(import_zod18.z.string()).optional().describe(
|
|
10372
|
+
"TEXT MODE ONLY. Scope ownership as `namespace:value` entries, at most 2 (e.g. [\"org:<uuid>\", \"group:eng-team\"]). This is the document's COMPLETE scope declaration and values must come from the credential's own identity. An empty array `[]` creates a PRIVATE document owned by the calling user alone. Omit to stamp the credential's full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields. Rejected with `filePath` (file uploads do not accept a scope declaration): a file-uploaded document is stamped with the credential's full identity."
|
|
10373
|
+
)
|
|
10301
10374
|
};
|
|
10302
10375
|
var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
10303
10376
|
name: "document_ingest",
|
|
10304
10377
|
title: "Ingest a new document (text body or file upload)",
|
|
10305
|
-
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
|
|
10378
|
+
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.",
|
|
10306
10379
|
inputSchema: inputSchema19,
|
|
10307
10380
|
handler: async (args) => {
|
|
10308
10381
|
const title = args.title;
|
|
@@ -10331,6 +10404,14 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10331
10404
|
)
|
|
10332
10405
|
);
|
|
10333
10406
|
}
|
|
10407
|
+
if (filePath && args.scopes !== void 0) {
|
|
10408
|
+
return toolError(
|
|
10409
|
+
"document_ingest",
|
|
10410
|
+
new Error(
|
|
10411
|
+
"`scopes` is not supported with `filePath` \u2014 file uploads do not accept a scope declaration, and silently ignoring it could make an intended-private document team-visible. Use `text` mode to declare scopes, or omit `scopes` (the upload is stamped with the credential's full identity)."
|
|
10412
|
+
)
|
|
10413
|
+
);
|
|
10414
|
+
}
|
|
10334
10415
|
const common = {
|
|
10335
10416
|
folderId: args.folderId,
|
|
10336
10417
|
payload: args.payload,
|
|
@@ -10342,14 +10423,15 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10342
10423
|
};
|
|
10343
10424
|
try {
|
|
10344
10425
|
if (text) {
|
|
10345
|
-
const storeText = args.storeText ?? true;
|
|
10346
10426
|
const result = await client.documents.ingestDocument({
|
|
10347
10427
|
upsert,
|
|
10348
10428
|
body: {
|
|
10349
10429
|
title,
|
|
10350
10430
|
text,
|
|
10351
10431
|
indexMode,
|
|
10352
|
-
|
|
10432
|
+
// Scope ownership is a TEXT-mode contract only (the upload-init
|
|
10433
|
+
// endpoint has no scopes field — see the gate above).
|
|
10434
|
+
scopes: args.scopes,
|
|
10353
10435
|
...common
|
|
10354
10436
|
}
|
|
10355
10437
|
});
|
|
@@ -10380,11 +10462,13 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10380
10462
|
}
|
|
10381
10463
|
const fileName = (0, import_node_path.basename)(safePath);
|
|
10382
10464
|
const fileType = args.fileType ?? inferMimeType(safePath);
|
|
10465
|
+
const storeTextFile = args.storeText ?? true;
|
|
10383
10466
|
const upload = await client.documents.uploadDocument({
|
|
10384
10467
|
upsert,
|
|
10385
10468
|
fileName,
|
|
10386
10469
|
fileType,
|
|
10387
10470
|
indexMode,
|
|
10471
|
+
storeText: storeTextFile,
|
|
10388
10472
|
...common
|
|
10389
10473
|
});
|
|
10390
10474
|
const uploadUrl = upload?.uploadUrl;
|
|
@@ -10440,7 +10524,7 @@ var document_ingest_default = documentIngest;
|
|
|
10440
10524
|
// src/tools/lookup_principal.ts
|
|
10441
10525
|
var import_zod19 = require("zod");
|
|
10442
10526
|
var MCP_DEFAULT_LIMIT5 = 10;
|
|
10443
|
-
var MCP_MAX_LIMIT5 =
|
|
10527
|
+
var MCP_MAX_LIMIT5 = 100;
|
|
10444
10528
|
var inputSchema20 = {
|
|
10445
10529
|
kind: import_zod19.z.enum(["user", "org", "client"]).describe("Which principal to look up: a user, an organization, or a client."),
|
|
10446
10530
|
externalId: import_zod19.z.string().optional().describe(
|
|
@@ -10454,12 +10538,15 @@ var inputSchema20 = {
|
|
|
10454
10538
|
to: import_zod19.z.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
|
|
10455
10539
|
prefix: import_zod19.z.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
|
|
10456
10540
|
order: import_zod19.z.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
|
|
10457
|
-
|
|
10541
|
+
startFrom: import_zod19.z.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
|
|
10542
|
+
limit: import_zod19.z.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
|
|
10543
|
+
`Max principals per page. Defaults to ${MCP_DEFAULT_LIMIT5}; raise up to ${MCP_MAX_LIMIT5} (the principals API max) in one call when you can accept the payload, or page with \`startFrom\`.`
|
|
10544
|
+
)
|
|
10458
10545
|
};
|
|
10459
10546
|
var lookupPrincipal = ({ client, log }) => ({
|
|
10460
10547
|
name: "lookup_principal",
|
|
10461
10548
|
title: "Look up a user / org / client identity",
|
|
10462
|
-
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
|
|
10549
|
+
description: "Resolve a user, org, or client identity. Two modes:\n \u2022 Resolve by your own id: pass `externalId` \u2192 the matching principal incl. its Vectros UUID (the id the ownership filters on record_query / hybrid_search / rag_ask expect).\n \u2022 Lookup by a schema field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Sensitive-field-safe. (If both `externalId` and a `field` lookup are given, `externalId` wins.) Returns a `{ data, nextCursor }` page (default 10, max 100 \u2014 raise it in one call when you can accept the payload); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom`. Read-only \u2014 does not create or modify identities.",
|
|
10463
10550
|
inputSchema: inputSchema20,
|
|
10464
10551
|
handler: async (args) => {
|
|
10465
10552
|
const kind = args.kind;
|
|
@@ -10472,6 +10559,7 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10472
10559
|
const to = args.to;
|
|
10473
10560
|
const prefix = args.prefix;
|
|
10474
10561
|
const order = args.order;
|
|
10562
|
+
const startFrom = args.startFrom;
|
|
10475
10563
|
try {
|
|
10476
10564
|
let page;
|
|
10477
10565
|
if (externalId !== void 0) {
|
|
@@ -10509,7 +10597,7 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10509
10597
|
new Error("lookup requires 'type' (the identity schema's record type) alongside 'field'.")
|
|
10510
10598
|
);
|
|
10511
10599
|
}
|
|
10512
|
-
const req = { type, field, value, from, to, prefix, order, limit };
|
|
10600
|
+
const req = { type, field, value, from, to, prefix, order, startFrom, limit };
|
|
10513
10601
|
if (kind === "user") {
|
|
10514
10602
|
page = await client.identity.lookupUsers(req);
|
|
10515
10603
|
} else if (kind === "org") {
|
|
@@ -10525,7 +10613,8 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10525
10613
|
);
|
|
10526
10614
|
}
|
|
10527
10615
|
const principals = pageItems(page);
|
|
10528
|
-
|
|
10616
|
+
const nextCursor = page.nextCursor ?? null;
|
|
10617
|
+
return { content: [{ type: "text", text: JSON.stringify({ data: principals, nextCursor }, null, 2) }] };
|
|
10529
10618
|
} catch (err) {
|
|
10530
10619
|
log.warn({ tool: "lookup_principal", kind, err: String(err) }, "lookup_principal failed");
|
|
10531
10620
|
return toolError("lookup_principal", err);
|