@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.mjs
CHANGED
|
@@ -650,6 +650,10 @@ var ReadAccessLogResponse;
|
|
|
650
650
|
})(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
|
|
651
651
|
var RecordRequest;
|
|
652
652
|
((RecordRequest2) => {
|
|
653
|
+
RecordRequest2.Status = {
|
|
654
|
+
Active: "ACTIVE",
|
|
655
|
+
Archived: "ARCHIVED"
|
|
656
|
+
};
|
|
653
657
|
RecordRequest2.IndexMode = {
|
|
654
658
|
Hybrid: "HYBRID",
|
|
655
659
|
Semantic: "SEMANTIC",
|
|
@@ -659,6 +663,10 @@ var RecordRequest;
|
|
|
659
663
|
})(RecordRequest || (RecordRequest = {}));
|
|
660
664
|
var RecordResponse;
|
|
661
665
|
((RecordResponse2) => {
|
|
666
|
+
RecordResponse2.Status = {
|
|
667
|
+
Active: "ACTIVE",
|
|
668
|
+
Archived: "ARCHIVED"
|
|
669
|
+
};
|
|
662
670
|
RecordResponse2.IndexStatus = {
|
|
663
671
|
PendingIndex: "PENDING_INDEX",
|
|
664
672
|
Indexed: "INDEXED",
|
|
@@ -3133,7 +3141,7 @@ var AuthClient = class {
|
|
|
3133
3141
|
* roleId: "engineering-member",
|
|
3134
3142
|
* name: "Engineering Team Member",
|
|
3135
3143
|
* scopes: [{
|
|
3136
|
-
* allowed_actions: ["
|
|
3144
|
+
* allowed_actions: ["records:cru", "search:r"]
|
|
3137
3145
|
* }]
|
|
3138
3146
|
* }
|
|
3139
3147
|
* })
|
|
@@ -3681,7 +3689,7 @@ var AuthClient = class {
|
|
|
3681
3689
|
* roleId: "engineering-member",
|
|
3682
3690
|
* name: "Engineering Team Member",
|
|
3683
3691
|
* scopes: [{
|
|
3684
|
-
* allowed_actions: ["
|
|
3692
|
+
* allowed_actions: ["records:cru", "search:r"]
|
|
3685
3693
|
* }]
|
|
3686
3694
|
* }
|
|
3687
3695
|
* })
|
|
@@ -4584,7 +4592,7 @@ var DocumentsClient = class {
|
|
|
4584
4592
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
4585
4593
|
}
|
|
4586
4594
|
/**
|
|
4587
|
-
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, or `
|
|
4595
|
+
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, `clientId`, or `scope`). The response is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom` to fetch the next page. Requires the `documents:r` scope.
|
|
4588
4596
|
*
|
|
4589
4597
|
* @param {Vectros.ListDocumentsRequest} request
|
|
4590
4598
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4594,6 +4602,7 @@ var DocumentsClient = class {
|
|
|
4594
4602
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
4595
4603
|
* orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
4596
4604
|
* clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
4605
|
+
* scope: "group:eng-team",
|
|
4597
4606
|
* folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
4598
4607
|
* startFrom: "doc_prev123"
|
|
4599
4608
|
* })
|
|
@@ -4602,11 +4611,12 @@ var DocumentsClient = class {
|
|
|
4602
4611
|
return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
|
|
4603
4612
|
}
|
|
4604
4613
|
async __listDocuments(request = {}, requestOptions) {
|
|
4605
|
-
const { userId, orgId, clientId, folderId, startFrom, limit } = request;
|
|
4614
|
+
const { userId, orgId, clientId, scope, folderId, startFrom, limit } = request;
|
|
4606
4615
|
const _queryParams = {
|
|
4607
4616
|
userId,
|
|
4608
4617
|
orgId,
|
|
4609
4618
|
clientId,
|
|
4619
|
+
scope,
|
|
4610
4620
|
folderId,
|
|
4611
4621
|
startFrom,
|
|
4612
4622
|
limit
|
|
@@ -4664,9 +4674,10 @@ var DocumentsClient = class {
|
|
|
4664
4674
|
return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
|
|
4665
4675
|
}
|
|
4666
4676
|
async __ingestDocument(request, requestOptions) {
|
|
4667
|
-
const { upsert, body: _body } = request;
|
|
4677
|
+
const { upsert, allowClear, body: _body } = request;
|
|
4668
4678
|
const _queryParams = {
|
|
4669
|
-
upsert
|
|
4679
|
+
upsert,
|
|
4680
|
+
allowClear
|
|
4670
4681
|
};
|
|
4671
4682
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4672
4683
|
const _headers = mergeHeaders(
|
|
@@ -4790,7 +4801,10 @@ var DocumentsClient = class {
|
|
|
4790
4801
|
return HttpResponsePromise.fromPromise(this.__updateDocument(request, requestOptions));
|
|
4791
4802
|
}
|
|
4792
4803
|
async __updateDocument(request, requestOptions) {
|
|
4793
|
-
const { id, body: _body } = request;
|
|
4804
|
+
const { id, allowClear, body: _body } = request;
|
|
4805
|
+
const _queryParams = {
|
|
4806
|
+
allowClear
|
|
4807
|
+
};
|
|
4794
4808
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4795
4809
|
const _headers = mergeHeaders(
|
|
4796
4810
|
_authRequest.headers,
|
|
@@ -4805,7 +4819,7 @@ var DocumentsClient = class {
|
|
|
4805
4819
|
method: "PUT",
|
|
4806
4820
|
headers: _headers,
|
|
4807
4821
|
contentType: "application/json",
|
|
4808
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4822
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4809
4823
|
requestType: "json",
|
|
4810
4824
|
body: _body,
|
|
4811
4825
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
@@ -4896,7 +4910,7 @@ var DocumentsClient = class {
|
|
|
4896
4910
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/documents/{id}");
|
|
4897
4911
|
}
|
|
4898
4912
|
/**
|
|
4899
|
-
* 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`, `
|
|
4913
|
+
* 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.
|
|
4900
4914
|
*
|
|
4901
4915
|
* @param {Vectros.PatchDocumentRequest} request
|
|
4902
4916
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -5148,7 +5162,7 @@ var DocumentsClient = class {
|
|
|
5148
5162
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/download");
|
|
5149
5163
|
}
|
|
5150
5164
|
/**
|
|
5151
|
-
* Returns the full
|
|
5165
|
+
* 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.
|
|
5152
5166
|
*
|
|
5153
5167
|
* @param {Vectros.GetDocumentTextRequest} request
|
|
5154
5168
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7541,7 +7555,7 @@ var RecordsClient = class {
|
|
|
7541
7555
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch");
|
|
7542
7556
|
}
|
|
7543
7557
|
/**
|
|
7544
|
-
* Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
|
|
7558
|
+
* Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`, `scope`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
|
|
7545
7559
|
*
|
|
7546
7560
|
* @param {Vectros.ListRecordsRequest} request
|
|
7547
7561
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7553,6 +7567,7 @@ var RecordsClient = class {
|
|
|
7553
7567
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
7554
7568
|
* orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
7555
7569
|
* clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
7570
|
+
* scope: "group:eng-team",
|
|
7556
7571
|
* startFrom: "550e8400-e29b-41d4-a716-446655440000"
|
|
7557
7572
|
* })
|
|
7558
7573
|
*/
|
|
@@ -7560,13 +7575,25 @@ var RecordsClient = class {
|
|
|
7560
7575
|
return HttpResponsePromise.fromPromise(this.__listRecords(request, requestOptions));
|
|
7561
7576
|
}
|
|
7562
7577
|
async __listRecords(request = {}, requestOptions) {
|
|
7563
|
-
const {
|
|
7578
|
+
const {
|
|
7579
|
+
type: type_,
|
|
7580
|
+
folderId,
|
|
7581
|
+
userId,
|
|
7582
|
+
orgId,
|
|
7583
|
+
clientId,
|
|
7584
|
+
scope,
|
|
7585
|
+
startFrom,
|
|
7586
|
+
limit,
|
|
7587
|
+
includePayload,
|
|
7588
|
+
recent
|
|
7589
|
+
} = request;
|
|
7564
7590
|
const _queryParams = {
|
|
7565
7591
|
type: type_,
|
|
7566
7592
|
folderId,
|
|
7567
7593
|
userId,
|
|
7568
7594
|
orgId,
|
|
7569
7595
|
clientId,
|
|
7596
|
+
scope,
|
|
7570
7597
|
startFrom,
|
|
7571
7598
|
limit,
|
|
7572
7599
|
includePayload,
|
|
@@ -7631,9 +7658,10 @@ var RecordsClient = class {
|
|
|
7631
7658
|
return HttpResponsePromise.fromPromise(this.__createRecord(request, requestOptions));
|
|
7632
7659
|
}
|
|
7633
7660
|
async __createRecord(request, requestOptions) {
|
|
7634
|
-
const { upsert, body: _body } = request;
|
|
7661
|
+
const { upsert, allowClear, body: _body } = request;
|
|
7635
7662
|
const _queryParams = {
|
|
7636
|
-
upsert
|
|
7663
|
+
upsert,
|
|
7664
|
+
allowClear
|
|
7637
7665
|
};
|
|
7638
7666
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7639
7667
|
const _headers = mergeHeaders(
|
|
@@ -7755,7 +7783,10 @@ var RecordsClient = class {
|
|
|
7755
7783
|
return HttpResponsePromise.fromPromise(this.__updateRecord(request, requestOptions));
|
|
7756
7784
|
}
|
|
7757
7785
|
async __updateRecord(request, requestOptions) {
|
|
7758
|
-
const { id, body: _body } = request;
|
|
7786
|
+
const { id, allowClear, body: _body } = request;
|
|
7787
|
+
const _queryParams = {
|
|
7788
|
+
allowClear
|
|
7789
|
+
};
|
|
7759
7790
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7760
7791
|
const _headers = mergeHeaders(
|
|
7761
7792
|
_authRequest.headers,
|
|
@@ -7770,7 +7801,7 @@ var RecordsClient = class {
|
|
|
7770
7801
|
method: "PUT",
|
|
7771
7802
|
headers: _headers,
|
|
7772
7803
|
contentType: "application/json",
|
|
7773
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
7804
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
7774
7805
|
requestType: "json",
|
|
7775
7806
|
body: _body,
|
|
7776
7807
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
@@ -8762,8 +8793,8 @@ function warnOnSuboptimalKey(key, log) {
|
|
|
8762
8793
|
|
|
8763
8794
|
// src/build-info.ts
|
|
8764
8795
|
var BUILD_INFO = {
|
|
8765
|
-
mcpServer: true ? "0.
|
|
8766
|
-
sdk: true ? "0.
|
|
8796
|
+
mcpServer: true ? "0.9.0" : "dev",
|
|
8797
|
+
sdk: true ? "0.34.0" : "dev"
|
|
8767
8798
|
};
|
|
8768
8799
|
|
|
8769
8800
|
// src/log.ts
|
|
@@ -8999,7 +9030,7 @@ ${detail}${pointer}`
|
|
|
8999
9030
|
|
|
9000
9031
|
// src/tools/hybrid_search.ts
|
|
9001
9032
|
var MCP_DEFAULT_LIMIT = 3;
|
|
9002
|
-
var MCP_MAX_LIMIT =
|
|
9033
|
+
var MCP_MAX_LIMIT = 50;
|
|
9003
9034
|
var MCP_RESPONSE_CHAR_BUDGET = 24e3;
|
|
9004
9035
|
var SNIPPET_MAX_CHARS = 160;
|
|
9005
9036
|
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["tenantId", "owner_id", "model_type", "rootFolderId", "folderId"]);
|
|
@@ -9033,9 +9064,9 @@ var inputSchema = {
|
|
|
9033
9064
|
"Keyword-match precision for the BM25 leg (TEXT/HYBRID modes). OR = any term (broad recall), AND = all terms (higher precision), PHRASE = contiguous phrase. Default: PHRASE (slop 3) in HYBRID, OR in TEXT. (The advanced raw-query COMPLEX mode is intentionally not exposed here.)"
|
|
9034
9065
|
),
|
|
9035
9066
|
limit: z.number().int().min(1).max(MCP_MAX_LIMIT).optional().describe(
|
|
9036
|
-
`Max hits
|
|
9067
|
+
`Max hits per page. Defaults to a low ${MCP_DEFAULT_LIMIT} to protect the agent context window; raise up to ${MCP_MAX_LIMIT} (the most a single search returns) in one call when you can accept the larger result, or page with \`offset\` instead.`
|
|
9037
9068
|
),
|
|
9038
|
-
offset: z.number().int().min(0).optional().describe("Skip the first N hits \u2014 for pagination."),
|
|
9069
|
+
offset: z.number().int().min(0).max(200).optional().describe("Skip the first N hits \u2014 for pagination (0\u2013200)."),
|
|
9039
9070
|
folderId: z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
|
|
9040
9071
|
rootFolderId: z.string().optional().describe("Scope to content under this folder AND all its descendants (subtree-root folder id)."),
|
|
9041
9072
|
// Content narrowing.
|
|
@@ -9050,6 +9081,9 @@ var inputSchema = {
|
|
|
9050
9081
|
userId: z.string().optional().describe("Restrict to content owned by this user (Vectros UUID)."),
|
|
9051
9082
|
orgId: z.string().optional().describe("Restrict to content belonging to this org (Vectros UUID)."),
|
|
9052
9083
|
clientId: z.string().optional().describe("Restrict to content associated with this client (Vectros UUID)."),
|
|
9084
|
+
scope: z.string().optional().describe(
|
|
9085
|
+
'Restrict to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9086
|
+
),
|
|
9053
9087
|
// Date window.
|
|
9054
9088
|
createdAfter: z.string().optional().describe("Restrict to content created at/after this ISO 8601 UTC timestamp."),
|
|
9055
9089
|
createdBefore: z.string().optional().describe("Restrict to content created at/before this ISO 8601 UTC timestamp."),
|
|
@@ -9067,7 +9101,7 @@ var inputSchema = {
|
|
|
9067
9101
|
var hybridSearch = ({ client, log }) => ({
|
|
9068
9102
|
name: "hybrid_search",
|
|
9069
9103
|
title: "Hybrid search",
|
|
9070
|
-
description:
|
|
9104
|
+
description: "Search your tenant's indexed content (documents + structured records) using hybrid BM25 + dense ranking. Returns up to `limit` results (default 3, max 50 \u2014 raise it in one call when you can accept the larger result) with citations and surrounding context for grounding follow-up reasoning. Narrow with contentTypes/typeName/filters, ownership (userId/orgId/clientId), folder scope, a created date window, keyword precision (textMode OR/AND/PHRASE), relevance floors (minSimilarity/minTextRelevance), uniqueDocuments, and requireComplete (fail closed on a degraded backend). Tenant-isolated; the caller's scoped key fully constrains which content is visible. ARCHIVED (soft-retracted) documents never appear in results \u2014 fetch them by id via document_get, or restore them via document_update. To stay within the agent context window each hit returns its matched chunkText + a short snippet by default; set includeContext:true for the broader surrounding passage. If the whole result would be too large it is truncated to the top hits (truncated:true). totalResults is the full matching pool and hasMore:true means results remain past this page \u2014 raise limit or advance offset to fetch them. textLegEmpty:true flags that the keyword leg matched nothing (all textScores 0) \u2014 usually a too-long PHRASE query; retry with textMode:\"OR\".",
|
|
9071
9105
|
inputSchema,
|
|
9072
9106
|
handler: async (args) => {
|
|
9073
9107
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT;
|
|
@@ -9087,6 +9121,7 @@ var hybridSearch = ({ client, log }) => ({
|
|
|
9087
9121
|
userId: args.userId,
|
|
9088
9122
|
orgId: args.orgId,
|
|
9089
9123
|
clientId: args.clientId,
|
|
9124
|
+
scope: args.scope,
|
|
9090
9125
|
createdAfter: args.createdAfter,
|
|
9091
9126
|
createdBefore: args.createdBefore,
|
|
9092
9127
|
minSimilarity: args.minSimilarity,
|
|
@@ -9113,6 +9148,12 @@ var hybridSearch = ({ client, log }) => ({
|
|
|
9113
9148
|
envelope.returnedResults = shaped.length;
|
|
9114
9149
|
text = JSON.stringify(envelope, null, 2);
|
|
9115
9150
|
}
|
|
9151
|
+
const offset = args.offset ?? 0;
|
|
9152
|
+
const totalResults = result.totalResults;
|
|
9153
|
+
if (typeof totalResults === "number" && offset + shaped.length < totalResults) {
|
|
9154
|
+
envelope.hasMore = true;
|
|
9155
|
+
text = JSON.stringify(envelope, null, 2);
|
|
9156
|
+
}
|
|
9116
9157
|
log.debug(
|
|
9117
9158
|
{ tool: "hybrid_search", mode, limit, returned: shaped.length, textLegEmpty },
|
|
9118
9159
|
"hybrid_search ok"
|
|
@@ -9148,7 +9189,7 @@ async function drainPages(fetchPage) {
|
|
|
9148
9189
|
|
|
9149
9190
|
// src/tools/record_query.ts
|
|
9150
9191
|
var MCP_DEFAULT_LIMIT2 = 3;
|
|
9151
|
-
var MCP_MAX_LIMIT2 =
|
|
9192
|
+
var MCP_MAX_LIMIT2 = 100;
|
|
9152
9193
|
var inputSchema2 = {
|
|
9153
9194
|
type: z2.string().min(1, "type (record type) is required").describe('Record type / schema name (e.g. "patient", "clinical_note").'),
|
|
9154
9195
|
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
|
|
@@ -9166,14 +9207,18 @@ var inputSchema2 = {
|
|
|
9166
9207
|
userId: z2.string().optional().describe("List mode: scope to records owned by this user."),
|
|
9167
9208
|
orgId: z2.string().optional().describe("List mode: scope to records owned by this org."),
|
|
9168
9209
|
clientId: z2.string().optional().describe("List mode: scope to records associated with this client."),
|
|
9210
|
+
scope: z2.string().optional().describe(
|
|
9211
|
+
'List mode: filter to records carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9212
|
+
),
|
|
9213
|
+
startFrom: z2.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
|
|
9169
9214
|
limit: z2.number().int().min(1).max(MCP_MAX_LIMIT2).optional().describe(
|
|
9170
|
-
`Max records
|
|
9215
|
+
`Max records per page. Defaults to a low ${MCP_DEFAULT_LIMIT2} to protect the agent context window; raise up to ${MCP_MAX_LIMIT2} (the records API max) in one call when you can accept the larger payload, or page with \`startFrom\` instead.`
|
|
9171
9216
|
)
|
|
9172
9217
|
};
|
|
9173
9218
|
var recordQuery = ({ client, log }) => ({
|
|
9174
9219
|
name: "record_query",
|
|
9175
9220
|
title: "Record query (list or lookup)",
|
|
9176
|
-
description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields too.\n \u2022 List by type: omit `field`; optionally filter by `userId`/`orgId`/`clientId`.\nReturns up to
|
|
9221
|
+
description: "Query structured records of a given type. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields too.\n \u2022 List by type: omit `field`; optionally filter by `userId`/`orgId`/`clientId`.\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` records (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected from the arguments present.",
|
|
9177
9222
|
inputSchema: inputSchema2,
|
|
9178
9223
|
handler: async (args) => {
|
|
9179
9224
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT2;
|
|
@@ -9183,8 +9228,9 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9183
9228
|
const from = args.from;
|
|
9184
9229
|
const to = args.to;
|
|
9185
9230
|
const prefix = args.prefix;
|
|
9231
|
+
const startFrom = args.startFrom;
|
|
9186
9232
|
try {
|
|
9187
|
-
let
|
|
9233
|
+
let page;
|
|
9188
9234
|
if (field) {
|
|
9189
9235
|
const hasEquality = value !== void 0;
|
|
9190
9236
|
const hasRange = from !== void 0 || to !== void 0;
|
|
@@ -9207,7 +9253,7 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9207
9253
|
if (hasRange && !(from !== void 0 && to !== void 0)) {
|
|
9208
9254
|
return toolError("record_query", new Error("range lookup requires both 'from' and 'to'."));
|
|
9209
9255
|
}
|
|
9210
|
-
|
|
9256
|
+
page = await client.records.lookupRecordsByBody({
|
|
9211
9257
|
type,
|
|
9212
9258
|
field,
|
|
9213
9259
|
value,
|
|
@@ -9215,29 +9261,32 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9215
9261
|
to,
|
|
9216
9262
|
prefix,
|
|
9217
9263
|
order: args.order,
|
|
9264
|
+
startFrom,
|
|
9218
9265
|
limit
|
|
9219
9266
|
});
|
|
9220
|
-
records = pageItems(page);
|
|
9221
9267
|
log.debug(
|
|
9222
|
-
{ tool: "record_query", mode: "lookup", type, field, returned:
|
|
9268
|
+
{ tool: "record_query", mode: "lookup", type, field, returned: pageItems(page).length },
|
|
9223
9269
|
"record_query lookup ok"
|
|
9224
9270
|
);
|
|
9225
9271
|
} else {
|
|
9226
|
-
|
|
9272
|
+
page = await client.records.listRecords({
|
|
9227
9273
|
type,
|
|
9228
9274
|
userId: args.userId,
|
|
9229
9275
|
orgId: args.orgId,
|
|
9230
9276
|
clientId: args.clientId,
|
|
9277
|
+
scope: args.scope,
|
|
9278
|
+
startFrom,
|
|
9231
9279
|
limit
|
|
9232
9280
|
});
|
|
9233
|
-
records = pageItems(page);
|
|
9234
9281
|
log.debug(
|
|
9235
|
-
{ tool: "record_query", mode: "list", type, limit, returned:
|
|
9282
|
+
{ tool: "record_query", mode: "list", type, limit, returned: pageItems(page).length },
|
|
9236
9283
|
"record_query list ok"
|
|
9237
9284
|
);
|
|
9238
9285
|
}
|
|
9286
|
+
const records = pageItems(page);
|
|
9287
|
+
const nextCursor = page.nextCursor ?? null;
|
|
9239
9288
|
return {
|
|
9240
|
-
content: [{ type: "text", text: JSON.stringify(records, null, 2) }]
|
|
9289
|
+
content: [{ type: "text", text: JSON.stringify({ data: records, nextCursor }, null, 2) }]
|
|
9241
9290
|
};
|
|
9242
9291
|
} catch (err) {
|
|
9243
9292
|
log.warn({ tool: "record_query", err: String(err) }, "record_query failed");
|
|
@@ -9360,7 +9409,10 @@ var inputSchema4 = {
|
|
|
9360
9409
|
folderId: z4.string().optional().describe("Group this record into a folder."),
|
|
9361
9410
|
userId: z4.string().optional().describe("Owning user id."),
|
|
9362
9411
|
orgId: z4.string().optional().describe("Owning organization id."),
|
|
9363
|
-
clientId: z4.string().optional().describe("Associated client id.")
|
|
9412
|
+
clientId: z4.string().optional().describe("Associated client id."),
|
|
9413
|
+
scopes: z4.array(z4.string()).optional().describe(
|
|
9414
|
+
'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). This is the record\'s COMPLETE scope declaration and values must come from the credential\'s own identity. An empty array `[]` creates a PRIVATE record owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields.'
|
|
9415
|
+
)
|
|
9364
9416
|
};
|
|
9365
9417
|
var recordCreate = ({ client, log }) => ({
|
|
9366
9418
|
name: "record_create",
|
|
@@ -9380,7 +9432,8 @@ var recordCreate = ({ client, log }) => ({
|
|
|
9380
9432
|
folderId: args.folderId,
|
|
9381
9433
|
userId: args.userId,
|
|
9382
9434
|
orgId: args.orgId,
|
|
9383
|
-
clientId: args.clientId
|
|
9435
|
+
clientId: args.clientId,
|
|
9436
|
+
scopes: args.scopes
|
|
9384
9437
|
}
|
|
9385
9438
|
});
|
|
9386
9439
|
log.debug({ tool: "record_create", type, id: created.id }, "record_create ok");
|
|
@@ -9465,24 +9518,29 @@ var record_delete_default = recordDelete;
|
|
|
9465
9518
|
// src/tools/rag_ask.ts
|
|
9466
9519
|
import { z as z7 } from "zod";
|
|
9467
9520
|
var SEARCH_MCP_DEFAULT_LIMIT = 5;
|
|
9468
|
-
var SEARCH_MCP_MAX_LIMIT =
|
|
9521
|
+
var SEARCH_MCP_MAX_LIMIT = 50;
|
|
9469
9522
|
var inputSchema7 = {
|
|
9470
9523
|
query: z7.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
|
|
9471
9524
|
instructions: z7.string().optional().describe(
|
|
9472
9525
|
'System prompt that overrides the default for the generation step \u2014 e.g. "answer only from the provided context, cite sources, be concise". Default: a generic answer-from-context instruction.'
|
|
9473
9526
|
),
|
|
9474
9527
|
model: z7.string().optional().describe(
|
|
9475
|
-
"Inference model alias (e.g. claude-haiku-4-5, claude-sonnet-
|
|
9528
|
+
"Inference model alias (e.g. claude-haiku-4-5, claude-sonnet-5, claude-opus-4-8). Default = tier-appropriate Haiku. See GET /v1/models for the catalog the calling key can reach."
|
|
9476
9529
|
),
|
|
9477
9530
|
search: z7.object({
|
|
9478
9531
|
mode: z7.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
|
|
9479
|
-
limit: z7.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
|
|
9532
|
+
limit: z7.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
|
|
9533
|
+
`Passages to retrieve before generating (the grounding-corpus size). Default ${SEARCH_MCP_DEFAULT_LIMIT}; raise up to ${SEARCH_MCP_MAX_LIMIT} (the RAG search API max) for a broader corpus when the extra grounding is worth the larger prompt.`
|
|
9534
|
+
),
|
|
9480
9535
|
// Retrieval scoping — parity with hybrid_search so an agent can ground the
|
|
9481
9536
|
// answer on a specific owner / folder / type / metadata subset, not just the
|
|
9482
9537
|
// whole-tenant corpus.
|
|
9483
9538
|
userId: z7.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
|
|
9484
9539
|
orgId: z7.string().optional().describe("Restrict retrieval to content owned by this org (Vectros UUID)."),
|
|
9485
9540
|
clientId: z7.string().optional().describe("Restrict retrieval to content tagged with this client (Vectros UUID)."),
|
|
9541
|
+
scope: z7.string().optional().describe(
|
|
9542
|
+
'Restrict retrieval to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9543
|
+
),
|
|
9486
9544
|
folderId: z7.string().optional().describe("Restrict retrieval to this exact folder."),
|
|
9487
9545
|
rootFolderId: z7.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
|
|
9488
9546
|
typeName: z7.string().optional().describe("Restrict record retrieval to this schema type."),
|
|
@@ -9500,7 +9558,7 @@ var inputSchema7 = {
|
|
|
9500
9558
|
var ragAsk = ({ client, log }) => ({
|
|
9501
9559
|
name: "rag_ask",
|
|
9502
9560
|
title: "RAG ask (corpus-wide)",
|
|
9503
|
-
description: "Ask a question grounded against
|
|
9561
|
+
description: "Ask a question grounded against your tenant's indexed content. Vectros performs a hybrid search, injects the top-K passages into the prompt, and streams a model answer back. Scope retrieval with `search` (ownership, folder, type, metadata filters, date window) to ground on a subset \u2014 e.g. one patient or one folder \u2014 and steer generation with `instructions` / `temperature`. The full answer is returned as a single response; progress notifications keep the call alive during the 30-45s generation window. Inference runs in-perimeter against AWS Bedrock \u2014 PHI never leaves the BAA boundary.",
|
|
9504
9562
|
inputSchema: inputSchema7,
|
|
9505
9563
|
handler: async (args, extra) => {
|
|
9506
9564
|
try {
|
|
@@ -9511,6 +9569,7 @@ var ragAsk = ({ client, log }) => ({
|
|
|
9511
9569
|
userId: searchArg.userId,
|
|
9512
9570
|
orgId: searchArg.orgId,
|
|
9513
9571
|
clientId: searchArg.clientId,
|
|
9572
|
+
scope: searchArg.scope,
|
|
9514
9573
|
folderId: searchArg.folderId,
|
|
9515
9574
|
rootFolderId: searchArg.rootFolderId,
|
|
9516
9575
|
typeName: searchArg.typeName,
|
|
@@ -9668,7 +9727,7 @@ var inputSchema9 = {
|
|
|
9668
9727
|
var listSchemas = ({ client, log }) => ({
|
|
9669
9728
|
name: "list_schemas",
|
|
9670
9729
|
title: "List record schemas",
|
|
9671
|
-
description: 'List the structured-record schema catalog for
|
|
9730
|
+
description: 'List the structured-record schema catalog for your tenant. Each schema describes a record type (e.g. "patient", "clinical_note") \u2014 its fields, lookup-indexed fields, and capabilities. Use this to discover what record types exist before calling `record_query`. Filter with `surface` (record/document/user/org/client \u2014 e.g. only document types), `recordType` (resolve one schema by its type name), or `userId`/`orgId`; default returns everything the credential can see.',
|
|
9672
9731
|
inputSchema: inputSchema9,
|
|
9673
9732
|
handler: async (args) => {
|
|
9674
9733
|
const userId = args.userId;
|
|
@@ -9722,7 +9781,7 @@ function isNotAvailable(err) {
|
|
|
9722
9781
|
var documentGet = ({ client, log }) => ({
|
|
9723
9782
|
name: "document_get",
|
|
9724
9783
|
title: "Get document metadata (and optionally text)",
|
|
9725
|
-
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).
|
|
9784
|
+
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`.",
|
|
9726
9785
|
inputSchema: inputSchema10,
|
|
9727
9786
|
handler: async (args) => {
|
|
9728
9787
|
const documentId = args.documentId;
|
|
@@ -9746,7 +9805,7 @@ var documentGet = ({ client, log }) => ({
|
|
|
9746
9805
|
if (isNotAvailable(textErr)) {
|
|
9747
9806
|
log.debug(
|
|
9748
9807
|
{ tool: "document_get", id },
|
|
9749
|
-
"document_get text unavailable (
|
|
9808
|
+
"document_get text unavailable (not retained \u2014 storeText:false upload)"
|
|
9750
9809
|
);
|
|
9751
9810
|
extra = { ...extra, textAvailable: false };
|
|
9752
9811
|
} else {
|
|
@@ -9790,7 +9849,7 @@ var document_get_default = documentGet;
|
|
|
9790
9849
|
// src/tools/document_query.ts
|
|
9791
9850
|
import { z as z11 } from "zod";
|
|
9792
9851
|
var MCP_DEFAULT_LIMIT3 = 3;
|
|
9793
|
-
var MCP_MAX_LIMIT3 =
|
|
9852
|
+
var MCP_MAX_LIMIT3 = 100;
|
|
9794
9853
|
var inputSchema11 = {
|
|
9795
9854
|
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
|
|
9796
9855
|
field: z11.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
|
|
@@ -9807,14 +9866,18 @@ var inputSchema11 = {
|
|
|
9807
9866
|
userId: z11.string().optional().describe("List mode: scope to documents owned by this user."),
|
|
9808
9867
|
orgId: z11.string().optional().describe("List mode: scope to documents belonging to this org."),
|
|
9809
9868
|
clientId: z11.string().optional().describe("List mode: scope to documents associated with this client."),
|
|
9869
|
+
scope: z11.string().optional().describe(
|
|
9870
|
+
'List mode: filter to documents carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "group:eng-team"). `scope=org:<id>`/`scope=client:<id>` are equivalent to the orgId/clientId filters.'
|
|
9871
|
+
),
|
|
9872
|
+
startFrom: z11.string().optional().describe("Pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next. Works in both list and lookup mode."),
|
|
9810
9873
|
limit: z11.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
|
|
9811
|
-
`Max documents
|
|
9874
|
+
`Max documents per page. Defaults to a low ${MCP_DEFAULT_LIMIT3} to protect the agent context window; raise up to ${MCP_MAX_LIMIT3} (the documents API max) in one call when you can accept the larger payload, or page with \`startFrom\` instead.`
|
|
9812
9875
|
)
|
|
9813
9876
|
};
|
|
9814
9877
|
var documentQuery = ({ client, log }) => ({
|
|
9815
9878
|
name: "document_query",
|
|
9816
9879
|
title: "Document query (list or lookup)",
|
|
9817
|
-
description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `folderId`/`userId`/`orgId`/`clientId`.\nReturns up to
|
|
9880
|
+
description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `folderId`/`userId`/`orgId`/`clientId`/`scope`.\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` documents (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected. Use document_get for the full text/download URL of one document.",
|
|
9818
9881
|
inputSchema: inputSchema11,
|
|
9819
9882
|
handler: async (args) => {
|
|
9820
9883
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT3;
|
|
@@ -9824,8 +9887,9 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9824
9887
|
const to = args.to;
|
|
9825
9888
|
const prefix = args.prefix;
|
|
9826
9889
|
const type = args.type;
|
|
9890
|
+
const startFrom = args.startFrom;
|
|
9827
9891
|
try {
|
|
9828
|
-
let
|
|
9892
|
+
let page;
|
|
9829
9893
|
if (field) {
|
|
9830
9894
|
const hasEquality = value !== void 0;
|
|
9831
9895
|
const hasRange = from !== void 0 || to !== void 0;
|
|
@@ -9854,7 +9918,7 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9854
9918
|
new Error("lookup requires 'type' (the document's bound schema type) alongside 'field'.")
|
|
9855
9919
|
);
|
|
9856
9920
|
}
|
|
9857
|
-
|
|
9921
|
+
page = await client.documents.lookupDocumentsByBody({
|
|
9858
9922
|
type,
|
|
9859
9923
|
field,
|
|
9860
9924
|
value,
|
|
@@ -9862,28 +9926,31 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9862
9926
|
to,
|
|
9863
9927
|
prefix,
|
|
9864
9928
|
order: args.order,
|
|
9929
|
+
startFrom,
|
|
9865
9930
|
limit
|
|
9866
9931
|
});
|
|
9867
|
-
documents = pageItems(page);
|
|
9868
9932
|
log.debug(
|
|
9869
|
-
{ tool: "document_query", mode: "lookup", type, field, returned:
|
|
9933
|
+
{ tool: "document_query", mode: "lookup", type, field, returned: pageItems(page).length },
|
|
9870
9934
|
"document_query lookup ok"
|
|
9871
9935
|
);
|
|
9872
9936
|
} else {
|
|
9873
|
-
|
|
9937
|
+
page = await client.documents.listDocuments({
|
|
9874
9938
|
folderId: args.folderId,
|
|
9875
9939
|
userId: args.userId,
|
|
9876
9940
|
orgId: args.orgId,
|
|
9877
9941
|
clientId: args.clientId,
|
|
9942
|
+
scope: args.scope,
|
|
9943
|
+
startFrom,
|
|
9878
9944
|
limit
|
|
9879
9945
|
});
|
|
9880
|
-
documents = pageItems(page);
|
|
9881
9946
|
log.debug(
|
|
9882
|
-
{ tool: "document_query", mode: "list", limit, returned:
|
|
9947
|
+
{ tool: "document_query", mode: "list", limit, returned: pageItems(page).length },
|
|
9883
9948
|
"document_query list ok"
|
|
9884
9949
|
);
|
|
9885
9950
|
}
|
|
9886
|
-
|
|
9951
|
+
const documents = pageItems(page);
|
|
9952
|
+
const nextCursor = page.nextCursor ?? null;
|
|
9953
|
+
return { content: [{ type: "text", text: JSON.stringify({ data: documents, nextCursor }, null, 2) }] };
|
|
9887
9954
|
} catch (err) {
|
|
9888
9955
|
log.warn({ tool: "document_query", err: String(err) }, "document_query failed");
|
|
9889
9956
|
return toolError("document_query", err);
|
|
@@ -9908,7 +9975,6 @@ var inputSchema12 = {
|
|
|
9908
9975
|
"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."
|
|
9909
9976
|
),
|
|
9910
9977
|
folderId: z12.string().optional().describe("Move the document into this folder (cannot be cleared once set)."),
|
|
9911
|
-
storeText: z12.boolean().optional().describe("Whether the raw text is stored + retrievable."),
|
|
9912
9978
|
status: z12.enum(["ACTIVE", "ARCHIVED"]).optional().describe(
|
|
9913
9979
|
"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."
|
|
9914
9980
|
),
|
|
@@ -9932,7 +9998,6 @@ var documentUpdate = ({ client, log }) => ({
|
|
|
9932
9998
|
const fields = args.fields;
|
|
9933
9999
|
const text = args.text;
|
|
9934
10000
|
const folderId = args.folderId;
|
|
9935
|
-
const storeText = args.storeText;
|
|
9936
10001
|
const status = args.status;
|
|
9937
10002
|
const userId = args.userId;
|
|
9938
10003
|
const orgId = args.orgId;
|
|
@@ -9946,7 +10011,6 @@ var documentUpdate = ({ client, log }) => ({
|
|
|
9946
10011
|
if (fields !== void 0) body.payload = fields;
|
|
9947
10012
|
if (text !== void 0) body.text = text;
|
|
9948
10013
|
if (folderId !== void 0) body.folderId = folderId;
|
|
9949
|
-
if (storeText !== void 0) body.storeText = storeText;
|
|
9950
10014
|
if (status !== void 0) body.status = status;
|
|
9951
10015
|
if (userId !== void 0) body.userId = userId;
|
|
9952
10016
|
if (orgId !== void 0) body.orgId = orgId;
|
|
@@ -9995,7 +10059,7 @@ var document_delete_default = documentDelete;
|
|
|
9995
10059
|
// src/tools/folder_query.ts
|
|
9996
10060
|
import { z as z14 } from "zod";
|
|
9997
10061
|
var MCP_DEFAULT_LIMIT4 = 10;
|
|
9998
|
-
var MCP_MAX_LIMIT4 =
|
|
10062
|
+
var MCP_MAX_LIMIT4 = 100;
|
|
9999
10063
|
var inputSchema14 = {
|
|
10000
10064
|
id: z14.string().optional().describe("Get mode: fetch this single folder by id."),
|
|
10001
10065
|
parentId: z14.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
|
|
@@ -10003,12 +10067,14 @@ var inputSchema14 = {
|
|
|
10003
10067
|
orgId: z14.string().optional().describe("List mode: scope to folders belonging to this org."),
|
|
10004
10068
|
clientId: z14.string().optional().describe("List mode: scope to folders associated with this client."),
|
|
10005
10069
|
startFrom: z14.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
|
|
10006
|
-
limit: z14.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
|
|
10070
|
+
limit: z14.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
|
|
10071
|
+
`List mode: max folders per page. Default 10; raise up to ${MCP_MAX_LIMIT4} (the folders API max) in one call when you can accept the larger page, or page with \`startFrom\`.`
|
|
10072
|
+
)
|
|
10007
10073
|
};
|
|
10008
10074
|
var folderQuery = ({ client, log }) => ({
|
|
10009
10075
|
name: "folder_query",
|
|
10010
10076
|
title: "Folder query (list or get)",
|
|
10011
|
-
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
|
|
10077
|
+
description: "Read folders. Two modes:\n \u2022 Get: pass `id` \u2192 returns the single folder.\n \u2022 List: omit `id`; pass `parentId` for a folder's direct children (tree navigation) or omit for a flat tenant list. Optionally filter by `userId`/`orgId`/`clientId`.\nList mode returns `{ data, nextCursor }` (default 10, max 100 per page \u2014 raise it in one call when you can accept the larger page); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page through all folders. Get mode returns the single folder object.",
|
|
10012
10078
|
inputSchema: inputSchema14,
|
|
10013
10079
|
handler: async (args) => {
|
|
10014
10080
|
const id = args.id;
|
|
@@ -10048,7 +10114,10 @@ var inputSchema15 = {
|
|
|
10048
10114
|
slug: z15.string().optional().describe("Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. Derived from name when omitted."),
|
|
10049
10115
|
userId: z15.string().optional().describe("Owning user (Vectros UUID)."),
|
|
10050
10116
|
orgId: z15.string().optional().describe("Owning org (Vectros UUID)."),
|
|
10051
|
-
clientId: z15.string().optional().describe("Associated client (Vectros UUID).")
|
|
10117
|
+
clientId: z15.string().optional().describe("Associated client (Vectros UUID)."),
|
|
10118
|
+
scopes: z15.array(z15.string()).optional().describe(
|
|
10119
|
+
'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). This is the folder\'s COMPLETE scope declaration and values must come from the credential\'s own identity. An empty array `[]` creates a PRIVATE folder owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields.'
|
|
10120
|
+
)
|
|
10052
10121
|
};
|
|
10053
10122
|
var folderCreate = ({ client, log }) => ({
|
|
10054
10123
|
name: "folder_create",
|
|
@@ -10065,7 +10134,8 @@ var folderCreate = ({ client, log }) => ({
|
|
|
10065
10134
|
slug: args.slug,
|
|
10066
10135
|
userId: args.userId,
|
|
10067
10136
|
orgId: args.orgId,
|
|
10068
|
-
clientId: args.clientId
|
|
10137
|
+
clientId: args.clientId,
|
|
10138
|
+
scopes: args.scopes
|
|
10069
10139
|
}
|
|
10070
10140
|
});
|
|
10071
10141
|
log.debug({ tool: "folder_create", id: folder.id, name: folder.name }, "folder_create ok");
|
|
@@ -10266,7 +10336,7 @@ var inputSchema19 = {
|
|
|
10266
10336
|
// Inline text mode (mutually exclusive with filePath):
|
|
10267
10337
|
text: z18.string().min(1).optional().describe("Text body to ingest inline. Mutually exclusive with filePath. Either text or filePath is required."),
|
|
10268
10338
|
storeText: z18.boolean().optional().describe(
|
|
10269
|
-
"
|
|
10339
|
+
"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."
|
|
10270
10340
|
),
|
|
10271
10341
|
// File upload mode (mutually exclusive with text; stdio-transport-only):
|
|
10272
10342
|
filePath: z18.string().min(1).optional().describe(
|
|
@@ -10277,12 +10347,15 @@ var inputSchema19 = {
|
|
|
10277
10347
|
folderId: z18.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
|
|
10278
10348
|
userId: z18.string().optional().describe("Owning user ID. Optional."),
|
|
10279
10349
|
orgId: z18.string().optional().describe("Owning org ID. Optional."),
|
|
10280
|
-
clientId: z18.string().optional().describe("Owning client ID. Optional.")
|
|
10350
|
+
clientId: z18.string().optional().describe("Owning client ID. Optional."),
|
|
10351
|
+
scopes: z18.array(z18.string()).optional().describe(
|
|
10352
|
+
"TEXT MODE ONLY. Scope ownership as `namespace:value` entries, at most 2 (e.g. [\"org:<uuid>\", \"group:eng-team\"]). This is the document's COMPLETE scope declaration and values must come from the credential's own identity. An empty array `[]` creates a PRIVATE document owned by the calling user alone. Omit to stamp the credential's full identity \u2014 the default. `org:`/`client:` entries are equivalent to the orgId/clientId fields. Rejected with `filePath` (file uploads do not accept a scope declaration): a file-uploaded document is stamped with the credential's full identity."
|
|
10353
|
+
)
|
|
10281
10354
|
};
|
|
10282
10355
|
var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
10283
10356
|
name: "document_ingest",
|
|
10284
10357
|
title: "Ingest a new document (text body or file upload)",
|
|
10285
|
-
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
|
|
10358
|
+
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.",
|
|
10286
10359
|
inputSchema: inputSchema19,
|
|
10287
10360
|
handler: async (args) => {
|
|
10288
10361
|
const title = args.title;
|
|
@@ -10311,6 +10384,14 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10311
10384
|
)
|
|
10312
10385
|
);
|
|
10313
10386
|
}
|
|
10387
|
+
if (filePath && args.scopes !== void 0) {
|
|
10388
|
+
return toolError(
|
|
10389
|
+
"document_ingest",
|
|
10390
|
+
new Error(
|
|
10391
|
+
"`scopes` is not supported with `filePath` \u2014 file uploads do not accept a scope declaration, and silently ignoring it could make an intended-private document team-visible. Use `text` mode to declare scopes, or omit `scopes` (the upload is stamped with the credential's full identity)."
|
|
10392
|
+
)
|
|
10393
|
+
);
|
|
10394
|
+
}
|
|
10314
10395
|
const common = {
|
|
10315
10396
|
folderId: args.folderId,
|
|
10316
10397
|
payload: args.payload,
|
|
@@ -10322,14 +10403,15 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10322
10403
|
};
|
|
10323
10404
|
try {
|
|
10324
10405
|
if (text) {
|
|
10325
|
-
const storeText = args.storeText ?? true;
|
|
10326
10406
|
const result = await client.documents.ingestDocument({
|
|
10327
10407
|
upsert,
|
|
10328
10408
|
body: {
|
|
10329
10409
|
title,
|
|
10330
10410
|
text,
|
|
10331
10411
|
indexMode,
|
|
10332
|
-
|
|
10412
|
+
// Scope ownership is a TEXT-mode contract only (the upload-init
|
|
10413
|
+
// endpoint has no scopes field — see the gate above).
|
|
10414
|
+
scopes: args.scopes,
|
|
10333
10415
|
...common
|
|
10334
10416
|
}
|
|
10335
10417
|
});
|
|
@@ -10360,11 +10442,13 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10360
10442
|
}
|
|
10361
10443
|
const fileName = basename(safePath);
|
|
10362
10444
|
const fileType = args.fileType ?? inferMimeType(safePath);
|
|
10445
|
+
const storeTextFile = args.storeText ?? true;
|
|
10363
10446
|
const upload = await client.documents.uploadDocument({
|
|
10364
10447
|
upsert,
|
|
10365
10448
|
fileName,
|
|
10366
10449
|
fileType,
|
|
10367
10450
|
indexMode,
|
|
10451
|
+
storeText: storeTextFile,
|
|
10368
10452
|
...common
|
|
10369
10453
|
});
|
|
10370
10454
|
const uploadUrl = upload?.uploadUrl;
|
|
@@ -10420,7 +10504,7 @@ var document_ingest_default = documentIngest;
|
|
|
10420
10504
|
// src/tools/lookup_principal.ts
|
|
10421
10505
|
import { z as z19 } from "zod";
|
|
10422
10506
|
var MCP_DEFAULT_LIMIT5 = 10;
|
|
10423
|
-
var MCP_MAX_LIMIT5 =
|
|
10507
|
+
var MCP_MAX_LIMIT5 = 100;
|
|
10424
10508
|
var inputSchema20 = {
|
|
10425
10509
|
kind: z19.enum(["user", "org", "client"]).describe("Which principal to look up: a user, an organization, or a client."),
|
|
10426
10510
|
externalId: z19.string().optional().describe(
|
|
@@ -10434,12 +10518,15 @@ var inputSchema20 = {
|
|
|
10434
10518
|
to: z19.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
|
|
10435
10519
|
prefix: z19.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
|
|
10436
10520
|
order: z19.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
|
|
10437
|
-
|
|
10521
|
+
startFrom: z19.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
|
|
10522
|
+
limit: z19.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
|
|
10523
|
+
`Max principals per page. Defaults to ${MCP_DEFAULT_LIMIT5}; raise up to ${MCP_MAX_LIMIT5} (the principals API max) in one call when you can accept the payload, or page with \`startFrom\`.`
|
|
10524
|
+
)
|
|
10438
10525
|
};
|
|
10439
10526
|
var lookupPrincipal = ({ client, log }) => ({
|
|
10440
10527
|
name: "lookup_principal",
|
|
10441
10528
|
title: "Look up a user / org / client identity",
|
|
10442
|
-
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
|
|
10529
|
+
description: "Resolve a user, org, or client identity. Two modes:\n \u2022 Resolve by your own id: pass `externalId` \u2192 the matching principal incl. its Vectros UUID (the id the ownership filters on record_query / hybrid_search / rag_ask expect).\n \u2022 Lookup by a schema field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Sensitive-field-safe. (If both `externalId` and a `field` lookup are given, `externalId` wins.) Returns a `{ data, nextCursor }` page (default 10, max 100 \u2014 raise it in one call when you can accept the payload); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom`. Read-only \u2014 does not create or modify identities.",
|
|
10443
10530
|
inputSchema: inputSchema20,
|
|
10444
10531
|
handler: async (args) => {
|
|
10445
10532
|
const kind = args.kind;
|
|
@@ -10452,6 +10539,7 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10452
10539
|
const to = args.to;
|
|
10453
10540
|
const prefix = args.prefix;
|
|
10454
10541
|
const order = args.order;
|
|
10542
|
+
const startFrom = args.startFrom;
|
|
10455
10543
|
try {
|
|
10456
10544
|
let page;
|
|
10457
10545
|
if (externalId !== void 0) {
|
|
@@ -10489,7 +10577,7 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10489
10577
|
new Error("lookup requires 'type' (the identity schema's record type) alongside 'field'.")
|
|
10490
10578
|
);
|
|
10491
10579
|
}
|
|
10492
|
-
const req = { type, field, value, from, to, prefix, order, limit };
|
|
10580
|
+
const req = { type, field, value, from, to, prefix, order, startFrom, limit };
|
|
10493
10581
|
if (kind === "user") {
|
|
10494
10582
|
page = await client.identity.lookupUsers(req);
|
|
10495
10583
|
} else if (kind === "org") {
|
|
@@ -10505,7 +10593,8 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10505
10593
|
);
|
|
10506
10594
|
}
|
|
10507
10595
|
const principals = pageItems(page);
|
|
10508
|
-
|
|
10596
|
+
const nextCursor = page.nextCursor ?? null;
|
|
10597
|
+
return { content: [{ type: "text", text: JSON.stringify({ data: principals, nextCursor }, null, 2) }] };
|
|
10509
10598
|
} catch (err) {
|
|
10510
10599
|
log.warn({ tool: "lookup_principal", kind, err: String(err) }, "lookup_principal failed");
|
|
10511
10600
|
return toolError("lookup_principal", err);
|