@agent-vm/mcp-portal 0.0.69 → 0.0.71
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/README.md +42 -11
- package/dist/agent-bearer-token-DCtpDPCZ.js +59 -0
- package/dist/agent-bearer-token-DCtpDPCZ.js.map +1 -0
- package/dist/bin/mcp-portal.d.ts +28 -0
- package/dist/bin/mcp-portal.d.ts.map +1 -0
- package/dist/bin/mcp-portal.js +318 -0
- package/dist/bin/mcp-portal.js.map +1 -0
- package/dist/{catalog-types--gUGFPpN.d.ts → catalog-types-BVuB4Ynx.d.ts} +1 -1
- package/dist/{catalog-types--gUGFPpN.d.ts.map → catalog-types-BVuB4Ynx.d.ts.map} +1 -1
- package/dist/cli/index.d.ts +101 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/core/index.d.ts +40 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +5 -0
- package/dist/hmac-env-B4shpRRB.js +20 -0
- package/dist/hmac-env-B4shpRRB.js.map +1 -0
- package/dist/hmac-token-DBqWY3-w.js +100 -0
- package/dist/hmac-token-DBqWY3-w.js.map +1 -0
- package/dist/index.d.ts +5 -485
- package/dist/index.js +4 -5
- package/dist/mcp-proxy/index.d.ts +24 -0
- package/dist/mcp-proxy/index.d.ts.map +1 -0
- package/dist/mcp-proxy/index.js +2 -0
- package/dist/portal-auth/agent-bearer-token.d.ts +22 -0
- package/dist/portal-auth/agent-bearer-token.d.ts.map +1 -0
- package/dist/portal-auth/agent-bearer-token.js +2 -0
- package/dist/portal-auth/hmac-env.d.ts +6 -0
- package/dist/portal-auth/hmac-env.d.ts.map +1 -0
- package/dist/portal-auth/hmac-env.js +2 -0
- package/dist/portal-auth/hmac-token.d.ts +40 -0
- package/dist/portal-auth/hmac-token.d.ts.map +1 -0
- package/dist/portal-auth/hmac-token.js +2 -0
- package/dist/portal-config/index.d.ts +11 -0
- package/dist/portal-config/index.d.ts.map +1 -0
- package/dist/{tool-vm → portal-config}/index.js +2 -3
- package/dist/portal-core-CZQI7Ob6.d.ts +264 -0
- package/dist/portal-core-CZQI7Ob6.d.ts.map +1 -0
- package/dist/portal-core-Cgu714CL.js +416 -0
- package/dist/portal-core-Cgu714CL.js.map +1 -0
- package/dist/portal-session-DG2CUjIo.d.ts +184 -0
- package/dist/portal-session-DG2CUjIo.d.ts.map +1 -0
- package/dist/portal-tools-DKci1szO.js +528 -0
- package/dist/portal-tools-DKci1szO.js.map +1 -0
- package/dist/resolve-agent-identity-DnC_Pmnh.js +550 -0
- package/dist/resolve-agent-identity-DnC_Pmnh.js.map +1 -0
- package/dist/resolve-agent-identity-FQL02YdW.d.ts +81 -0
- package/dist/resolve-agent-identity-FQL02YdW.d.ts.map +1 -0
- package/dist/serve-command-D3SlETy_.js +358 -0
- package/dist/serve-command-D3SlETy_.js.map +1 -0
- package/dist/testing/fake-upstream-mcp-server.d.ts +5 -2
- package/dist/testing/fake-upstream-mcp-server.d.ts.map +1 -1
- package/dist/testing/fake-upstream-mcp-server.js +14 -4
- package/dist/testing/fake-upstream-mcp-server.js.map +1 -1
- package/dist/typescript-artifact-BVLt3Ifd.js +60 -0
- package/dist/typescript-artifact-BVLt3Ifd.js.map +1 -0
- package/dist/upstream-mcp-client-runtime-JlsfTm7_.js +760 -0
- package/dist/upstream-mcp-client-runtime-JlsfTm7_.js.map +1 -0
- package/dist/upstream-response-middleware-1MZnAD9C.d.ts +115 -0
- package/dist/upstream-response-middleware-1MZnAD9C.d.ts.map +1 -0
- package/dist/upstream-response-middleware-BjUWZ2G8.js +172 -0
- package/dist/upstream-response-middleware-BjUWZ2G8.js.map +1 -0
- package/dist/{index-BcI9c8sg.d.ts → zod-schema-loader-DLGQpYFD.d.ts} +3 -9
- package/dist/zod-schema-loader-DLGQpYFD.d.ts.map +1 -0
- package/dist/{typescript-artifact-BqU8okQy.js → zod-schema-loader-yNekKNpm.js} +85 -55
- package/dist/zod-schema-loader-yNekKNpm.js.map +1 -0
- package/package.json +30 -13
- package/dist/bin/agent-vm-mcp-portal.d.ts +0 -10
- package/dist/bin/agent-vm-mcp-portal.d.ts.map +0 -1
- package/dist/bin/agent-vm-mcp-portal.js +0 -56
- package/dist/bin/agent-vm-mcp-portal.js.map +0 -1
- package/dist/bin/portal-server.d.ts +0 -55
- package/dist/bin/portal-server.d.ts.map +0 -1
- package/dist/bin/portal-server.js +0 -289
- package/dist/bin/portal-server.js.map +0 -1
- package/dist/index-BcI9c8sg.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/tool-vm/index.d.ts +0 -2
- package/dist/tool-vm-ihnzDyjJ.js +0 -3
- package/dist/typescript-artifact-BqU8okQy.js.map +0 -1
- package/dist/upstream-mcp-client-runtime-DiBCBsDj.js +0 -1729
- package/dist/upstream-mcp-client-runtime-DiBCBsDj.js.map +0 -1
- package/dist/zod-schema-loader-CDDtoRE1.js +0 -90
- package/dist/zod-schema-loader-CDDtoRE1.js.map +0 -1
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
import { a as portalToolRecordSchema, r as encodeToolRef } from "./zod-schema-loader-yNekKNpm.js";
|
|
2
|
+
import { a as redactUpstreamCatalogValue, c as createToolSummary, d as portalAgentScopeKey, f as resolvePortalAccessPolicy, i as redactThrownError, o as redactUpstreamResponse } from "./upstream-response-middleware-BjUWZ2G8.js";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
5
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
6
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
7
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
8
|
+
import { normalizeHeaders } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
9
|
+
import { ToolSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
//#region src/search-index.ts
|
|
11
|
+
function collectSchemaText(value, parts) {
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
for (const item of value) collectSchemaText(item, parts);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (typeof value !== "object" || value === null) {
|
|
17
|
+
if (typeof value === "string") parts.push(value);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const [key, childValue] of Object.entries(value)) {
|
|
21
|
+
parts.push(key);
|
|
22
|
+
collectSchemaText(childValue, parts);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function normalizeSearchText(text) {
|
|
26
|
+
return text.toLowerCase().replace(/[_-]/g, " ");
|
|
27
|
+
}
|
|
28
|
+
function buildSearchText(tool) {
|
|
29
|
+
const parts = [
|
|
30
|
+
tool.namespace,
|
|
31
|
+
tool.toolName,
|
|
32
|
+
tool.title ?? "",
|
|
33
|
+
tool.description ?? ""
|
|
34
|
+
];
|
|
35
|
+
collectSchemaText(tool.inputSchema, parts);
|
|
36
|
+
if (tool.outputSchema) collectSchemaText(tool.outputSchema, parts);
|
|
37
|
+
return normalizeSearchText(parts.join(" "));
|
|
38
|
+
}
|
|
39
|
+
function propertiesFromSchema(schema) {
|
|
40
|
+
const properties = schema.properties;
|
|
41
|
+
if (typeof properties !== "object" || properties === null || Array.isArray(properties)) return [];
|
|
42
|
+
return Object.keys(properties).toSorted();
|
|
43
|
+
}
|
|
44
|
+
function createRelationshipHints(tool, relationships) {
|
|
45
|
+
const targetToolRef = createToolSummary(tool).toolRef;
|
|
46
|
+
return relationships.filter((relationship) => relationship.to.toolRef === targetToolRef).map((relationship) => relationship.field === void 0 ? {
|
|
47
|
+
reason: relationship.reason,
|
|
48
|
+
sourceToolRef: relationship.from.toolRef,
|
|
49
|
+
type: relationship.type
|
|
50
|
+
} : {
|
|
51
|
+
field: relationship.field,
|
|
52
|
+
reason: relationship.reason,
|
|
53
|
+
sourceToolRef: relationship.from.toolRef,
|
|
54
|
+
type: relationship.type
|
|
55
|
+
}).toSorted((left, right) => {
|
|
56
|
+
const typeOrder = left.type.localeCompare(right.type);
|
|
57
|
+
if (typeOrder !== 0) return typeOrder;
|
|
58
|
+
return left.sourceToolRef.localeCompare(right.sourceToolRef);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function scopedSkillText(toolRef, graph) {
|
|
62
|
+
if (!graph) return "";
|
|
63
|
+
return graph.skills.filter((skill) => skill.toolRefs.includes(toolRef)).map((skill) => [
|
|
64
|
+
skill.title,
|
|
65
|
+
skill.description ?? "",
|
|
66
|
+
...skill.tags
|
|
67
|
+
].join(" ")).join(" ");
|
|
68
|
+
}
|
|
69
|
+
function scoreEntry(entry, terms) {
|
|
70
|
+
let score = 0;
|
|
71
|
+
for (const term of terms) if (entry.searchText.includes(term)) score += 1;
|
|
72
|
+
return score;
|
|
73
|
+
}
|
|
74
|
+
function compareSummaries(left, right) {
|
|
75
|
+
const namespaceOrder = left.namespace.localeCompare(right.namespace);
|
|
76
|
+
if (namespaceOrder !== 0) return namespaceOrder;
|
|
77
|
+
return left.toolName.localeCompare(right.toolName);
|
|
78
|
+
}
|
|
79
|
+
function withRelationshipHints(summary, relationshipHints) {
|
|
80
|
+
if (relationshipHints.length === 0) return summary;
|
|
81
|
+
return {
|
|
82
|
+
...summary.description !== void 0 ? { description: summary.description } : {},
|
|
83
|
+
input: summary.input,
|
|
84
|
+
namespace: summary.namespace,
|
|
85
|
+
...summary.output !== void 0 ? { output: summary.output } : {},
|
|
86
|
+
relationshipHints,
|
|
87
|
+
safety: summary.safety,
|
|
88
|
+
...summary.title !== void 0 ? { title: summary.title } : {},
|
|
89
|
+
toolName: summary.toolName,
|
|
90
|
+
toolRef: summary.toolRef
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function withSchemaFieldMatches(summary, schemaFieldMatches) {
|
|
94
|
+
if (schemaFieldMatches.length === 0) return summary;
|
|
95
|
+
return {
|
|
96
|
+
...summary.description !== void 0 ? { description: summary.description } : {},
|
|
97
|
+
input: summary.input,
|
|
98
|
+
namespace: summary.namespace,
|
|
99
|
+
...summary.output !== void 0 ? { output: summary.output } : {},
|
|
100
|
+
...summary.relationshipHints !== void 0 ? { relationshipHints: summary.relationshipHints } : {},
|
|
101
|
+
safety: summary.safety,
|
|
102
|
+
schemaFieldMatches,
|
|
103
|
+
...summary.title !== void 0 ? { title: summary.title } : {},
|
|
104
|
+
toolName: summary.toolName,
|
|
105
|
+
toolRef: summary.toolRef
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function createSearchIndex(tools, graph) {
|
|
109
|
+
const entries = tools.map((tool) => portalToolRecordSchema.parse(tool)).map((tool) => {
|
|
110
|
+
const summary = createToolSummary(tool);
|
|
111
|
+
const relationshipHints = createRelationshipHints(tool, graph?.relationships ?? []);
|
|
112
|
+
const inputFields = propertiesFromSchema(tool.inputSchema);
|
|
113
|
+
const relationText = relationshipHints.map((hint) => [
|
|
114
|
+
hint.field ?? "",
|
|
115
|
+
hint.reason,
|
|
116
|
+
hint.sourceToolRef,
|
|
117
|
+
hint.type
|
|
118
|
+
].join(" ")).join(" ");
|
|
119
|
+
const skillText = scopedSkillText(summary.toolRef, graph);
|
|
120
|
+
return {
|
|
121
|
+
inputFields,
|
|
122
|
+
relationshipHints,
|
|
123
|
+
searchText: normalizeSearchText([
|
|
124
|
+
buildSearchText(tool),
|
|
125
|
+
relationText,
|
|
126
|
+
skillText
|
|
127
|
+
].join(" ")),
|
|
128
|
+
summary: withRelationshipHints(summary, relationshipHints)
|
|
129
|
+
};
|
|
130
|
+
}).toSorted((left, right) => compareSummaries(left.summary, right.summary));
|
|
131
|
+
return { search(query) {
|
|
132
|
+
const limit = Math.max(0, Math.floor(query.limit));
|
|
133
|
+
const namespaceFilter = new Set(query.namespaces ?? []);
|
|
134
|
+
const terms = normalizeSearchText(query.query ?? "").split(/\s+/).map((term) => term.trim()).filter(Boolean);
|
|
135
|
+
return { results: entries.filter((entry) => namespaceFilter.size === 0 || namespaceFilter.has(entry.summary.namespace)).map((entry) => ({
|
|
136
|
+
entry,
|
|
137
|
+
score: terms.length === 0 ? 1 : scoreEntry(entry, terms)
|
|
138
|
+
})).filter(({ score }) => score > 0).toSorted((left, right) => {
|
|
139
|
+
if (right.score !== left.score) return right.score - left.score;
|
|
140
|
+
return compareSummaries(left.entry.summary, right.entry.summary);
|
|
141
|
+
}).slice(0, limit).map(({ entry }) => {
|
|
142
|
+
const schemaFieldMatches = entry.inputFields.filter((fieldName) => terms.some((term) => normalizeSearchText(fieldName).includes(term))).toSorted();
|
|
143
|
+
return withSchemaFieldMatches(entry.summary, schemaFieldMatches);
|
|
144
|
+
}) };
|
|
145
|
+
} };
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/tool-graph.ts
|
|
149
|
+
const genericLinkFields = new Set([
|
|
150
|
+
"id",
|
|
151
|
+
"name",
|
|
152
|
+
"title",
|
|
153
|
+
"url",
|
|
154
|
+
"uri"
|
|
155
|
+
]);
|
|
156
|
+
function compareCodePoint(left, right) {
|
|
157
|
+
if (left < right) return -1;
|
|
158
|
+
if (left > right) return 1;
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
function objectPropertiesFromSchema(schema) {
|
|
162
|
+
if (!schema) return [];
|
|
163
|
+
const properties = schema.properties;
|
|
164
|
+
if (typeof properties !== "object" || properties === null || Array.isArray(properties)) return [];
|
|
165
|
+
return Object.keys(properties).toSorted();
|
|
166
|
+
}
|
|
167
|
+
function collectEntityNames(schema) {
|
|
168
|
+
if (!schema) return [];
|
|
169
|
+
const names = [];
|
|
170
|
+
const schemaId = schema.$id;
|
|
171
|
+
const title = schema.title;
|
|
172
|
+
const entityName = schema.entityName;
|
|
173
|
+
if (typeof schemaId === "string" && schemaId.length > 0) names.push(schemaId);
|
|
174
|
+
if (typeof title === "string" && title.length > 0) names.push(title);
|
|
175
|
+
if (typeof entityName === "string" && entityName.length > 0) names.push(entityName);
|
|
176
|
+
return names.map((name) => name.toLowerCase()).toSorted();
|
|
177
|
+
}
|
|
178
|
+
function createGraphTool(tool) {
|
|
179
|
+
const record = portalToolRecordSchema.parse(tool);
|
|
180
|
+
const toolRef = encodeToolRef({
|
|
181
|
+
namespace: record.namespace,
|
|
182
|
+
toolName: record.toolName
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
entityNames: [...collectEntityNames(record.inputSchema), ...collectEntityNames(record.outputSchema)].toSorted(),
|
|
186
|
+
inputFields: objectPropertiesFromSchema(record.inputSchema),
|
|
187
|
+
outputFields: objectPropertiesFromSchema(record.outputSchema),
|
|
188
|
+
record,
|
|
189
|
+
ref: {
|
|
190
|
+
namespace: record.namespace,
|
|
191
|
+
toolName: record.toolName,
|
|
192
|
+
toolRef
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function sharesEntity(left, right) {
|
|
197
|
+
const rightEntities = new Set(right.entityNames);
|
|
198
|
+
return left.entityNames.some((entityName) => rightEntities.has(entityName));
|
|
199
|
+
}
|
|
200
|
+
function shouldLinkField(field, fromTool, toTool) {
|
|
201
|
+
if (!genericLinkFields.has(field.toLowerCase())) return true;
|
|
202
|
+
return fromTool.record.namespace === toTool.record.namespace && sharesEntity(fromTool, toTool);
|
|
203
|
+
}
|
|
204
|
+
function createSchemaRelationships(tools) {
|
|
205
|
+
const relationships = [];
|
|
206
|
+
for (const fromTool of tools) for (const toTool of tools) {
|
|
207
|
+
if (fromTool.ref.toolRef === toTool.ref.toolRef) continue;
|
|
208
|
+
const inputFields = new Set(toTool.inputFields);
|
|
209
|
+
for (const field of fromTool.outputFields) {
|
|
210
|
+
if (!inputFields.has(field) || !shouldLinkField(field, fromTool, toTool)) continue;
|
|
211
|
+
relationships.push({
|
|
212
|
+
field,
|
|
213
|
+
from: fromTool.ref,
|
|
214
|
+
reason: `Output field "${field}" matches input field "${field}".`,
|
|
215
|
+
to: toTool.ref,
|
|
216
|
+
type: "schema-field"
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return relationships;
|
|
221
|
+
}
|
|
222
|
+
function createEntityRelationships(tools) {
|
|
223
|
+
const relationships = [];
|
|
224
|
+
for (const fromTool of tools) for (const toTool of tools) {
|
|
225
|
+
if (fromTool.ref.toolRef === toTool.ref.toolRef || !sharesEntity(fromTool, toTool)) continue;
|
|
226
|
+
relationships.push({
|
|
227
|
+
from: fromTool.ref,
|
|
228
|
+
reason: "Tools declare a shared schema entity through title, $id, or entityName.",
|
|
229
|
+
to: toTool.ref,
|
|
230
|
+
type: "entity"
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
return relationships;
|
|
234
|
+
}
|
|
235
|
+
function createSkillEntries(skills, allowedToolRefs) {
|
|
236
|
+
const entries = [];
|
|
237
|
+
for (const skill of skills) {
|
|
238
|
+
const scopedToolRefs = skill.toolRefs.filter((toolRef) => allowedToolRefs.has(toolRef)).toSorted();
|
|
239
|
+
if (scopedToolRefs.length === 0) continue;
|
|
240
|
+
entries.push({
|
|
241
|
+
...skill.description !== void 0 ? { description: skill.description } : {},
|
|
242
|
+
tags: [...skill.tags ?? []].toSorted(),
|
|
243
|
+
title: skill.title,
|
|
244
|
+
toolRefs: scopedToolRefs
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return entries.toSorted((left, right) => left.title.localeCompare(right.title));
|
|
248
|
+
}
|
|
249
|
+
function createSkillRelationships(skills, toolsByRef) {
|
|
250
|
+
const relationships = [];
|
|
251
|
+
for (const skill of skills) for (const fromToolRef of skill.toolRefs) for (const toToolRef of skill.toolRefs) {
|
|
252
|
+
if (fromToolRef === toToolRef) continue;
|
|
253
|
+
const fromTool = toolsByRef.get(fromToolRef);
|
|
254
|
+
const toTool = toolsByRef.get(toToolRef);
|
|
255
|
+
if (!fromTool || !toTool) continue;
|
|
256
|
+
relationships.push({
|
|
257
|
+
from: fromTool.ref,
|
|
258
|
+
reason: `Both tools are referenced by skill "${skill.title}".`,
|
|
259
|
+
to: toTool.ref,
|
|
260
|
+
type: "skill"
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return relationships;
|
|
264
|
+
}
|
|
265
|
+
function compareRelationships(left, right) {
|
|
266
|
+
const typeOrder = compareCodePoint(left.type, right.type);
|
|
267
|
+
if (typeOrder !== 0) return typeOrder;
|
|
268
|
+
const targetOrder = compareCodePoint(left.to.toolRef, right.to.toolRef);
|
|
269
|
+
if (targetOrder !== 0) return targetOrder;
|
|
270
|
+
return compareCodePoint(left.from.toolRef, right.from.toolRef);
|
|
271
|
+
}
|
|
272
|
+
function buildToolGraph(input) {
|
|
273
|
+
const tools = input.tools.map((tool) => createGraphTool(tool)).toSorted((left, right) => compareCodePoint(left.ref.toolRef, right.ref.toolRef));
|
|
274
|
+
const allowedToolRefs = new Set(tools.map((tool) => tool.ref.toolRef));
|
|
275
|
+
const skills = createSkillEntries(input.skills ?? [], allowedToolRefs);
|
|
276
|
+
const toolsByRef = new Map(tools.map((tool) => [tool.ref.toolRef, tool]));
|
|
277
|
+
return {
|
|
278
|
+
relationships: [
|
|
279
|
+
...createEntityRelationships(tools),
|
|
280
|
+
...createSchemaRelationships(tools),
|
|
281
|
+
...createSkillRelationships(skills, toolsByRef)
|
|
282
|
+
].toSorted(compareRelationships),
|
|
283
|
+
skills
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
//#endregion
|
|
287
|
+
//#region src/portal-session.ts
|
|
288
|
+
function isHiddenTool(tool, hiddenTools) {
|
|
289
|
+
return hiddenTools.some((hiddenTool) => hiddenTool.namespace === tool.namespace && hiddenTool.toolName === tool.toolName);
|
|
290
|
+
}
|
|
291
|
+
function isEnabledTool(tool, enabledTools) {
|
|
292
|
+
if (enabledTools.length === 0) return true;
|
|
293
|
+
return enabledTools.some((enabledTool) => enabledTool.namespace === tool.namespace && enabledTool.toolName === tool.toolName);
|
|
294
|
+
}
|
|
295
|
+
function portalToolFromMcpTool(namespace, tool) {
|
|
296
|
+
return portalToolRecordSchema.parse({
|
|
297
|
+
...tool.annotations !== void 0 ? { annotations: tool.annotations } : {},
|
|
298
|
+
...tool.description !== void 0 ? { description: tool.description } : {},
|
|
299
|
+
inputSchema: tool.inputSchema,
|
|
300
|
+
namespace,
|
|
301
|
+
...tool.outputSchema !== void 0 ? { outputSchema: tool.outputSchema } : {},
|
|
302
|
+
...tool.title !== void 0 ? { title: tool.title } : {},
|
|
303
|
+
toolName: tool.name
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
function createSourceHash(tools) {
|
|
307
|
+
return createHash("sha256").update(JSON.stringify(tools)).digest("hex");
|
|
308
|
+
}
|
|
309
|
+
function messageFromError(error) {
|
|
310
|
+
return error instanceof Error ? error.message : String(error);
|
|
311
|
+
}
|
|
312
|
+
function createPortalSessionManager(options) {
|
|
313
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
314
|
+
const agentScopeGenerations = /* @__PURE__ */ new Map();
|
|
315
|
+
const getNow = options.now ?? (() => Date.now());
|
|
316
|
+
function generationForAgentScope(agentScopeId) {
|
|
317
|
+
return agentScopeGenerations.get(agentScopeId) ?? 0;
|
|
318
|
+
}
|
|
319
|
+
function incrementAgentScopeGeneration(agentScopeId) {
|
|
320
|
+
agentScopeGenerations.set(agentScopeId, generationForAgentScope(agentScopeId) + 1);
|
|
321
|
+
}
|
|
322
|
+
function generationForScope(scopeKey) {
|
|
323
|
+
const agentScopeId = scopeKey.split("\n", 1)[0] ?? scopeKey;
|
|
324
|
+
return (agentScopeGenerations.get(scopeKey) ?? 0) + generationForAgentScope(agentScopeId);
|
|
325
|
+
}
|
|
326
|
+
function incrementScopeGeneration(scopeKey) {
|
|
327
|
+
agentScopeGenerations.set(scopeKey, generationForScope(scopeKey) + 1);
|
|
328
|
+
}
|
|
329
|
+
async function buildSession(identity) {
|
|
330
|
+
const policy = resolvePortalAccessPolicy({
|
|
331
|
+
config: options.accessPolicy,
|
|
332
|
+
identity,
|
|
333
|
+
upstreamNamespaces: options.upstreamNamespaces
|
|
334
|
+
});
|
|
335
|
+
const tools = [];
|
|
336
|
+
const discoveryFailures = [...options.discoveryFailures ?? []];
|
|
337
|
+
const allowedNamespaces = policy.allowedNamespaces;
|
|
338
|
+
const namespaceToolGroups = await Promise.allSettled(allowedNamespaces.map(async (namespace) => ({
|
|
339
|
+
mcpTools: await options.runtime.listTools({
|
|
340
|
+
agentScopeId: portalAgentScopeKey(identity),
|
|
341
|
+
namespace
|
|
342
|
+
}),
|
|
343
|
+
namespace
|
|
344
|
+
})));
|
|
345
|
+
for (const [index, namespaceToolGroup] of namespaceToolGroups.entries()) {
|
|
346
|
+
if (namespaceToolGroup.status === "rejected") {
|
|
347
|
+
const namespace = allowedNamespaces[index] ?? "unknown";
|
|
348
|
+
discoveryFailures.push({
|
|
349
|
+
message: messageFromError(namespaceToolGroup.reason),
|
|
350
|
+
namespace
|
|
351
|
+
});
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const { mcpTools, namespace } = namespaceToolGroup.value;
|
|
355
|
+
for (const mcpTool of mcpTools) {
|
|
356
|
+
const portalTool = portalToolFromMcpTool(namespace, mcpTool);
|
|
357
|
+
if (isEnabledTool(portalTool, policy.enabledTools) && !isHiddenTool(portalTool, policy.hiddenTools)) tools.push(portalTool);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const sortedTools = tools.toSorted((left, right) => {
|
|
361
|
+
const namespaceOrder = left.namespace.localeCompare(right.namespace);
|
|
362
|
+
return namespaceOrder === 0 ? left.toolName.localeCompare(right.toolName) : namespaceOrder;
|
|
363
|
+
});
|
|
364
|
+
const graph = buildToolGraph({
|
|
365
|
+
skills: options.skills ?? [],
|
|
366
|
+
tools: sortedTools
|
|
367
|
+
});
|
|
368
|
+
return {
|
|
369
|
+
catalog: {
|
|
370
|
+
agentScopeId: identity.agentScopeId,
|
|
371
|
+
discoveryFailures,
|
|
372
|
+
generatedAt: new Date(getNow()).toISOString(),
|
|
373
|
+
sourceHash: createSourceHash(sortedTools),
|
|
374
|
+
tools: sortedTools
|
|
375
|
+
},
|
|
376
|
+
graph,
|
|
377
|
+
identity,
|
|
378
|
+
searchIndex: createSearchIndex(sortedTools, graph)
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
async getSession(identity) {
|
|
383
|
+
const key = portalAgentScopeKey(identity);
|
|
384
|
+
const now = getNow();
|
|
385
|
+
const cached = sessions.get(key);
|
|
386
|
+
if (cached && cached.expiresAt > now) return cached.session;
|
|
387
|
+
const generation = generationForScope(key);
|
|
388
|
+
const session = await buildSession(identity);
|
|
389
|
+
if (generationForScope(key) === generation && session.catalog.discoveryFailures.length === 0) sessions.set(key, {
|
|
390
|
+
expiresAt: now + options.catalogTtlMs,
|
|
391
|
+
session
|
|
392
|
+
});
|
|
393
|
+
return session;
|
|
394
|
+
},
|
|
395
|
+
async invalidateAgentScope(agentScopeId) {
|
|
396
|
+
incrementAgentScopeGeneration(agentScopeId);
|
|
397
|
+
for (const key of sessions.keys()) if (key === agentScopeId || key.startsWith(`${agentScopeId}\n`)) sessions.delete(key);
|
|
398
|
+
await options.runtime.closeAgentScope(agentScopeId);
|
|
399
|
+
},
|
|
400
|
+
async invalidateSession(identity) {
|
|
401
|
+
const scopeKey = portalAgentScopeKey(identity);
|
|
402
|
+
incrementScopeGeneration(scopeKey);
|
|
403
|
+
sessions.delete(scopeKey);
|
|
404
|
+
if (options.runtime.closeSession) {
|
|
405
|
+
await options.runtime.closeSession(scopeKey);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
await options.runtime.closeAgentScope(scopeKey);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
//#endregion
|
|
413
|
+
//#region src/upstream-mcp-client-runtime.ts
|
|
414
|
+
const defaultConnectionTimeoutMs = 3e4;
|
|
415
|
+
const defaultMaxResponseBytes = 4 * 1024 * 1024;
|
|
416
|
+
function isObjectRecord(value) {
|
|
417
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
418
|
+
}
|
|
419
|
+
function isTransport(value) {
|
|
420
|
+
return isObjectRecord(value) && typeof value.start === "function" && typeof value.send === "function" && typeof value.close === "function";
|
|
421
|
+
}
|
|
422
|
+
function createSdkClient() {
|
|
423
|
+
const client = new Client({
|
|
424
|
+
name: "mcp-portal",
|
|
425
|
+
version: "1.0.0"
|
|
426
|
+
});
|
|
427
|
+
return {
|
|
428
|
+
callTool: async (params, _resultSchema, options) => await client.callTool(params, void 0, options),
|
|
429
|
+
close: async () => {
|
|
430
|
+
await client.close();
|
|
431
|
+
},
|
|
432
|
+
connect: async (transport, options) => {
|
|
433
|
+
if (!isTransport(transport)) throw new Error("SDK MCP client requires a valid MCP transport.");
|
|
434
|
+
await client.connect(transport, options);
|
|
435
|
+
},
|
|
436
|
+
listTools: async (params, options) => {
|
|
437
|
+
const result = await client.listTools(params, options);
|
|
438
|
+
return {
|
|
439
|
+
...result.nextCursor !== void 0 ? { nextCursor: result.nextCursor } : {},
|
|
440
|
+
tools: result.tools
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
function withRemoteHeaders(server) {
|
|
446
|
+
if (!server.headers) return server;
|
|
447
|
+
return {
|
|
448
|
+
...server,
|
|
449
|
+
requestInit: {
|
|
450
|
+
...server.requestInit,
|
|
451
|
+
headers: {
|
|
452
|
+
...normalizeHeaders(server.requestInit?.headers),
|
|
453
|
+
...server.headers
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function createSdkTransport(server, transport) {
|
|
459
|
+
if (transport === "stdio") {
|
|
460
|
+
if (server.transport !== "stdio") throw new Error("Stdio transport requires stdio server config.");
|
|
461
|
+
return new StdioClientTransport({
|
|
462
|
+
...server.args ? { args: [...server.args] } : {},
|
|
463
|
+
command: server.command,
|
|
464
|
+
...server.cwd !== void 0 ? { cwd: server.cwd } : {},
|
|
465
|
+
...server.env ? { env: { ...server.env } } : {}
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
if (server.transport === "stdio") throw new Error("Remote transport requires remote server config.");
|
|
469
|
+
const remoteServer = withRemoteHeaders(server);
|
|
470
|
+
if (transport === "sse") {
|
|
471
|
+
const options = {};
|
|
472
|
+
if (remoteServer.eventSourceInit !== void 0) options.eventSourceInit = remoteServer.eventSourceInit;
|
|
473
|
+
if (remoteServer.requestInit !== void 0) options.requestInit = remoteServer.requestInit;
|
|
474
|
+
return new SSEClientTransport(new URL(remoteServer.url), options);
|
|
475
|
+
}
|
|
476
|
+
const options = {};
|
|
477
|
+
if (remoteServer.requestInit !== void 0) options.requestInit = remoteServer.requestInit;
|
|
478
|
+
return new StreamableHTTPClientTransport(new URL(remoteServer.url), options);
|
|
479
|
+
}
|
|
480
|
+
function cacheKey(agentScopeId, namespace) {
|
|
481
|
+
return `${agentScopeId}\n${namespace}`;
|
|
482
|
+
}
|
|
483
|
+
function rootAgentScopeId(agentScopeId) {
|
|
484
|
+
return agentScopeId.split("\n", 1)[0] ?? agentScopeId;
|
|
485
|
+
}
|
|
486
|
+
function transportAttempts(server) {
|
|
487
|
+
if (server.transport === "auto-http") return ["streamable-http", "sse"];
|
|
488
|
+
return [server.transport];
|
|
489
|
+
}
|
|
490
|
+
function redactionValuesFromServer(server) {
|
|
491
|
+
if (server.transport === "stdio") return Object.values(server.env ?? {}).filter((value) => value.length > 0);
|
|
492
|
+
return Object.values(server.headers ?? {}).filter((value) => value.length > 0);
|
|
493
|
+
}
|
|
494
|
+
function timeoutMsForServer(server) {
|
|
495
|
+
return server.connectionTimeoutMs ?? defaultConnectionTimeoutMs;
|
|
496
|
+
}
|
|
497
|
+
function assertUpstreamResponseSize(value, maxResponseBytes) {
|
|
498
|
+
const serialized = JSON.stringify(value);
|
|
499
|
+
if (serialized === void 0) return;
|
|
500
|
+
const byteLength = Buffer.byteLength(serialized, "utf8");
|
|
501
|
+
if (byteLength > maxResponseBytes) throw new Error(`MCP upstream response exceeded ${String(maxResponseBytes)} bytes (${String(byteLength)} bytes).`);
|
|
502
|
+
}
|
|
503
|
+
function isAbortError(error) {
|
|
504
|
+
return error instanceof Error && error.name === "AbortError";
|
|
505
|
+
}
|
|
506
|
+
function isCallerAbortError(error, signal) {
|
|
507
|
+
return signal?.aborted === true && (error === signal.reason || isAbortError(error));
|
|
508
|
+
}
|
|
509
|
+
async function withTimeout(promise, props) {
|
|
510
|
+
let timeout;
|
|
511
|
+
try {
|
|
512
|
+
return await Promise.race([promise, new Promise((_resolve, reject) => {
|
|
513
|
+
timeout = setTimeout(() => {
|
|
514
|
+
const error = /* @__PURE__ */ new Error(`${props.operation} timed out after ${props.timeoutMs}ms.`);
|
|
515
|
+
props.onTimeout?.(error);
|
|
516
|
+
reject(error);
|
|
517
|
+
}, props.timeoutMs);
|
|
518
|
+
})]);
|
|
519
|
+
} finally {
|
|
520
|
+
if (timeout) clearTimeout(timeout);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function createRuntimeAbortSignal(parentSignal) {
|
|
524
|
+
const controller = new AbortController();
|
|
525
|
+
const abortFromParent = () => {
|
|
526
|
+
if (!controller.signal.aborted) controller.abort(parentSignal?.reason);
|
|
527
|
+
};
|
|
528
|
+
if (parentSignal?.aborted) abortFromParent();
|
|
529
|
+
else parentSignal?.addEventListener("abort", abortFromParent, { once: true });
|
|
530
|
+
return {
|
|
531
|
+
abortTimeout: (error) => {
|
|
532
|
+
if (!controller.signal.aborted) controller.abort(error);
|
|
533
|
+
},
|
|
534
|
+
dispose: () => {
|
|
535
|
+
parentSignal?.removeEventListener("abort", abortFromParent);
|
|
536
|
+
},
|
|
537
|
+
signal: controller.signal
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
async function listAllTools(client, cursor, collectedTools, timeoutAbort, timeoutMs) {
|
|
541
|
+
const result = await withTimeout(client.listTools(cursor ? { cursor } : void 0, { signal: timeoutAbort.signal }), {
|
|
542
|
+
onTimeout: timeoutAbort.abortTimeout,
|
|
543
|
+
operation: "MCP listTools",
|
|
544
|
+
timeoutMs
|
|
545
|
+
});
|
|
546
|
+
const nextTools = [...collectedTools, ...result.tools];
|
|
547
|
+
return result.nextCursor ? listAllTools(client, result.nextCursor, nextTools, timeoutAbort, timeoutMs) : nextTools;
|
|
548
|
+
}
|
|
549
|
+
async function closeClientAfterFailure(client) {
|
|
550
|
+
if (!client) return;
|
|
551
|
+
try {
|
|
552
|
+
await client.close();
|
|
553
|
+
} catch {}
|
|
554
|
+
}
|
|
555
|
+
async function closeClientForTeardown(client, props) {
|
|
556
|
+
if (!client) return;
|
|
557
|
+
try {
|
|
558
|
+
await client.close();
|
|
559
|
+
} catch (error) {
|
|
560
|
+
props.onCloseError?.(redactThrownError(error), props.context);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
function closeErrorContextFromCacheKey(cacheKeyValue) {
|
|
564
|
+
const keyParts = cacheKeyValue.split("\n");
|
|
565
|
+
const namespace = keyParts.length > 1 ? keyParts[keyParts.length - 1] : void 0;
|
|
566
|
+
return {
|
|
567
|
+
agentScopeId: namespace !== void 0 ? keyParts.slice(0, keyParts.length - 1).join("\n") : cacheKeyValue,
|
|
568
|
+
...namespace !== void 0 ? { namespace } : {}
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function redactToolCatalog(tools, options) {
|
|
572
|
+
return tools.map((tool) => ToolSchema.parse(redactUpstreamCatalogValue(tool, options)));
|
|
573
|
+
}
|
|
574
|
+
function createUpstreamMcpClientRuntime(options) {
|
|
575
|
+
const serversByNamespace = new Map(options.servers.map((server) => [server.namespace, server]));
|
|
576
|
+
const clients = /* @__PURE__ */ new Map();
|
|
577
|
+
const pendingClients = /* @__PURE__ */ new Map();
|
|
578
|
+
const agentScopeGenerations = /* @__PURE__ */ new Map();
|
|
579
|
+
const closedClients = /* @__PURE__ */ new WeakSet();
|
|
580
|
+
const createClient = options.createClient ?? createSdkClient;
|
|
581
|
+
const createTransport = options.createTransport ?? createSdkTransport;
|
|
582
|
+
const maxResponseBytes = options.maxResponseBytes ?? defaultMaxResponseBytes;
|
|
583
|
+
const redactionValues = [...options.additionalRedactionValues ?? [], ...options.servers.flatMap((server) => redactionValuesFromServer(server))];
|
|
584
|
+
function generationForAgentScope(agentScopeId) {
|
|
585
|
+
return agentScopeGenerations.get(agentScopeId) ?? agentScopeGenerations.get(rootAgentScopeId(agentScopeId)) ?? 0;
|
|
586
|
+
}
|
|
587
|
+
function incrementAgentScopeGeneration(agentScopeId) {
|
|
588
|
+
const rootAgentScope = rootAgentScopeId(agentScopeId);
|
|
589
|
+
agentScopeGenerations.set(rootAgentScope, (agentScopeGenerations.get(rootAgentScope) ?? 0) + 1);
|
|
590
|
+
}
|
|
591
|
+
function incrementScopeGeneration(scopeKey) {
|
|
592
|
+
agentScopeGenerations.set(scopeKey, (agentScopeGenerations.get(scopeKey) ?? 0) + 1);
|
|
593
|
+
}
|
|
594
|
+
async function closeClientAfterFailureOnce(client) {
|
|
595
|
+
if (!client || closedClients.has(client)) return;
|
|
596
|
+
closedClients.add(client);
|
|
597
|
+
await closeClientAfterFailure(client);
|
|
598
|
+
}
|
|
599
|
+
async function closeClientForTeardownOnce(client, context) {
|
|
600
|
+
if (!client || closedClients.has(client)) return;
|
|
601
|
+
closedClients.add(client);
|
|
602
|
+
await closeClientForTeardown(client, {
|
|
603
|
+
context,
|
|
604
|
+
onCloseError: options.onCloseError
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
async function createConnectedClient(server) {
|
|
608
|
+
const attempts = transportAttempts(server);
|
|
609
|
+
async function tryAttempt(attemptIndex, lastError) {
|
|
610
|
+
const transportKind = attempts[attemptIndex];
|
|
611
|
+
if (transportKind === void 0) throw lastError ?? /* @__PURE__ */ new Error(`Could not connect to upstream MCP namespace "${server.namespace}".`);
|
|
612
|
+
const client = createClient();
|
|
613
|
+
const transport = createTransport(transportKind === "sse" && server.transport !== "stdio" ? withRemoteHeaders(server) : server, transportKind);
|
|
614
|
+
const timeoutAbort = createRuntimeAbortSignal(void 0);
|
|
615
|
+
try {
|
|
616
|
+
await withTimeout(client.connect(transport, { signal: timeoutAbort.signal }), {
|
|
617
|
+
onTimeout: timeoutAbort.abortTimeout,
|
|
618
|
+
operation: `MCP ${transportKind} connect for namespace "${server.namespace}"`,
|
|
619
|
+
timeoutMs: timeoutMsForServer(server)
|
|
620
|
+
});
|
|
621
|
+
return client;
|
|
622
|
+
} catch (error) {
|
|
623
|
+
const redactedError = redactThrownError(error, { exactValues: redactionValues });
|
|
624
|
+
await closeClientAfterFailureOnce(client);
|
|
625
|
+
return tryAttempt(attemptIndex + 1, redactedError);
|
|
626
|
+
} finally {
|
|
627
|
+
timeoutAbort.dispose();
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return tryAttempt(0, null);
|
|
631
|
+
}
|
|
632
|
+
async function getClient(agentScopeId, namespace) {
|
|
633
|
+
const key = cacheKey(agentScopeId, namespace);
|
|
634
|
+
const cachedClient = clients.get(key);
|
|
635
|
+
if (cachedClient) return cachedClient.client;
|
|
636
|
+
const pendingClient = pendingClients.get(key);
|
|
637
|
+
const generation = generationForAgentScope(agentScopeId);
|
|
638
|
+
if (pendingClient && pendingClient.generation === generation) return pendingClient.promise;
|
|
639
|
+
if (pendingClient) {
|
|
640
|
+
pendingClients.delete(key);
|
|
641
|
+
pendingClient.promise.then(closeClientAfterFailureOnce, () => void 0);
|
|
642
|
+
}
|
|
643
|
+
const server = serversByNamespace.get(namespace);
|
|
644
|
+
if (!server) throw new Error(`Unknown upstream MCP namespace "${namespace}".`);
|
|
645
|
+
const pending = createConnectedClient(server);
|
|
646
|
+
const pendingRecord = {
|
|
647
|
+
generation,
|
|
648
|
+
promise: pending
|
|
649
|
+
};
|
|
650
|
+
pendingClients.set(key, pendingRecord);
|
|
651
|
+
try {
|
|
652
|
+
const client = await pending;
|
|
653
|
+
if (generationForAgentScope(agentScopeId) !== generation) {
|
|
654
|
+
await closeClientAfterFailureOnce(client);
|
|
655
|
+
throw new Error(`MCP client for agent scope "${rootAgentScopeId(agentScopeId)}" was invalidated.`);
|
|
656
|
+
}
|
|
657
|
+
clients.set(key, { client });
|
|
658
|
+
if (generationForAgentScope(agentScopeId) !== generation) {
|
|
659
|
+
if (clients.get(key)?.client === client) clients.delete(key);
|
|
660
|
+
await closeClientAfterFailureOnce(client);
|
|
661
|
+
throw new Error(`MCP client for agent scope "${rootAgentScopeId(agentScopeId)}" was invalidated.`);
|
|
662
|
+
}
|
|
663
|
+
return client;
|
|
664
|
+
} finally {
|
|
665
|
+
if (pendingClients.get(key) === pendingRecord) pendingClients.delete(key);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
async callTool(call) {
|
|
670
|
+
const key = cacheKey(call.agentScopeId, call.namespace);
|
|
671
|
+
let client = null;
|
|
672
|
+
try {
|
|
673
|
+
client = await getClient(call.agentScopeId, call.namespace);
|
|
674
|
+
const server = serversByNamespace.get(call.namespace);
|
|
675
|
+
const timeoutAbort = createRuntimeAbortSignal(call.signal);
|
|
676
|
+
try {
|
|
677
|
+
const upstreamResult = await withTimeout(client.callTool({
|
|
678
|
+
arguments: call.arguments,
|
|
679
|
+
name: call.toolName
|
|
680
|
+
}, void 0, {
|
|
681
|
+
onprogress: (progress) => {
|
|
682
|
+
call.onEvent?.({
|
|
683
|
+
kind: "progress",
|
|
684
|
+
...progress.message !== void 0 ? { message: progress.message } : {},
|
|
685
|
+
progress: progress.progress,
|
|
686
|
+
...progress.total !== void 0 ? { total: progress.total } : {}
|
|
687
|
+
});
|
|
688
|
+
},
|
|
689
|
+
signal: timeoutAbort.signal
|
|
690
|
+
}), {
|
|
691
|
+
onTimeout: timeoutAbort.abortTimeout,
|
|
692
|
+
operation: `MCP callTool ${call.namespace}.${call.toolName}`,
|
|
693
|
+
timeoutMs: server ? timeoutMsForServer(server) : defaultConnectionTimeoutMs
|
|
694
|
+
});
|
|
695
|
+
assertUpstreamResponseSize(upstreamResult, maxResponseBytes);
|
|
696
|
+
return redactUpstreamResponse(upstreamResult, { exactValues: redactionValues });
|
|
697
|
+
} finally {
|
|
698
|
+
timeoutAbort.dispose();
|
|
699
|
+
}
|
|
700
|
+
} catch (error) {
|
|
701
|
+
if (isCallerAbortError(error, call.signal)) throw redactThrownError(error, { exactValues: redactionValues });
|
|
702
|
+
clients.delete(key);
|
|
703
|
+
await closeClientAfterFailureOnce(client);
|
|
704
|
+
throw redactThrownError(error, { exactValues: redactionValues });
|
|
705
|
+
}
|
|
706
|
+
},
|
|
707
|
+
async closeAgentScope(agentScopeId) {
|
|
708
|
+
incrementAgentScopeGeneration(agentScopeId);
|
|
709
|
+
const closePromises = [];
|
|
710
|
+
for (const [key, cachedClient] of clients.entries()) {
|
|
711
|
+
if (key !== agentScopeId && !key.startsWith(`${agentScopeId}\n`)) continue;
|
|
712
|
+
clients.delete(key);
|
|
713
|
+
closePromises.push(closeClientForTeardownOnce(cachedClient.client, closeErrorContextFromCacheKey(key)));
|
|
714
|
+
}
|
|
715
|
+
for (const [key, pendingClient] of pendingClients.entries()) {
|
|
716
|
+
if (key !== agentScopeId && !key.startsWith(`${agentScopeId}\n`)) continue;
|
|
717
|
+
pendingClients.delete(key);
|
|
718
|
+
closePromises.push(pendingClient.promise.then((client) => closeClientForTeardownOnce(client, closeErrorContextFromCacheKey(key)), () => void 0));
|
|
719
|
+
}
|
|
720
|
+
await Promise.all(closePromises);
|
|
721
|
+
},
|
|
722
|
+
async closeSession(scopeKey) {
|
|
723
|
+
incrementScopeGeneration(scopeKey);
|
|
724
|
+
const closePromises = [];
|
|
725
|
+
for (const [key, cachedClient] of clients.entries()) {
|
|
726
|
+
if (key !== scopeKey && !key.startsWith(`${scopeKey}\n`)) continue;
|
|
727
|
+
clients.delete(key);
|
|
728
|
+
closePromises.push(closeClientForTeardownOnce(cachedClient.client, closeErrorContextFromCacheKey(key)));
|
|
729
|
+
}
|
|
730
|
+
for (const [key, pendingClient] of pendingClients.entries()) {
|
|
731
|
+
if (key !== scopeKey && !key.startsWith(`${scopeKey}\n`)) continue;
|
|
732
|
+
pendingClients.delete(key);
|
|
733
|
+
closePromises.push(pendingClient.promise.then((client) => closeClientForTeardownOnce(client, closeErrorContextFromCacheKey(key)), () => void 0));
|
|
734
|
+
}
|
|
735
|
+
await Promise.all(closePromises);
|
|
736
|
+
},
|
|
737
|
+
async listTools(call) {
|
|
738
|
+
const key = cacheKey(call.agentScopeId, call.namespace);
|
|
739
|
+
let client = null;
|
|
740
|
+
try {
|
|
741
|
+
client = await getClient(call.agentScopeId, call.namespace);
|
|
742
|
+
const server = serversByNamespace.get(call.namespace);
|
|
743
|
+
const timeoutAbort = createRuntimeAbortSignal(void 0);
|
|
744
|
+
try {
|
|
745
|
+
return redactToolCatalog(await listAllTools(client, void 0, [], timeoutAbort, server ? timeoutMsForServer(server) : defaultConnectionTimeoutMs), { exactValues: redactionValues });
|
|
746
|
+
} finally {
|
|
747
|
+
timeoutAbort.dispose();
|
|
748
|
+
}
|
|
749
|
+
} catch (error) {
|
|
750
|
+
clients.delete(key);
|
|
751
|
+
await closeClientAfterFailureOnce(client);
|
|
752
|
+
throw redactThrownError(error, { exactValues: redactionValues });
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
//#endregion
|
|
758
|
+
export { createSearchIndex as i, createPortalSessionManager as n, buildToolGraph as r, createUpstreamMcpClientRuntime as t };
|
|
759
|
+
|
|
760
|
+
//# sourceMappingURL=upstream-mcp-client-runtime-JlsfTm7_.js.map
|