@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.mjs
CHANGED
|
@@ -652,6 +652,10 @@ var ReadAccessLogResponse;
|
|
|
652
652
|
})(ReadAccessLogResponse || (ReadAccessLogResponse = {}));
|
|
653
653
|
var RecordRequest;
|
|
654
654
|
((RecordRequest2) => {
|
|
655
|
+
RecordRequest2.Status = {
|
|
656
|
+
Active: "ACTIVE",
|
|
657
|
+
Archived: "ARCHIVED"
|
|
658
|
+
};
|
|
655
659
|
RecordRequest2.IndexMode = {
|
|
656
660
|
Hybrid: "HYBRID",
|
|
657
661
|
Semantic: "SEMANTIC",
|
|
@@ -661,6 +665,10 @@ var RecordRequest;
|
|
|
661
665
|
})(RecordRequest || (RecordRequest = {}));
|
|
662
666
|
var RecordResponse;
|
|
663
667
|
((RecordResponse2) => {
|
|
668
|
+
RecordResponse2.Status = {
|
|
669
|
+
Active: "ACTIVE",
|
|
670
|
+
Archived: "ARCHIVED"
|
|
671
|
+
};
|
|
664
672
|
RecordResponse2.IndexStatus = {
|
|
665
673
|
PendingIndex: "PENDING_INDEX",
|
|
666
674
|
Indexed: "INDEXED",
|
|
@@ -3135,7 +3143,7 @@ var AuthClient = class {
|
|
|
3135
3143
|
* roleId: "engineering-member",
|
|
3136
3144
|
* name: "Engineering Team Member",
|
|
3137
3145
|
* scopes: [{
|
|
3138
|
-
* allowed_actions: ["
|
|
3146
|
+
* allowed_actions: ["records:cru", "search:r"]
|
|
3139
3147
|
* }]
|
|
3140
3148
|
* }
|
|
3141
3149
|
* })
|
|
@@ -3683,7 +3691,7 @@ var AuthClient = class {
|
|
|
3683
3691
|
* roleId: "engineering-member",
|
|
3684
3692
|
* name: "Engineering Team Member",
|
|
3685
3693
|
* scopes: [{
|
|
3686
|
-
* allowed_actions: ["
|
|
3694
|
+
* allowed_actions: ["records:cru", "search:r"]
|
|
3687
3695
|
* }]
|
|
3688
3696
|
* }
|
|
3689
3697
|
* })
|
|
@@ -4586,7 +4594,7 @@ var DocumentsClient = class {
|
|
|
4586
4594
|
this._options = normalizeClientOptionsWithAuth(options);
|
|
4587
4595
|
}
|
|
4588
4596
|
/**
|
|
4589
|
-
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, or `
|
|
4597
|
+
* Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId`, `orgId`, `clientId`, or `scope`). The response is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom` to fetch the next page. Requires the `documents:r` scope.
|
|
4590
4598
|
*
|
|
4591
4599
|
* @param {Vectros.ListDocumentsRequest} request
|
|
4592
4600
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -4596,6 +4604,7 @@ var DocumentsClient = class {
|
|
|
4596
4604
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
4597
4605
|
* orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
4598
4606
|
* clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
4607
|
+
* scope: "group:eng-team",
|
|
4599
4608
|
* folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
4600
4609
|
* startFrom: "doc_prev123"
|
|
4601
4610
|
* })
|
|
@@ -4604,11 +4613,12 @@ var DocumentsClient = class {
|
|
|
4604
4613
|
return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
|
|
4605
4614
|
}
|
|
4606
4615
|
async __listDocuments(request = {}, requestOptions) {
|
|
4607
|
-
const { userId, orgId, clientId, folderId, startFrom, limit } = request;
|
|
4616
|
+
const { userId, orgId, clientId, scope, folderId, startFrom, limit } = request;
|
|
4608
4617
|
const _queryParams = {
|
|
4609
4618
|
userId,
|
|
4610
4619
|
orgId,
|
|
4611
4620
|
clientId,
|
|
4621
|
+
scope,
|
|
4612
4622
|
folderId,
|
|
4613
4623
|
startFrom,
|
|
4614
4624
|
limit
|
|
@@ -4666,9 +4676,10 @@ var DocumentsClient = class {
|
|
|
4666
4676
|
return HttpResponsePromise.fromPromise(this.__ingestDocument(request, requestOptions));
|
|
4667
4677
|
}
|
|
4668
4678
|
async __ingestDocument(request, requestOptions) {
|
|
4669
|
-
const { upsert, body: _body } = request;
|
|
4679
|
+
const { upsert, allowClear, body: _body } = request;
|
|
4670
4680
|
const _queryParams = {
|
|
4671
|
-
upsert
|
|
4681
|
+
upsert,
|
|
4682
|
+
allowClear
|
|
4672
4683
|
};
|
|
4673
4684
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4674
4685
|
const _headers = mergeHeaders(
|
|
@@ -4792,7 +4803,10 @@ var DocumentsClient = class {
|
|
|
4792
4803
|
return HttpResponsePromise.fromPromise(this.__updateDocument(request, requestOptions));
|
|
4793
4804
|
}
|
|
4794
4805
|
async __updateDocument(request, requestOptions) {
|
|
4795
|
-
const { id, body: _body } = request;
|
|
4806
|
+
const { id, allowClear, body: _body } = request;
|
|
4807
|
+
const _queryParams = {
|
|
4808
|
+
allowClear
|
|
4809
|
+
};
|
|
4796
4810
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
4797
4811
|
const _headers = mergeHeaders(
|
|
4798
4812
|
_authRequest.headers,
|
|
@@ -4807,7 +4821,7 @@ var DocumentsClient = class {
|
|
|
4807
4821
|
method: "PUT",
|
|
4808
4822
|
headers: _headers,
|
|
4809
4823
|
contentType: "application/json",
|
|
4810
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
4824
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
4811
4825
|
requestType: "json",
|
|
4812
4826
|
body: _body,
|
|
4813
4827
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
@@ -4898,7 +4912,7 @@ var DocumentsClient = class {
|
|
|
4898
4912
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/documents/{id}");
|
|
4899
4913
|
}
|
|
4900
4914
|
/**
|
|
4901
|
-
* 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`, `
|
|
4915
|
+
* 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.
|
|
4902
4916
|
*
|
|
4903
4917
|
* @param {Vectros.PatchDocumentRequest} request
|
|
4904
4918
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -5150,7 +5164,7 @@ var DocumentsClient = class {
|
|
|
5150
5164
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/download");
|
|
5151
5165
|
}
|
|
5152
5166
|
/**
|
|
5153
|
-
* Returns the full
|
|
5167
|
+
* 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.
|
|
5154
5168
|
*
|
|
5155
5169
|
* @param {Vectros.GetDocumentTextRequest} request
|
|
5156
5170
|
* @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7543,7 +7557,7 @@ var RecordsClient = class {
|
|
|
7543
7557
|
return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/records/batch");
|
|
7544
7558
|
}
|
|
7545
7559
|
/**
|
|
7546
|
-
* Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
|
|
7560
|
+
* Returns a paginated list of records in your account as a `{data, nextCursor}` page. Supply exactly one of `type`, `folderId`, or `recent=true` to choose the mode: `type` lists all records of a single type; `folderId` lists all records in a folder (any type); and `recent=true` returns the account-wide recently-updated feed across all types, newest first. You may combine `type` with `folderId` to list a single type within a folder. The owner filters (`userId`, `orgId`, `clientId`, `scope`) further narrow the type and folder modes; the `recent` feed is standalone and ignores all filters. Each token only sees the record types it is scoped to read. Requires the `records:r` scope. By default the response returns the indexed projection of each record; set `includePayload=true` to include full payloads.
|
|
7547
7561
|
*
|
|
7548
7562
|
* @param {Vectros.ListRecordsRequest} request
|
|
7549
7563
|
* @param {RecordsClient.RequestOptions} requestOptions - Request-specific configuration.
|
|
@@ -7555,6 +7569,7 @@ var RecordsClient = class {
|
|
|
7555
7569
|
* userId: "550e8400-e29b-41d4-a716-446655440000",
|
|
7556
7570
|
* orgId: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
|
7557
7571
|
* clientId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
7572
|
+
* scope: "group:eng-team",
|
|
7558
7573
|
* startFrom: "550e8400-e29b-41d4-a716-446655440000"
|
|
7559
7574
|
* })
|
|
7560
7575
|
*/
|
|
@@ -7562,13 +7577,25 @@ var RecordsClient = class {
|
|
|
7562
7577
|
return HttpResponsePromise.fromPromise(this.__listRecords(request, requestOptions));
|
|
7563
7578
|
}
|
|
7564
7579
|
async __listRecords(request = {}, requestOptions) {
|
|
7565
|
-
const {
|
|
7580
|
+
const {
|
|
7581
|
+
type: type_,
|
|
7582
|
+
folderId,
|
|
7583
|
+
userId,
|
|
7584
|
+
orgId,
|
|
7585
|
+
clientId,
|
|
7586
|
+
scope,
|
|
7587
|
+
startFrom,
|
|
7588
|
+
limit,
|
|
7589
|
+
includePayload,
|
|
7590
|
+
recent
|
|
7591
|
+
} = request;
|
|
7566
7592
|
const _queryParams = {
|
|
7567
7593
|
type: type_,
|
|
7568
7594
|
folderId,
|
|
7569
7595
|
userId,
|
|
7570
7596
|
orgId,
|
|
7571
7597
|
clientId,
|
|
7598
|
+
scope,
|
|
7572
7599
|
startFrom,
|
|
7573
7600
|
limit,
|
|
7574
7601
|
includePayload,
|
|
@@ -7633,9 +7660,10 @@ var RecordsClient = class {
|
|
|
7633
7660
|
return HttpResponsePromise.fromPromise(this.__createRecord(request, requestOptions));
|
|
7634
7661
|
}
|
|
7635
7662
|
async __createRecord(request, requestOptions) {
|
|
7636
|
-
const { upsert, body: _body } = request;
|
|
7663
|
+
const { upsert, allowClear, body: _body } = request;
|
|
7637
7664
|
const _queryParams = {
|
|
7638
|
-
upsert
|
|
7665
|
+
upsert,
|
|
7666
|
+
allowClear
|
|
7639
7667
|
};
|
|
7640
7668
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7641
7669
|
const _headers = mergeHeaders(
|
|
@@ -7757,7 +7785,10 @@ var RecordsClient = class {
|
|
|
7757
7785
|
return HttpResponsePromise.fromPromise(this.__updateRecord(request, requestOptions));
|
|
7758
7786
|
}
|
|
7759
7787
|
async __updateRecord(request, requestOptions) {
|
|
7760
|
-
const { id, body: _body } = request;
|
|
7788
|
+
const { id, allowClear, body: _body } = request;
|
|
7789
|
+
const _queryParams = {
|
|
7790
|
+
allowClear
|
|
7791
|
+
};
|
|
7761
7792
|
const _authRequest = await this._options.authProvider.getAuthRequest();
|
|
7762
7793
|
const _headers = mergeHeaders(
|
|
7763
7794
|
_authRequest.headers,
|
|
@@ -7772,7 +7803,7 @@ var RecordsClient = class {
|
|
|
7772
7803
|
method: "PUT",
|
|
7773
7804
|
headers: _headers,
|
|
7774
7805
|
contentType: "application/json",
|
|
7775
|
-
queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
|
|
7806
|
+
queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
|
|
7776
7807
|
requestType: "json",
|
|
7777
7808
|
body: _body,
|
|
7778
7809
|
timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
|
|
@@ -8764,8 +8795,8 @@ function warnOnSuboptimalKey(key, log) {
|
|
|
8764
8795
|
|
|
8765
8796
|
// src/build-info.ts
|
|
8766
8797
|
var BUILD_INFO = {
|
|
8767
|
-
mcpServer: true ? "0.
|
|
8768
|
-
sdk: true ? "0.
|
|
8798
|
+
mcpServer: true ? "0.9.0" : "dev",
|
|
8799
|
+
sdk: true ? "0.34.0" : "dev"
|
|
8769
8800
|
};
|
|
8770
8801
|
|
|
8771
8802
|
// src/log.ts
|
|
@@ -9001,7 +9032,7 @@ ${detail}${pointer}`
|
|
|
9001
9032
|
|
|
9002
9033
|
// src/tools/hybrid_search.ts
|
|
9003
9034
|
var MCP_DEFAULT_LIMIT = 3;
|
|
9004
|
-
var MCP_MAX_LIMIT =
|
|
9035
|
+
var MCP_MAX_LIMIT = 50;
|
|
9005
9036
|
var MCP_RESPONSE_CHAR_BUDGET = 24e3;
|
|
9006
9037
|
var SNIPPET_MAX_CHARS = 160;
|
|
9007
9038
|
var INTERNAL_METADATA_KEYS = /* @__PURE__ */ new Set(["tenantId", "owner_id", "model_type", "rootFolderId", "folderId"]);
|
|
@@ -9035,9 +9066,9 @@ var inputSchema = {
|
|
|
9035
9066
|
"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.)"
|
|
9036
9067
|
),
|
|
9037
9068
|
limit: z.number().int().min(1).max(MCP_MAX_LIMIT).optional().describe(
|
|
9038
|
-
`Max hits
|
|
9069
|
+
`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.`
|
|
9039
9070
|
),
|
|
9040
|
-
offset: z.number().int().min(0).optional().describe("Skip the first N hits \u2014 for pagination."),
|
|
9071
|
+
offset: z.number().int().min(0).max(200).optional().describe("Skip the first N hits \u2014 for pagination (0\u2013200)."),
|
|
9041
9072
|
folderId: z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
|
|
9042
9073
|
rootFolderId: z.string().optional().describe("Scope to content under this folder AND all its descendants (subtree-root folder id)."),
|
|
9043
9074
|
// Content narrowing.
|
|
@@ -9052,6 +9083,9 @@ var inputSchema = {
|
|
|
9052
9083
|
userId: z.string().optional().describe("Restrict to content owned by this user (Vectros UUID)."),
|
|
9053
9084
|
orgId: z.string().optional().describe("Restrict to content belonging to this org (Vectros UUID)."),
|
|
9054
9085
|
clientId: z.string().optional().describe("Restrict to content associated with this client (Vectros UUID)."),
|
|
9086
|
+
scope: z.string().optional().describe(
|
|
9087
|
+
'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.'
|
|
9088
|
+
),
|
|
9055
9089
|
// Date window.
|
|
9056
9090
|
createdAfter: z.string().optional().describe("Restrict to content created at/after this ISO 8601 UTC timestamp."),
|
|
9057
9091
|
createdBefore: z.string().optional().describe("Restrict to content created at/before this ISO 8601 UTC timestamp."),
|
|
@@ -9069,7 +9103,7 @@ var inputSchema = {
|
|
|
9069
9103
|
var hybridSearch = ({ client, log }) => ({
|
|
9070
9104
|
name: "hybrid_search",
|
|
9071
9105
|
title: "Hybrid search",
|
|
9072
|
-
description:
|
|
9106
|
+
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\".",
|
|
9073
9107
|
inputSchema,
|
|
9074
9108
|
handler: async (args) => {
|
|
9075
9109
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT;
|
|
@@ -9089,6 +9123,7 @@ var hybridSearch = ({ client, log }) => ({
|
|
|
9089
9123
|
userId: args.userId,
|
|
9090
9124
|
orgId: args.orgId,
|
|
9091
9125
|
clientId: args.clientId,
|
|
9126
|
+
scope: args.scope,
|
|
9092
9127
|
createdAfter: args.createdAfter,
|
|
9093
9128
|
createdBefore: args.createdBefore,
|
|
9094
9129
|
minSimilarity: args.minSimilarity,
|
|
@@ -9115,6 +9150,12 @@ var hybridSearch = ({ client, log }) => ({
|
|
|
9115
9150
|
envelope.returnedResults = shaped.length;
|
|
9116
9151
|
text = JSON.stringify(envelope, null, 2);
|
|
9117
9152
|
}
|
|
9153
|
+
const offset = args.offset ?? 0;
|
|
9154
|
+
const totalResults = result.totalResults;
|
|
9155
|
+
if (typeof totalResults === "number" && offset + shaped.length < totalResults) {
|
|
9156
|
+
envelope.hasMore = true;
|
|
9157
|
+
text = JSON.stringify(envelope, null, 2);
|
|
9158
|
+
}
|
|
9118
9159
|
log.debug(
|
|
9119
9160
|
{ tool: "hybrid_search", mode, limit, returned: shaped.length, textLegEmpty },
|
|
9120
9161
|
"hybrid_search ok"
|
|
@@ -9150,7 +9191,7 @@ async function drainPages(fetchPage) {
|
|
|
9150
9191
|
|
|
9151
9192
|
// src/tools/record_query.ts
|
|
9152
9193
|
var MCP_DEFAULT_LIMIT2 = 3;
|
|
9153
|
-
var MCP_MAX_LIMIT2 =
|
|
9194
|
+
var MCP_MAX_LIMIT2 = 100;
|
|
9154
9195
|
var inputSchema2 = {
|
|
9155
9196
|
type: z2.string().min(1, "type (record type) is required").describe('Record type / schema name (e.g. "patient", "clinical_note").'),
|
|
9156
9197
|
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
|
|
@@ -9168,14 +9209,18 @@ var inputSchema2 = {
|
|
|
9168
9209
|
userId: z2.string().optional().describe("List mode: scope to records owned by this user."),
|
|
9169
9210
|
orgId: z2.string().optional().describe("List mode: scope to records owned by this org."),
|
|
9170
9211
|
clientId: z2.string().optional().describe("List mode: scope to records associated with this client."),
|
|
9212
|
+
scope: z2.string().optional().describe(
|
|
9213
|
+
'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.'
|
|
9214
|
+
),
|
|
9215
|
+
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."),
|
|
9171
9216
|
limit: z2.number().int().min(1).max(MCP_MAX_LIMIT2).optional().describe(
|
|
9172
|
-
`Max records
|
|
9217
|
+
`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.`
|
|
9173
9218
|
)
|
|
9174
9219
|
};
|
|
9175
9220
|
var recordQuery = ({ client, log }) => ({
|
|
9176
9221
|
name: "record_query",
|
|
9177
9222
|
title: "Record query (list or lookup)",
|
|
9178
|
-
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
|
|
9223
|
+
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.",
|
|
9179
9224
|
inputSchema: inputSchema2,
|
|
9180
9225
|
handler: async (args) => {
|
|
9181
9226
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT2;
|
|
@@ -9185,8 +9230,9 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9185
9230
|
const from = args.from;
|
|
9186
9231
|
const to = args.to;
|
|
9187
9232
|
const prefix = args.prefix;
|
|
9233
|
+
const startFrom = args.startFrom;
|
|
9188
9234
|
try {
|
|
9189
|
-
let
|
|
9235
|
+
let page;
|
|
9190
9236
|
if (field) {
|
|
9191
9237
|
const hasEquality = value !== void 0;
|
|
9192
9238
|
const hasRange = from !== void 0 || to !== void 0;
|
|
@@ -9209,7 +9255,7 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9209
9255
|
if (hasRange && !(from !== void 0 && to !== void 0)) {
|
|
9210
9256
|
return toolError("record_query", new Error("range lookup requires both 'from' and 'to'."));
|
|
9211
9257
|
}
|
|
9212
|
-
|
|
9258
|
+
page = await client.records.lookupRecordsByBody({
|
|
9213
9259
|
type,
|
|
9214
9260
|
field,
|
|
9215
9261
|
value,
|
|
@@ -9217,29 +9263,32 @@ var recordQuery = ({ client, log }) => ({
|
|
|
9217
9263
|
to,
|
|
9218
9264
|
prefix,
|
|
9219
9265
|
order: args.order,
|
|
9266
|
+
startFrom,
|
|
9220
9267
|
limit
|
|
9221
9268
|
});
|
|
9222
|
-
records = pageItems(page);
|
|
9223
9269
|
log.debug(
|
|
9224
|
-
{ tool: "record_query", mode: "lookup", type, field, returned:
|
|
9270
|
+
{ tool: "record_query", mode: "lookup", type, field, returned: pageItems(page).length },
|
|
9225
9271
|
"record_query lookup ok"
|
|
9226
9272
|
);
|
|
9227
9273
|
} else {
|
|
9228
|
-
|
|
9274
|
+
page = await client.records.listRecords({
|
|
9229
9275
|
type,
|
|
9230
9276
|
userId: args.userId,
|
|
9231
9277
|
orgId: args.orgId,
|
|
9232
9278
|
clientId: args.clientId,
|
|
9279
|
+
scope: args.scope,
|
|
9280
|
+
startFrom,
|
|
9233
9281
|
limit
|
|
9234
9282
|
});
|
|
9235
|
-
records = pageItems(page);
|
|
9236
9283
|
log.debug(
|
|
9237
|
-
{ tool: "record_query", mode: "list", type, limit, returned:
|
|
9284
|
+
{ tool: "record_query", mode: "list", type, limit, returned: pageItems(page).length },
|
|
9238
9285
|
"record_query list ok"
|
|
9239
9286
|
);
|
|
9240
9287
|
}
|
|
9288
|
+
const records = pageItems(page);
|
|
9289
|
+
const nextCursor = page.nextCursor ?? null;
|
|
9241
9290
|
return {
|
|
9242
|
-
content: [{ type: "text", text: JSON.stringify(records, null, 2) }]
|
|
9291
|
+
content: [{ type: "text", text: JSON.stringify({ data: records, nextCursor }, null, 2) }]
|
|
9243
9292
|
};
|
|
9244
9293
|
} catch (err) {
|
|
9245
9294
|
log.warn({ tool: "record_query", err: String(err) }, "record_query failed");
|
|
@@ -9362,7 +9411,10 @@ var inputSchema4 = {
|
|
|
9362
9411
|
folderId: z4.string().optional().describe("Group this record into a folder."),
|
|
9363
9412
|
userId: z4.string().optional().describe("Owning user id."),
|
|
9364
9413
|
orgId: z4.string().optional().describe("Owning organization id."),
|
|
9365
|
-
clientId: z4.string().optional().describe("Associated client id.")
|
|
9414
|
+
clientId: z4.string().optional().describe("Associated client id."),
|
|
9415
|
+
scopes: z4.array(z4.string()).optional().describe(
|
|
9416
|
+
'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.'
|
|
9417
|
+
)
|
|
9366
9418
|
};
|
|
9367
9419
|
var recordCreate = ({ client, log }) => ({
|
|
9368
9420
|
name: "record_create",
|
|
@@ -9382,7 +9434,8 @@ var recordCreate = ({ client, log }) => ({
|
|
|
9382
9434
|
folderId: args.folderId,
|
|
9383
9435
|
userId: args.userId,
|
|
9384
9436
|
orgId: args.orgId,
|
|
9385
|
-
clientId: args.clientId
|
|
9437
|
+
clientId: args.clientId,
|
|
9438
|
+
scopes: args.scopes
|
|
9386
9439
|
}
|
|
9387
9440
|
});
|
|
9388
9441
|
log.debug({ tool: "record_create", type, id: created.id }, "record_create ok");
|
|
@@ -9467,24 +9520,29 @@ var record_delete_default = recordDelete;
|
|
|
9467
9520
|
// src/tools/rag_ask.ts
|
|
9468
9521
|
import { z as z7 } from "zod";
|
|
9469
9522
|
var SEARCH_MCP_DEFAULT_LIMIT = 5;
|
|
9470
|
-
var SEARCH_MCP_MAX_LIMIT =
|
|
9523
|
+
var SEARCH_MCP_MAX_LIMIT = 50;
|
|
9471
9524
|
var inputSchema7 = {
|
|
9472
9525
|
query: z7.string().min(1, "query must be non-empty").describe("Question to ground against retrieved content."),
|
|
9473
9526
|
instructions: z7.string().optional().describe(
|
|
9474
9527
|
'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.'
|
|
9475
9528
|
),
|
|
9476
9529
|
model: z7.string().optional().describe(
|
|
9477
|
-
"Inference model alias (e.g. claude-haiku-4-5, claude-sonnet-
|
|
9530
|
+
"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."
|
|
9478
9531
|
),
|
|
9479
9532
|
search: z7.object({
|
|
9480
9533
|
mode: z7.enum(["HYBRID", "TEXT", "SEMANTIC"]).optional().describe("Retrieval mode. Default HYBRID."),
|
|
9481
|
-
limit: z7.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
|
|
9534
|
+
limit: z7.number().int().min(1).max(SEARCH_MCP_MAX_LIMIT).optional().describe(
|
|
9535
|
+
`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.`
|
|
9536
|
+
),
|
|
9482
9537
|
// Retrieval scoping — parity with hybrid_search so an agent can ground the
|
|
9483
9538
|
// answer on a specific owner / folder / type / metadata subset, not just the
|
|
9484
9539
|
// whole-tenant corpus.
|
|
9485
9540
|
userId: z7.string().optional().describe("Restrict retrieval to content owned by this user (Vectros UUID)."),
|
|
9486
9541
|
orgId: z7.string().optional().describe("Restrict retrieval to content owned by this org (Vectros UUID)."),
|
|
9487
9542
|
clientId: z7.string().optional().describe("Restrict retrieval to content tagged with this client (Vectros UUID)."),
|
|
9543
|
+
scope: z7.string().optional().describe(
|
|
9544
|
+
'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.'
|
|
9545
|
+
),
|
|
9488
9546
|
folderId: z7.string().optional().describe("Restrict retrieval to this exact folder."),
|
|
9489
9547
|
rootFolderId: z7.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
|
|
9490
9548
|
typeName: z7.string().optional().describe("Restrict record retrieval to this schema type."),
|
|
@@ -9502,7 +9560,7 @@ var inputSchema7 = {
|
|
|
9502
9560
|
var ragAsk = ({ client, log }) => ({
|
|
9503
9561
|
name: "rag_ask",
|
|
9504
9562
|
title: "RAG ask (corpus-wide)",
|
|
9505
|
-
description: "Ask a question grounded against
|
|
9563
|
+
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.",
|
|
9506
9564
|
inputSchema: inputSchema7,
|
|
9507
9565
|
handler: async (args, extra) => {
|
|
9508
9566
|
try {
|
|
@@ -9513,6 +9571,7 @@ var ragAsk = ({ client, log }) => ({
|
|
|
9513
9571
|
userId: searchArg.userId,
|
|
9514
9572
|
orgId: searchArg.orgId,
|
|
9515
9573
|
clientId: searchArg.clientId,
|
|
9574
|
+
scope: searchArg.scope,
|
|
9516
9575
|
folderId: searchArg.folderId,
|
|
9517
9576
|
rootFolderId: searchArg.rootFolderId,
|
|
9518
9577
|
typeName: searchArg.typeName,
|
|
@@ -9670,7 +9729,7 @@ var inputSchema9 = {
|
|
|
9670
9729
|
var listSchemas = ({ client, log }) => ({
|
|
9671
9730
|
name: "list_schemas",
|
|
9672
9731
|
title: "List record schemas",
|
|
9673
|
-
description: 'List the structured-record schema catalog for
|
|
9732
|
+
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.',
|
|
9674
9733
|
inputSchema: inputSchema9,
|
|
9675
9734
|
handler: async (args) => {
|
|
9676
9735
|
const userId = args.userId;
|
|
@@ -9724,7 +9783,7 @@ function isNotAvailable(err) {
|
|
|
9724
9783
|
var documentGet = ({ client, log }) => ({
|
|
9725
9784
|
name: "document_get",
|
|
9726
9785
|
title: "Get document metadata (and optionally text)",
|
|
9727
|
-
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).
|
|
9786
|
+
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`.",
|
|
9728
9787
|
inputSchema: inputSchema10,
|
|
9729
9788
|
handler: async (args) => {
|
|
9730
9789
|
const documentId = args.documentId;
|
|
@@ -9748,7 +9807,7 @@ var documentGet = ({ client, log }) => ({
|
|
|
9748
9807
|
if (isNotAvailable(textErr)) {
|
|
9749
9808
|
log.debug(
|
|
9750
9809
|
{ tool: "document_get", id },
|
|
9751
|
-
"document_get text unavailable (
|
|
9810
|
+
"document_get text unavailable (not retained \u2014 storeText:false upload)"
|
|
9752
9811
|
);
|
|
9753
9812
|
extra = { ...extra, textAvailable: false };
|
|
9754
9813
|
} else {
|
|
@@ -9792,7 +9851,7 @@ var document_get_default = documentGet;
|
|
|
9792
9851
|
// src/tools/document_query.ts
|
|
9793
9852
|
import { z as z11 } from "zod";
|
|
9794
9853
|
var MCP_DEFAULT_LIMIT3 = 3;
|
|
9795
|
-
var MCP_MAX_LIMIT3 =
|
|
9854
|
+
var MCP_MAX_LIMIT3 = 100;
|
|
9796
9855
|
var inputSchema11 = {
|
|
9797
9856
|
// Lookup-mode args — provide `field` plus EXACTLY ONE of: value | from+to | prefix.
|
|
9798
9857
|
field: z11.string().optional().describe("Lookup mode: name of the lookup-indexed field to query by."),
|
|
@@ -9809,14 +9868,18 @@ var inputSchema11 = {
|
|
|
9809
9868
|
userId: z11.string().optional().describe("List mode: scope to documents owned by this user."),
|
|
9810
9869
|
orgId: z11.string().optional().describe("List mode: scope to documents belonging to this org."),
|
|
9811
9870
|
clientId: z11.string().optional().describe("List mode: scope to documents associated with this client."),
|
|
9871
|
+
scope: z11.string().optional().describe(
|
|
9872
|
+
'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.'
|
|
9873
|
+
),
|
|
9874
|
+
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."),
|
|
9812
9875
|
limit: z11.number().int().min(1).max(MCP_MAX_LIMIT3).optional().describe(
|
|
9813
|
-
`Max documents
|
|
9876
|
+
`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.`
|
|
9814
9877
|
)
|
|
9815
9878
|
};
|
|
9816
9879
|
var documentQuery = ({ client, log }) => ({
|
|
9817
9880
|
name: "document_query",
|
|
9818
9881
|
title: "Document query (list or lookup)",
|
|
9819
|
-
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
|
|
9882
|
+
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.",
|
|
9820
9883
|
inputSchema: inputSchema11,
|
|
9821
9884
|
handler: async (args) => {
|
|
9822
9885
|
const limit = args.limit ?? MCP_DEFAULT_LIMIT3;
|
|
@@ -9826,8 +9889,9 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9826
9889
|
const to = args.to;
|
|
9827
9890
|
const prefix = args.prefix;
|
|
9828
9891
|
const type = args.type;
|
|
9892
|
+
const startFrom = args.startFrom;
|
|
9829
9893
|
try {
|
|
9830
|
-
let
|
|
9894
|
+
let page;
|
|
9831
9895
|
if (field) {
|
|
9832
9896
|
const hasEquality = value !== void 0;
|
|
9833
9897
|
const hasRange = from !== void 0 || to !== void 0;
|
|
@@ -9856,7 +9920,7 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9856
9920
|
new Error("lookup requires 'type' (the document's bound schema type) alongside 'field'.")
|
|
9857
9921
|
);
|
|
9858
9922
|
}
|
|
9859
|
-
|
|
9923
|
+
page = await client.documents.lookupDocumentsByBody({
|
|
9860
9924
|
type,
|
|
9861
9925
|
field,
|
|
9862
9926
|
value,
|
|
@@ -9864,28 +9928,31 @@ var documentQuery = ({ client, log }) => ({
|
|
|
9864
9928
|
to,
|
|
9865
9929
|
prefix,
|
|
9866
9930
|
order: args.order,
|
|
9931
|
+
startFrom,
|
|
9867
9932
|
limit
|
|
9868
9933
|
});
|
|
9869
|
-
documents = pageItems(page);
|
|
9870
9934
|
log.debug(
|
|
9871
|
-
{ tool: "document_query", mode: "lookup", type, field, returned:
|
|
9935
|
+
{ tool: "document_query", mode: "lookup", type, field, returned: pageItems(page).length },
|
|
9872
9936
|
"document_query lookup ok"
|
|
9873
9937
|
);
|
|
9874
9938
|
} else {
|
|
9875
|
-
|
|
9939
|
+
page = await client.documents.listDocuments({
|
|
9876
9940
|
folderId: args.folderId,
|
|
9877
9941
|
userId: args.userId,
|
|
9878
9942
|
orgId: args.orgId,
|
|
9879
9943
|
clientId: args.clientId,
|
|
9944
|
+
scope: args.scope,
|
|
9945
|
+
startFrom,
|
|
9880
9946
|
limit
|
|
9881
9947
|
});
|
|
9882
|
-
documents = pageItems(page);
|
|
9883
9948
|
log.debug(
|
|
9884
|
-
{ tool: "document_query", mode: "list", limit, returned:
|
|
9949
|
+
{ tool: "document_query", mode: "list", limit, returned: pageItems(page).length },
|
|
9885
9950
|
"document_query list ok"
|
|
9886
9951
|
);
|
|
9887
9952
|
}
|
|
9888
|
-
|
|
9953
|
+
const documents = pageItems(page);
|
|
9954
|
+
const nextCursor = page.nextCursor ?? null;
|
|
9955
|
+
return { content: [{ type: "text", text: JSON.stringify({ data: documents, nextCursor }, null, 2) }] };
|
|
9889
9956
|
} catch (err) {
|
|
9890
9957
|
log.warn({ tool: "document_query", err: String(err) }, "document_query failed");
|
|
9891
9958
|
return toolError("document_query", err);
|
|
@@ -9910,7 +9977,6 @@ var inputSchema12 = {
|
|
|
9910
9977
|
"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."
|
|
9911
9978
|
),
|
|
9912
9979
|
folderId: z12.string().optional().describe("Move the document into this folder (cannot be cleared once set)."),
|
|
9913
|
-
storeText: z12.boolean().optional().describe("Whether the raw text is stored + retrievable."),
|
|
9914
9980
|
status: z12.enum(["ACTIVE", "ARCHIVED"]).optional().describe(
|
|
9915
9981
|
"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."
|
|
9916
9982
|
),
|
|
@@ -9934,7 +10000,6 @@ var documentUpdate = ({ client, log }) => ({
|
|
|
9934
10000
|
const fields = args.fields;
|
|
9935
10001
|
const text = args.text;
|
|
9936
10002
|
const folderId = args.folderId;
|
|
9937
|
-
const storeText = args.storeText;
|
|
9938
10003
|
const status = args.status;
|
|
9939
10004
|
const userId = args.userId;
|
|
9940
10005
|
const orgId = args.orgId;
|
|
@@ -9948,7 +10013,6 @@ var documentUpdate = ({ client, log }) => ({
|
|
|
9948
10013
|
if (fields !== void 0) body.payload = fields;
|
|
9949
10014
|
if (text !== void 0) body.text = text;
|
|
9950
10015
|
if (folderId !== void 0) body.folderId = folderId;
|
|
9951
|
-
if (storeText !== void 0) body.storeText = storeText;
|
|
9952
10016
|
if (status !== void 0) body.status = status;
|
|
9953
10017
|
if (userId !== void 0) body.userId = userId;
|
|
9954
10018
|
if (orgId !== void 0) body.orgId = orgId;
|
|
@@ -9997,7 +10061,7 @@ var document_delete_default = documentDelete;
|
|
|
9997
10061
|
// src/tools/folder_query.ts
|
|
9998
10062
|
import { z as z14 } from "zod";
|
|
9999
10063
|
var MCP_DEFAULT_LIMIT4 = 10;
|
|
10000
|
-
var MCP_MAX_LIMIT4 =
|
|
10064
|
+
var MCP_MAX_LIMIT4 = 100;
|
|
10001
10065
|
var inputSchema14 = {
|
|
10002
10066
|
id: z14.string().optional().describe("Get mode: fetch this single folder by id."),
|
|
10003
10067
|
parentId: z14.string().optional().describe("List mode: direct children of this folder (tree navigation). Omit for a flat tenant list."),
|
|
@@ -10005,12 +10069,14 @@ var inputSchema14 = {
|
|
|
10005
10069
|
orgId: z14.string().optional().describe("List mode: scope to folders belonging to this org."),
|
|
10006
10070
|
clientId: z14.string().optional().describe("List mode: scope to folders associated with this client."),
|
|
10007
10071
|
startFrom: z14.string().optional().describe("List mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
|
|
10008
|
-
limit: z14.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
|
|
10072
|
+
limit: z14.number().int().min(1).max(MCP_MAX_LIMIT4).optional().describe(
|
|
10073
|
+
`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\`.`
|
|
10074
|
+
)
|
|
10009
10075
|
};
|
|
10010
10076
|
var folderQuery = ({ client, log }) => ({
|
|
10011
10077
|
name: "folder_query",
|
|
10012
10078
|
title: "Folder query (list or get)",
|
|
10013
|
-
description: "Read folders. Two modes:\n \u2022 Get: pass `id` \u2192 returns the single folder.\n \u2022 List: omit `id`; pass `parentId` for a folder's direct children (tree navigation) or omit for a flat tenant list. Optionally filter by `userId`/`orgId`/`clientId`.\nList mode returns `{ data, nextCursor }` (default 10, max
|
|
10079
|
+
description: "Read folders. Two modes:\n \u2022 Get: pass `id` \u2192 returns the single folder.\n \u2022 List: omit `id`; pass `parentId` for a folder's direct children (tree navigation) or omit for a flat tenant list. Optionally filter by `userId`/`orgId`/`clientId`.\nList mode returns `{ data, nextCursor }` (default 10, max 100 per page \u2014 raise it in one call when you can accept the larger page); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page through all folders. Get mode returns the single folder object.",
|
|
10014
10080
|
inputSchema: inputSchema14,
|
|
10015
10081
|
handler: async (args) => {
|
|
10016
10082
|
const id = args.id;
|
|
@@ -10050,7 +10116,10 @@ var inputSchema15 = {
|
|
|
10050
10116
|
slug: z15.string().optional().describe("Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. Derived from name when omitted."),
|
|
10051
10117
|
userId: z15.string().optional().describe("Owning user (Vectros UUID)."),
|
|
10052
10118
|
orgId: z15.string().optional().describe("Owning org (Vectros UUID)."),
|
|
10053
|
-
clientId: z15.string().optional().describe("Associated client (Vectros UUID).")
|
|
10119
|
+
clientId: z15.string().optional().describe("Associated client (Vectros UUID)."),
|
|
10120
|
+
scopes: z15.array(z15.string()).optional().describe(
|
|
10121
|
+
'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.'
|
|
10122
|
+
)
|
|
10054
10123
|
};
|
|
10055
10124
|
var folderCreate = ({ client, log }) => ({
|
|
10056
10125
|
name: "folder_create",
|
|
@@ -10067,7 +10136,8 @@ var folderCreate = ({ client, log }) => ({
|
|
|
10067
10136
|
slug: args.slug,
|
|
10068
10137
|
userId: args.userId,
|
|
10069
10138
|
orgId: args.orgId,
|
|
10070
|
-
clientId: args.clientId
|
|
10139
|
+
clientId: args.clientId,
|
|
10140
|
+
scopes: args.scopes
|
|
10071
10141
|
}
|
|
10072
10142
|
});
|
|
10073
10143
|
log.debug({ tool: "folder_create", id: folder.id, name: folder.name }, "folder_create ok");
|
|
@@ -10268,7 +10338,7 @@ var inputSchema19 = {
|
|
|
10268
10338
|
// Inline text mode (mutually exclusive with filePath):
|
|
10269
10339
|
text: z18.string().min(1).optional().describe("Text body to ingest inline. Mutually exclusive with filePath. Either text or filePath is required."),
|
|
10270
10340
|
storeText: z18.boolean().optional().describe(
|
|
10271
|
-
"
|
|
10341
|
+
"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."
|
|
10272
10342
|
),
|
|
10273
10343
|
// File upload mode (mutually exclusive with text; stdio-transport-only):
|
|
10274
10344
|
filePath: z18.string().min(1).optional().describe(
|
|
@@ -10279,12 +10349,15 @@ var inputSchema19 = {
|
|
|
10279
10349
|
folderId: z18.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
|
|
10280
10350
|
userId: z18.string().optional().describe("Owning user ID. Optional."),
|
|
10281
10351
|
orgId: z18.string().optional().describe("Owning org ID. Optional."),
|
|
10282
|
-
clientId: z18.string().optional().describe("Owning client ID. Optional.")
|
|
10352
|
+
clientId: z18.string().optional().describe("Owning client ID. Optional."),
|
|
10353
|
+
scopes: z18.array(z18.string()).optional().describe(
|
|
10354
|
+
"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."
|
|
10355
|
+
)
|
|
10283
10356
|
};
|
|
10284
10357
|
var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
10285
10358
|
name: "document_ingest",
|
|
10286
10359
|
title: "Ingest a new document (text body or file upload)",
|
|
10287
|
-
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
|
|
10360
|
+
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.",
|
|
10288
10361
|
inputSchema: inputSchema19,
|
|
10289
10362
|
handler: async (args) => {
|
|
10290
10363
|
const title = args.title;
|
|
@@ -10313,6 +10386,14 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10313
10386
|
)
|
|
10314
10387
|
);
|
|
10315
10388
|
}
|
|
10389
|
+
if (filePath && args.scopes !== void 0) {
|
|
10390
|
+
return toolError(
|
|
10391
|
+
"document_ingest",
|
|
10392
|
+
new Error(
|
|
10393
|
+
"`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)."
|
|
10394
|
+
)
|
|
10395
|
+
);
|
|
10396
|
+
}
|
|
10316
10397
|
const common = {
|
|
10317
10398
|
folderId: args.folderId,
|
|
10318
10399
|
payload: args.payload,
|
|
@@ -10324,14 +10405,15 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10324
10405
|
};
|
|
10325
10406
|
try {
|
|
10326
10407
|
if (text) {
|
|
10327
|
-
const storeText = args.storeText ?? true;
|
|
10328
10408
|
const result = await client.documents.ingestDocument({
|
|
10329
10409
|
upsert,
|
|
10330
10410
|
body: {
|
|
10331
10411
|
title,
|
|
10332
10412
|
text,
|
|
10333
10413
|
indexMode,
|
|
10334
|
-
|
|
10414
|
+
// Scope ownership is a TEXT-mode contract only (the upload-init
|
|
10415
|
+
// endpoint has no scopes field — see the gate above).
|
|
10416
|
+
scopes: args.scopes,
|
|
10335
10417
|
...common
|
|
10336
10418
|
}
|
|
10337
10419
|
});
|
|
@@ -10362,11 +10444,13 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
|
|
|
10362
10444
|
}
|
|
10363
10445
|
const fileName = basename(safePath);
|
|
10364
10446
|
const fileType = args.fileType ?? inferMimeType(safePath);
|
|
10447
|
+
const storeTextFile = args.storeText ?? true;
|
|
10365
10448
|
const upload = await client.documents.uploadDocument({
|
|
10366
10449
|
upsert,
|
|
10367
10450
|
fileName,
|
|
10368
10451
|
fileType,
|
|
10369
10452
|
indexMode,
|
|
10453
|
+
storeText: storeTextFile,
|
|
10370
10454
|
...common
|
|
10371
10455
|
});
|
|
10372
10456
|
const uploadUrl = upload?.uploadUrl;
|
|
@@ -10422,7 +10506,7 @@ var document_ingest_default = documentIngest;
|
|
|
10422
10506
|
// src/tools/lookup_principal.ts
|
|
10423
10507
|
import { z as z19 } from "zod";
|
|
10424
10508
|
var MCP_DEFAULT_LIMIT5 = 10;
|
|
10425
|
-
var MCP_MAX_LIMIT5 =
|
|
10509
|
+
var MCP_MAX_LIMIT5 = 100;
|
|
10426
10510
|
var inputSchema20 = {
|
|
10427
10511
|
kind: z19.enum(["user", "org", "client"]).describe("Which principal to look up: a user, an organization, or a client."),
|
|
10428
10512
|
externalId: z19.string().optional().describe(
|
|
@@ -10436,12 +10520,15 @@ var inputSchema20 = {
|
|
|
10436
10520
|
to: z19.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
|
|
10437
10521
|
prefix: z19.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
|
|
10438
10522
|
order: z19.enum(["asc", "desc"]).optional().describe("Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc`."),
|
|
10439
|
-
|
|
10523
|
+
startFrom: z19.string().optional().describe("Lookup mode: pagination cursor \u2014 pass the `nextCursor` from the previous page to fetch the next."),
|
|
10524
|
+
limit: z19.number().int().min(1).max(MCP_MAX_LIMIT5).optional().describe(
|
|
10525
|
+
`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\`.`
|
|
10526
|
+
)
|
|
10440
10527
|
};
|
|
10441
10528
|
var lookupPrincipal = ({ client, log }) => ({
|
|
10442
10529
|
name: "lookup_principal",
|
|
10443
10530
|
title: "Look up a user / org / client identity",
|
|
10444
|
-
description: "Resolve a user, org, or client identity. Two modes:\n \u2022 Resolve by your own id: pass `externalId` \u2192 the matching principal incl. its Vectros UUID (the id the ownership filters on record_query / hybrid_search / rag_ask expect).\n \u2022 Lookup by a schema field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Sensitive-field-safe. (If both `externalId` and a `field` lookup are given, `externalId` wins.) Returns
|
|
10531
|
+
description: "Resolve a user, org, or client identity. Two modes:\n \u2022 Resolve by your own id: pass `externalId` \u2192 the matching principal incl. its Vectros UUID (the id the ownership filters on record_query / hybrid_search / rag_ask expect).\n \u2022 Lookup by a schema field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Sensitive-field-safe. (If both `externalId` and a `field` lookup are given, `externalId` wins.) Returns a `{ data, nextCursor }` page (default 10, max 100 \u2014 raise it in one call when you can accept the payload); a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom`. Read-only \u2014 does not create or modify identities.",
|
|
10445
10532
|
inputSchema: inputSchema20,
|
|
10446
10533
|
handler: async (args) => {
|
|
10447
10534
|
const kind = args.kind;
|
|
@@ -10454,6 +10541,7 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10454
10541
|
const to = args.to;
|
|
10455
10542
|
const prefix = args.prefix;
|
|
10456
10543
|
const order = args.order;
|
|
10544
|
+
const startFrom = args.startFrom;
|
|
10457
10545
|
try {
|
|
10458
10546
|
let page;
|
|
10459
10547
|
if (externalId !== void 0) {
|
|
@@ -10491,7 +10579,7 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10491
10579
|
new Error("lookup requires 'type' (the identity schema's record type) alongside 'field'.")
|
|
10492
10580
|
);
|
|
10493
10581
|
}
|
|
10494
|
-
const req = { type, field, value, from, to, prefix, order, limit };
|
|
10582
|
+
const req = { type, field, value, from, to, prefix, order, startFrom, limit };
|
|
10495
10583
|
if (kind === "user") {
|
|
10496
10584
|
page = await client.identity.lookupUsers(req);
|
|
10497
10585
|
} else if (kind === "org") {
|
|
@@ -10507,7 +10595,8 @@ var lookupPrincipal = ({ client, log }) => ({
|
|
|
10507
10595
|
);
|
|
10508
10596
|
}
|
|
10509
10597
|
const principals = pageItems(page);
|
|
10510
|
-
|
|
10598
|
+
const nextCursor = page.nextCursor ?? null;
|
|
10599
|
+
return { content: [{ type: "text", text: JSON.stringify({ data: principals, nextCursor }, null, 2) }] };
|
|
10511
10600
|
} catch (err) {
|
|
10512
10601
|
log.warn({ tool: "lookup_principal", kind, err: String(err) }, "lookup_principal failed");
|
|
10513
10602
|
return toolError("lookup_principal", err);
|