@openeryc/pi-coding-agent 0.75.48 → 0.75.50
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 +13 -0
- package/dist/core/agent-session.d.ts +9 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +20 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/mcp/client.d.ts +6 -0
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +84 -39
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/manager.d.ts +21 -1
- package/dist/core/mcp/manager.d.ts.map +1 -1
- package/dist/core/mcp/manager.js +186 -57
- package/dist/core/mcp/manager.js.map +1 -1
- package/dist/core/mcp/types.d.ts +12 -0
- package/dist/core/mcp/types.d.ts.map +1 -1
- package/dist/core/mcp/types.js +3 -0
- package/dist/core/mcp/types.js.map +1 -1
- package/dist/core/settings-manager.d.ts +5 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/modes/interactive/components/mcp-selector.d.ts +1 -1
- package/dist/modes/interactive/components/mcp-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/mcp-selector.js +3 -1
- package/dist/modes/interactive/components/mcp-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +9 -15
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
package/dist/core/mcp/manager.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { MCPClient } from "./client.js";
|
|
3
|
+
const DEFAULT_RECONNECT_MAX_ATTEMPTS = 5;
|
|
4
|
+
const DEFAULT_RECONNECT_INTERVAL_MS = 5000;
|
|
3
5
|
function toTypeBox(schema) {
|
|
4
6
|
if (!schema.properties || Object.keys(schema.properties).length === 0) {
|
|
5
7
|
return Type.Object({});
|
|
@@ -34,65 +36,194 @@ function sanitizeMcpName(name) {
|
|
|
34
36
|
}
|
|
35
37
|
export class MCPManager {
|
|
36
38
|
clients = new Map();
|
|
39
|
+
serverConfigs = new Map();
|
|
37
40
|
tools = new Map();
|
|
38
41
|
resourceUris = [];
|
|
42
|
+
reconnectTimers = new Map();
|
|
43
|
+
reconnectAttempts = new Map();
|
|
44
|
+
_statuses = new Map();
|
|
45
|
+
/** Called when any server's connection status changes */
|
|
46
|
+
onStatusChange = null;
|
|
47
|
+
getServerStatus(serverName) {
|
|
48
|
+
return this._statuses.get(serverName) ?? "disconnected";
|
|
49
|
+
}
|
|
50
|
+
getAllStatuses() {
|
|
51
|
+
return [...this._statuses.entries()].map(([serverName, status]) => ({
|
|
52
|
+
serverName,
|
|
53
|
+
status,
|
|
54
|
+
toolCount: this.tools.get(serverName)?.length ?? 0,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
setStatus(serverName, status) {
|
|
58
|
+
const prev = this._statuses.get(serverName);
|
|
59
|
+
if (prev === status)
|
|
60
|
+
return;
|
|
61
|
+
this._statuses.set(serverName, status);
|
|
62
|
+
try {
|
|
63
|
+
this.onStatusChange?.(serverName, status);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// ignore
|
|
67
|
+
}
|
|
68
|
+
}
|
|
39
69
|
async start(serverConfigs) {
|
|
40
70
|
for (const [serverName, config] of Object.entries(serverConfigs)) {
|
|
41
|
-
if (config.enabled === false)
|
|
71
|
+
if (config.enabled === false) {
|
|
72
|
+
this.setStatus(serverName, "disabled");
|
|
42
73
|
continue;
|
|
74
|
+
}
|
|
75
|
+
this.serverConfigs.set(serverName, config);
|
|
76
|
+
await this.connectServer(serverName, config);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async connectServer(serverName, config) {
|
|
80
|
+
this.setStatus(serverName, "reconnecting");
|
|
81
|
+
try {
|
|
82
|
+
const client = new MCPClient(config.timeoutMs);
|
|
83
|
+
client.onDisconnect = (_reason) => {
|
|
84
|
+
this.setStatus(serverName, "disconnected");
|
|
85
|
+
this.tools.delete(serverName);
|
|
86
|
+
this.scheduleReconnect(serverName);
|
|
87
|
+
};
|
|
88
|
+
await client.connect(config);
|
|
89
|
+
const { tools: mcpTools } = await client.listTools();
|
|
90
|
+
this.clients.set(serverName, client);
|
|
91
|
+
this.buildToolDefs(serverName, mcpTools);
|
|
92
|
+
// Discover resources
|
|
43
93
|
try {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
94
|
+
const { resources } = await client.listResources();
|
|
95
|
+
for (const r of resources) {
|
|
96
|
+
this.resourceUris.push(`mcp://${serverName}${r.uri}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Server may not support resources
|
|
101
|
+
}
|
|
102
|
+
this.setStatus(serverName, "connected");
|
|
103
|
+
this.reconnectAttempts.delete(serverName);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
this.clients.delete(serverName);
|
|
107
|
+
console.warn(`Failed to connect to MCP server "${serverName}": ${error instanceof Error ? error.message : String(error)}`);
|
|
108
|
+
this.setStatus(serverName, "disconnected");
|
|
109
|
+
this.scheduleReconnect(serverName);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
buildToolDefs(serverName, mcpTools) {
|
|
113
|
+
const safeServerName = sanitizeMcpName(serverName);
|
|
114
|
+
const client = this.clients.get(serverName);
|
|
115
|
+
const defs = mcpTools.map((tool) => ({
|
|
116
|
+
name: `mcp_${safeServerName}_${sanitizeMcpName(tool.name)}`,
|
|
117
|
+
label: `mcp.${serverName}.${tool.name}`,
|
|
118
|
+
description: tool.description
|
|
119
|
+
? `${tool.description} (from MCP server "${serverName}")`
|
|
120
|
+
: `Tool from MCP server "${serverName}"`,
|
|
121
|
+
promptSnippet: tool.description
|
|
122
|
+
? `[${serverName}] ${tool.description.split("\n")[0]}`
|
|
123
|
+
: `[${serverName}] ${tool.name}`,
|
|
124
|
+
parameters: toTypeBox(tool.inputSchema),
|
|
125
|
+
renderShell: "default",
|
|
126
|
+
execute: async (_id, params, signal) => {
|
|
127
|
+
if (!client.isConnected) {
|
|
128
|
+
return {
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
131
|
+
type: "text",
|
|
132
|
+
text: `MCP server "${serverName}" is disconnected. Use /mcp to check status.`,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
details: { error: "disconnected" },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
82
138
|
try {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
139
|
+
const result = await client.callTool(tool.name, params, signal);
|
|
140
|
+
const text = result.content
|
|
141
|
+
.map((item) => (item.type === "text" && item.text ? item.text : `[Image: ${item.mimeType}]`))
|
|
142
|
+
.join("\n");
|
|
143
|
+
return {
|
|
144
|
+
content: [{ type: "text", text }],
|
|
145
|
+
details: result,
|
|
146
|
+
};
|
|
87
147
|
}
|
|
88
|
-
catch {
|
|
89
|
-
|
|
148
|
+
catch (error) {
|
|
149
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
150
|
+
return {
|
|
151
|
+
content: [{ type: "text", text: `MCP tool error: ${msg}` }],
|
|
152
|
+
details: { error: msg },
|
|
153
|
+
};
|
|
90
154
|
}
|
|
155
|
+
},
|
|
156
|
+
}));
|
|
157
|
+
this.tools.set(serverName, defs);
|
|
158
|
+
}
|
|
159
|
+
scheduleReconnect(serverName) {
|
|
160
|
+
// Cancel any existing reconnect timer for this server
|
|
161
|
+
const existing = this.reconnectTimers.get(serverName);
|
|
162
|
+
if (existing) {
|
|
163
|
+
clearTimeout(existing);
|
|
164
|
+
}
|
|
165
|
+
const config = this.serverConfigs.get(serverName);
|
|
166
|
+
if (!config)
|
|
167
|
+
return;
|
|
168
|
+
const reconnectCfg = config.reconnect ?? {};
|
|
169
|
+
if (reconnectCfg.enabled === false)
|
|
170
|
+
return;
|
|
171
|
+
const maxAttempts = reconnectCfg.maxAttempts ?? DEFAULT_RECONNECT_MAX_ATTEMPTS;
|
|
172
|
+
const intervalMs = reconnectCfg.intervalMs ?? DEFAULT_RECONNECT_INTERVAL_MS;
|
|
173
|
+
const attempt = (this.reconnectAttempts.get(serverName) ?? 0) + 1;
|
|
174
|
+
if (attempt > maxAttempts) {
|
|
175
|
+
console.warn(`MCP server "${serverName}": max reconnection attempts (${maxAttempts}) reached, giving up.`);
|
|
176
|
+
this.reconnectAttempts.delete(serverName);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
this.reconnectAttempts.set(serverName, attempt);
|
|
180
|
+
this.setStatus(serverName, "reconnecting");
|
|
181
|
+
const timer = setTimeout(async () => {
|
|
182
|
+
this.reconnectTimers.delete(serverName);
|
|
183
|
+
const cfg = this.serverConfigs.get(serverName);
|
|
184
|
+
if (!cfg)
|
|
185
|
+
return;
|
|
186
|
+
// Remove old client if any
|
|
187
|
+
const oldClient = this.clients.get(serverName);
|
|
188
|
+
if (oldClient) {
|
|
189
|
+
oldClient.onDisconnect = null;
|
|
190
|
+
await oldClient.disconnect().catch(() => { });
|
|
191
|
+
this.clients.delete(serverName);
|
|
91
192
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
193
|
+
await this.connectServer(serverName, cfg);
|
|
194
|
+
}, intervalMs * Math.min(attempt, 5)); // exponential backoff, cap at 5x
|
|
195
|
+
this.reconnectTimers.set(serverName, timer);
|
|
196
|
+
}
|
|
197
|
+
/** Manually trigger reconnection for a specific server */
|
|
198
|
+
async reconnect(serverName) {
|
|
199
|
+
const existingTimer = this.reconnectTimers.get(serverName);
|
|
200
|
+
if (existingTimer) {
|
|
201
|
+
clearTimeout(existingTimer);
|
|
202
|
+
this.reconnectTimers.delete(serverName);
|
|
95
203
|
}
|
|
204
|
+
const oldClient = this.clients.get(serverName);
|
|
205
|
+
if (oldClient) {
|
|
206
|
+
oldClient.onDisconnect = null;
|
|
207
|
+
await oldClient.disconnect().catch(() => { });
|
|
208
|
+
this.clients.delete(serverName);
|
|
209
|
+
}
|
|
210
|
+
const config = this.serverConfigs.get(serverName);
|
|
211
|
+
if (!config) {
|
|
212
|
+
this.setStatus(serverName, "disconnected");
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
this.reconnectAttempts.delete(serverName);
|
|
216
|
+
await this.connectServer(serverName, config);
|
|
217
|
+
}
|
|
218
|
+
// Rebuild tool definitions for a server (used after reconnect to pick up any changes)
|
|
219
|
+
getToolDefinitions() {
|
|
220
|
+
return Array.from(this.tools.values()).flat();
|
|
221
|
+
}
|
|
222
|
+
getServerNames() {
|
|
223
|
+
return [...this.clients.keys()];
|
|
224
|
+
}
|
|
225
|
+
getResourceUris() {
|
|
226
|
+
return this.resourceUris;
|
|
96
227
|
}
|
|
97
228
|
async readResource(uri) {
|
|
98
229
|
const serverName = this.clients.keys().next().value;
|
|
@@ -105,16 +236,13 @@ export class MCPManager {
|
|
|
105
236
|
const result = await client.readResource(mcpUri);
|
|
106
237
|
return result.contents.map((c) => c.text ?? "").join("\n");
|
|
107
238
|
}
|
|
108
|
-
getToolDefinitions() {
|
|
109
|
-
return Array.from(this.tools.values()).flat();
|
|
110
|
-
}
|
|
111
|
-
getServerNames() {
|
|
112
|
-
return [...this.clients.keys()];
|
|
113
|
-
}
|
|
114
|
-
getResourceUris() {
|
|
115
|
-
return this.resourceUris;
|
|
116
|
-
}
|
|
117
239
|
async stop() {
|
|
240
|
+
// Cancel all reconnect timers
|
|
241
|
+
for (const [, timer] of this.reconnectTimers) {
|
|
242
|
+
clearTimeout(timer);
|
|
243
|
+
}
|
|
244
|
+
this.reconnectTimers.clear();
|
|
245
|
+
this.reconnectAttempts.clear();
|
|
118
246
|
const pending = [...this.clients.entries()].map(async ([name, client]) => {
|
|
119
247
|
try {
|
|
120
248
|
await client.disconnect();
|
|
@@ -126,6 +254,7 @@ export class MCPManager {
|
|
|
126
254
|
this.clients.clear();
|
|
127
255
|
this.tools.clear();
|
|
128
256
|
this.resourceUris = [];
|
|
257
|
+
this._statuses.clear();
|
|
129
258
|
await Promise.all(pending);
|
|
130
259
|
}
|
|
131
260
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentToolResult } from \"@openeryc/pi-agent-core\";\nimport { Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { MCPClient, type MCPServerConfig } from \"./client.ts\";\nimport type { MCPToolDefinition } from \"./types.ts\";\n\nfunction toTypeBox(schema: MCPToolDefinition[\"inputSchema\"]): ReturnType<typeof Type.Object> {\n\tif (!schema.properties || Object.keys(schema.properties).length === 0) {\n\t\treturn Type.Object({});\n\t}\n\tconst required = new Set(schema.required ?? []);\n\tconst fields: Record<string, unknown> = {};\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst p = prop as { type?: string; description?: string; enum?: string[] };\n\t\tconst t = p.type ?? \"string\";\n\t\tconst desc = p.description ? { description: p.description } : {};\n\t\tlet f: unknown;\n\t\tswitch (t) {\n\t\t\tcase \"string\":\n\t\t\t\tf = p.enum ? Type.Unsafe<string>({ type: \"string\", enum: p.enum, ...desc }) : Type.String(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"number\":\n\t\t\tcase \"integer\":\n\t\t\t\tf = Type.Number(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"boolean\":\n\t\t\t\tf = Type.Boolean(desc);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tf = Type.Any(desc);\n\t\t}\n\t\tfields[key] = required.has(key) ? f : Type.Optional(f as never);\n\t}\n\treturn Type.Object(fields as Record<string, ReturnType<typeof Type.String>>);\n}\n\nfunction sanitizeMcpName(name: string): string {\n\treturn name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport class MCPManager {\n\tprivate clients = new Map<string, MCPClient>();\n\tprivate tools = new Map<string, ToolDefinition[]>();\n\tprivate resourceUris: string[] = [];\n\n\tasync start(serverConfigs: Record<string, MCPServerConfig>): Promise<void> {\n\t\tfor (const [serverName, config] of Object.entries(serverConfigs)) {\n\t\t\tif (config.enabled === false) continue;\n\t\t\ttry {\n\t\t\t\tconst client = new MCPClient(config.timeoutMs);\n\t\t\t\tawait client.connect(config);\n\t\t\t\tconst { tools: mcpTools } = await client.listTools();\n\t\t\t\tthis.clients.set(serverName, client);\n\n\t\t\t\tconst safeServerName = sanitizeMcpName(serverName);\n\t\t\t\tconst defs: ToolDefinition[] = mcpTools.map((tool) => ({\n\t\t\t\t\tname: `mcp_${safeServerName}_${sanitizeMcpName(tool.name)}`,\n\t\t\t\t\tlabel: `mcp.${serverName}.${tool.name}`,\n\t\t\t\t\tdescription: tool.description\n\t\t\t\t\t\t? `${tool.description} (from MCP server \"${serverName}\")`\n\t\t\t\t\t\t: `Tool from MCP server \"${serverName}\"`,\n\t\t\t\t\tpromptSnippet: tool.description\n\t\t\t\t\t\t? `[${serverName}] ${tool.description.split(\"\\n\")[0]}`\n\t\t\t\t\t\t: `[${serverName}] ${tool.name}`,\n\t\t\t\t\tparameters: toTypeBox(tool.inputSchema),\n\t\t\t\t\trenderShell: \"default\" as const,\n\t\t\t\t\texecute: async (_id, params, signal) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst result = await client.callTool(tool.name, params as Record<string, unknown>, signal);\n\t\t\t\t\t\t\tconst text = result.content\n\t\t\t\t\t\t\t\t.map((item) => (item.type === \"text\" && item.text ? item.text : `[Image: ${item.mimeType}]`))\n\t\t\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\t\t\t\t\tdetails: result,\n\t\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `MCP tool error: ${msg}` }],\n\t\t\t\t\t\t\t\tdetails: { error: msg },\n\t\t\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t}));\n\n\t\t\t\tthis.tools.set(serverName, defs);\n\n\t\t\t\t// Discover resources\n\t\t\t\ttry {\n\t\t\t\t\tconst { resources } = await client.listResources();\n\t\t\t\t\tfor (const r of resources) {\n\t\t\t\t\t\tthis.resourceUris.push(`mcp://${serverName}${r.uri}`);\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Server may not support resources\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Failed to connect to MCP server \"${serverName}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync readResource(uri: string): Promise<string> {\n\t\tconst serverName = this.clients.keys().next().value;\n\t\tif (!serverName) throw new Error(\"No MCP servers connected\");\n\t\tconst mcpUri = uri.startsWith(`mcp://${serverName}`) ? uri.slice(`mcp://${serverName}`.length) : uri;\n\t\tconst client = this.clients.get(serverName);\n\t\tif (!client) throw new Error(`MCP server \"${serverName}\" not connected`);\n\t\tconst result = await client.readResource(mcpUri);\n\t\treturn result.contents.map((c) => c.text ?? \"\").join(\"\\n\");\n\t}\n\n\tgetToolDefinitions(): ToolDefinition[] {\n\t\treturn Array.from(this.tools.values()).flat();\n\t}\n\n\tgetServerNames(): string[] {\n\t\treturn [...this.clients.keys()];\n\t}\n\n\tgetResourceUris(): string[] {\n\t\treturn this.resourceUris;\n\t}\n\n\tasync stop(): Promise<void> {\n\t\tconst pending = [...this.clients.entries()].map(async ([name, client]) => {\n\t\t\ttry {\n\t\t\t\tawait client.disconnect();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Error disconnecting MCP server \"${name}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\tthis.clients.clear();\n\t\tthis.tools.clear();\n\t\tthis.resourceUris = [];\n\t\tawait Promise.all(pending);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../../src/core/mcp/manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/B,OAAO,EAAE,SAAS,EAAwB,MAAM,aAAa,CAAC;AAG9D,MAAM,8BAA8B,GAAG,CAAC,CAAC;AACzC,MAAM,6BAA6B,GAAG,IAAI,CAAC;AAE3C,SAAS,SAAS,CAAC,MAAwC,EAAkC;IAC5F,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7D,MAAM,CAAC,GAAG,IAAgE,CAAC;QAC3E,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,QAAQ,CAAC;QAC7B,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,IAAI,CAAU,CAAC;QACf,QAAQ,CAAC,EAAE,CAAC;YACX,KAAK,QAAQ;gBACZ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBAChG,MAAM;YACP,KAAK,QAAQ,CAAC;YACd,KAAK,SAAS;gBACb,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACtB,MAAM;YACP,KAAK,SAAS;gBACb,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBACvB,MAAM;YACP;gBACC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrB,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAU,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,MAAwD,CAAC,CAAC;AAAA,CAC7E;AAED,SAAS,eAAe,CAAC,IAAY,EAAU;IAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,GAAG,CAAC,CAAC;AAAA,CAC5C;AAID,MAAM,OAAO,UAAU;IACd,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;IACvC,aAAa,GAAG,IAAI,GAAG,EAA2B,CAAC;IACnD,KAAK,GAAG,IAAI,GAAG,EAA4B,CAAC;IAC5C,YAAY,GAAa,EAAE,CAAC;IAC5B,eAAe,GAAG,IAAI,GAAG,EAAyC,CAAC;IACnE,iBAAiB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,SAAS,GAAG,IAAI,GAAG,EAA+B,CAAC;IAE3D,yDAAyD;IACzD,cAAc,GAAkC,IAAI,CAAC;IAErD,eAAe,CAAC,UAAkB,EAAuB;QACxD,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,cAAc,CAAC;IAAA,CACxD;IAED,cAAc,GAAkF;QAC/F,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;YACnE,UAAU;YACV,MAAM;YACN,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;SAClD,CAAC,CAAC,CAAC;IAAA,CACJ;IAEO,SAAS,CAAC,UAAkB,EAAE,MAA2B,EAAQ;QACxE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,IAAI,KAAK,MAAM;YAAE,OAAO;QAC5B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC;YACJ,IAAI,CAAC,cAAc,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACR,SAAS;QACV,CAAC;IAAA,CACD;IAED,KAAK,CAAC,KAAK,CAAC,aAA8C,EAAiB;QAC1E,KAAK,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;YAClE,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;gBAC9B,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBACvC,SAAS;YACV,CAAC;YACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC3C,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC9C,CAAC;IAAA,CACD;IAEO,KAAK,CAAC,aAAa,CAAC,UAAkB,EAAE,MAAuB,EAAiB;QACvF,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QAC3C,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC/C,MAAM,CAAC,YAAY,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;gBAClC,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;gBAC3C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;gBAC9B,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAAA,CACnC,CAAC;YAEF,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC7B,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;YACrD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAErC,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YAEzC,qBAAqB;YACrB,IAAI,CAAC;gBACJ,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC;gBACnD,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;oBAC3B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,UAAU,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;gBACvD,CAAC;YACF,CAAC;YAAC,MAAM,CAAC;gBACR,mCAAmC;YACpC,CAAC;YAED,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;YACxC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAChC,OAAO,CAAC,IAAI,CACX,oCAAoC,UAAU,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC5G,CAAC;YACF,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;YAC3C,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACpC,CAAC;IAAA,CACD;IAEO,aAAa,CAAC,UAAkB,EAAE,QAA6B,EAAQ;QAC9E,MAAM,cAAc,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;QACnD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAE,CAAC;QAC7C,MAAM,IAAI,GAAqB,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACtD,IAAI,EAAE,OAAO,cAAc,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;YAC3D,KAAK,EAAE,OAAO,UAAU,IAAI,IAAI,CAAC,IAAI,EAAE;YACvC,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC5B,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,sBAAsB,UAAU,IAAI;gBACzD,CAAC,CAAC,yBAAyB,UAAU,GAAG;YACzC,aAAa,EAAE,IAAI,CAAC,WAAW;gBAC9B,CAAC,CAAC,IAAI,UAAU,KAAK,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;gBACtD,CAAC,CAAC,IAAI,UAAU,KAAK,IAAI,CAAC,IAAI,EAAE;YACjC,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC;YACvC,WAAW,EAAE,SAAkB;YAC/B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;gBACvC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;oBACzB,OAAO;wBACN,OAAO,EAAE;4BACR;gCACC,IAAI,EAAE,MAAe;gCACrB,IAAI,EAAE,eAAe,UAAU,8CAA8C;6BAC7E;yBACD;wBACD,OAAO,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE;qBACC,CAAC;gBACtC,CAAC;gBACD,IAAI,CAAC;oBACJ,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAiC,EAAE,MAAM,CAAC,CAAC;oBAC3F,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO;yBACzB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;yBAC5F,IAAI,CAAC,IAAI,CAAC,CAAC;oBACb,OAAO;wBACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;wBAC1C,OAAO,EAAE,MAAM;qBACoB,CAAC;gBACtC,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACnE,OAAO;wBACN,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,mBAAmB,GAAG,EAAE,EAAE,CAAC;wBACpE,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE;qBACY,CAAC;gBACtC,CAAC;YAAA,CACD;SACD,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAAA,CACjC;IAEO,iBAAiB,CAAC,UAAkB,EAAQ;QACnD,sDAAsD;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACtD,IAAI,QAAQ,EAAE,CAAC;YACd,YAAY,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;QAC5C,IAAI,YAAY,CAAC,OAAO,KAAK,KAAK;YAAE,OAAO;QAE3C,MAAM,WAAW,GAAG,YAAY,CAAC,WAAW,IAAI,8BAA8B,CAAC;QAC/E,MAAM,UAAU,GAAG,YAAY,CAAC,UAAU,IAAI,6BAA6B,CAAC;QAC5E,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAElE,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,eAAe,UAAU,iCAAiC,WAAW,uBAAuB,CAAC,CAAC;YAC3G,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAC1C,OAAO;QACR,CAAC;QAED,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QAE3C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC;YACpC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACxC,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC/C,IAAI,CAAC,GAAG;gBAAE,OAAO;YAEjB,2BAA2B;YAC3B,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC/C,IAAI,SAAS,EAAE,CAAC;gBACf,SAAS,CAAC,YAAY,GAAG,IAAI,CAAC;gBAC9B,MAAM,SAAS,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC,CAAC;gBAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACjC,CAAC;YAED,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAAA,CAC1C,EAAE,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,iCAAiC;QAExE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAAA,CAC5C;IAED,0DAA0D;IAC1D,KAAK,CAAC,SAAS,CAAC,UAAkB,EAAiB;QAClD,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC3D,IAAI,aAAa,EAAE,CAAC;YACnB,YAAY,CAAC,aAAa,CAAC,CAAC;YAC5B,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACzC,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC/C,IAAI,SAAS,EAAE,CAAC;YACf,SAAS,CAAC,YAAY,GAAG,IAAI,CAAC;YAC9B,MAAM,SAAS,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAC,CAAC,CAAC,CAAC;YAC7C,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACjC,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;YAC3C,OAAO;QACR,CAAC;QAED,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1C,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAAA,CAC7C;IAED,sFAAsF;IACtF,kBAAkB,GAAqB;QACtC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAAA,CAC9C;IAED,cAAc,GAAa;QAC1B,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAAA,CAChC;IAED,eAAe,GAAa;QAC3B,OAAO,IAAI,CAAC,YAAY,CAAC;IAAA,CACzB;IAED,KAAK,CAAC,YAAY,CAAC,GAAW,EAAmB;QAChD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;QACpD,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,UAAU,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QACrG,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,eAAe,UAAU,iBAAiB,CAAC,CAAC;QACzE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QACjD,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAAA,CAC3D;IAED,KAAK,CAAC,IAAI,GAAkB;QAC3B,8BAA8B;QAC9B,KAAK,MAAM,CAAC,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YAC9C,YAAY,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;QAC7B,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAE/B,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC;YACzE,IAAI,CAAC;gBACJ,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;YAC3B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,OAAO,CAAC,IAAI,CACX,mCAAmC,IAAI,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACrG,CAAC;YACH,CAAC;QAAA,CACD,CAAC,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAAA,CAC3B;CACD","sourcesContent":["import type { AgentToolResult } from \"@openeryc/pi-agent-core\";\nimport { Type } from \"typebox\";\nimport type { ToolDefinition } from \"../extensions/types.ts\";\nimport { MCPClient, type MCPServerConfig } from \"./client.ts\";\nimport type { MCPConnectionStatus, MCPToolDefinition } from \"./types.ts\";\n\nconst DEFAULT_RECONNECT_MAX_ATTEMPTS = 5;\nconst DEFAULT_RECONNECT_INTERVAL_MS = 5000;\n\nfunction toTypeBox(schema: MCPToolDefinition[\"inputSchema\"]): ReturnType<typeof Type.Object> {\n\tif (!schema.properties || Object.keys(schema.properties).length === 0) {\n\t\treturn Type.Object({});\n\t}\n\tconst required = new Set(schema.required ?? []);\n\tconst fields: Record<string, unknown> = {};\n\tfor (const [key, prop] of Object.entries(schema.properties)) {\n\t\tconst p = prop as { type?: string; description?: string; enum?: string[] };\n\t\tconst t = p.type ?? \"string\";\n\t\tconst desc = p.description ? { description: p.description } : {};\n\t\tlet f: unknown;\n\t\tswitch (t) {\n\t\t\tcase \"string\":\n\t\t\t\tf = p.enum ? Type.Unsafe<string>({ type: \"string\", enum: p.enum, ...desc }) : Type.String(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"number\":\n\t\t\tcase \"integer\":\n\t\t\t\tf = Type.Number(desc);\n\t\t\t\tbreak;\n\t\t\tcase \"boolean\":\n\t\t\t\tf = Type.Boolean(desc);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tf = Type.Any(desc);\n\t\t}\n\t\tfields[key] = required.has(key) ? f : Type.Optional(f as never);\n\t}\n\treturn Type.Object(fields as Record<string, ReturnType<typeof Type.String>>);\n}\n\nfunction sanitizeMcpName(name: string): string {\n\treturn name.replace(/[^a-zA-Z0-9_-]/g, \"_\");\n}\n\nexport type MCPStatusChangeHandler = (serverName: string, status: MCPConnectionStatus) => void;\n\nexport class MCPManager {\n\tprivate clients = new Map<string, MCPClient>();\n\tprivate serverConfigs = new Map<string, MCPServerConfig>();\n\tprivate tools = new Map<string, ToolDefinition[]>();\n\tprivate resourceUris: string[] = [];\n\tprivate reconnectTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\tprivate reconnectAttempts = new Map<string, number>();\n\tprivate _statuses = new Map<string, MCPConnectionStatus>();\n\n\t/** Called when any server's connection status changes */\n\tonStatusChange: MCPStatusChangeHandler | null = null;\n\n\tgetServerStatus(serverName: string): MCPConnectionStatus {\n\t\treturn this._statuses.get(serverName) ?? \"disconnected\";\n\t}\n\n\tgetAllStatuses(): Array<{ serverName: string; status: MCPConnectionStatus; toolCount: number }> {\n\t\treturn [...this._statuses.entries()].map(([serverName, status]) => ({\n\t\t\tserverName,\n\t\t\tstatus,\n\t\t\ttoolCount: this.tools.get(serverName)?.length ?? 0,\n\t\t}));\n\t}\n\n\tprivate setStatus(serverName: string, status: MCPConnectionStatus): void {\n\t\tconst prev = this._statuses.get(serverName);\n\t\tif (prev === status) return;\n\t\tthis._statuses.set(serverName, status);\n\t\ttry {\n\t\t\tthis.onStatusChange?.(serverName, status);\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t}\n\n\tasync start(serverConfigs: Record<string, MCPServerConfig>): Promise<void> {\n\t\tfor (const [serverName, config] of Object.entries(serverConfigs)) {\n\t\t\tif (config.enabled === false) {\n\t\t\t\tthis.setStatus(serverName, \"disabled\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.serverConfigs.set(serverName, config);\n\t\t\tawait this.connectServer(serverName, config);\n\t\t}\n\t}\n\n\tprivate async connectServer(serverName: string, config: MCPServerConfig): Promise<void> {\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\t\ttry {\n\t\t\tconst client = new MCPClient(config.timeoutMs);\n\t\t\tclient.onDisconnect = (_reason) => {\n\t\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\t\tthis.tools.delete(serverName);\n\t\t\t\tthis.scheduleReconnect(serverName);\n\t\t\t};\n\n\t\t\tawait client.connect(config);\n\t\t\tconst { tools: mcpTools } = await client.listTools();\n\t\t\tthis.clients.set(serverName, client);\n\n\t\t\tthis.buildToolDefs(serverName, mcpTools);\n\n\t\t\t// Discover resources\n\t\t\ttry {\n\t\t\t\tconst { resources } = await client.listResources();\n\t\t\t\tfor (const r of resources) {\n\t\t\t\t\tthis.resourceUris.push(`mcp://${serverName}${r.uri}`);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Server may not support resources\n\t\t\t}\n\n\t\t\tthis.setStatus(serverName, \"connected\");\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t} catch (error) {\n\t\t\tthis.clients.delete(serverName);\n\t\t\tconsole.warn(\n\t\t\t\t`Failed to connect to MCP server \"${serverName}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\tthis.scheduleReconnect(serverName);\n\t\t}\n\t}\n\n\tprivate buildToolDefs(serverName: string, mcpTools: MCPToolDefinition[]): void {\n\t\tconst safeServerName = sanitizeMcpName(serverName);\n\t\tconst client = this.clients.get(serverName)!;\n\t\tconst defs: ToolDefinition[] = mcpTools.map((tool) => ({\n\t\t\tname: `mcp_${safeServerName}_${sanitizeMcpName(tool.name)}`,\n\t\t\tlabel: `mcp.${serverName}.${tool.name}`,\n\t\t\tdescription: tool.description\n\t\t\t\t? `${tool.description} (from MCP server \"${serverName}\")`\n\t\t\t\t: `Tool from MCP server \"${serverName}\"`,\n\t\t\tpromptSnippet: tool.description\n\t\t\t\t? `[${serverName}] ${tool.description.split(\"\\n\")[0]}`\n\t\t\t\t: `[${serverName}] ${tool.name}`,\n\t\t\tparameters: toTypeBox(tool.inputSchema),\n\t\t\trenderShell: \"default\" as const,\n\t\t\texecute: async (_id, params, signal) => {\n\t\t\t\tif (!client.isConnected) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"text\" as const,\n\t\t\t\t\t\t\t\ttext: `MCP server \"${serverName}\" is disconnected. Use /mcp to check status.`,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t\tdetails: { error: \"disconnected\" },\n\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await client.callTool(tool.name, params as Record<string, unknown>, signal);\n\t\t\t\t\tconst text = result.content\n\t\t\t\t\t\t.map((item) => (item.type === \"text\" && item.text ? item.text : `[Image: ${item.mimeType}]`))\n\t\t\t\t\t\t.join(\"\\n\");\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text }],\n\t\t\t\t\t\tdetails: result,\n\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst msg = error instanceof Error ? error.message : String(error);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [{ type: \"text\" as const, text: `MCP tool error: ${msg}` }],\n\t\t\t\t\t\tdetails: { error: msg },\n\t\t\t\t\t} satisfies AgentToolResult<unknown>;\n\t\t\t\t}\n\t\t\t},\n\t\t}));\n\t\tthis.tools.set(serverName, defs);\n\t}\n\n\tprivate scheduleReconnect(serverName: string): void {\n\t\t// Cancel any existing reconnect timer for this server\n\t\tconst existing = this.reconnectTimers.get(serverName);\n\t\tif (existing) {\n\t\t\tclearTimeout(existing);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) return;\n\n\t\tconst reconnectCfg = config.reconnect ?? {};\n\t\tif (reconnectCfg.enabled === false) return;\n\n\t\tconst maxAttempts = reconnectCfg.maxAttempts ?? DEFAULT_RECONNECT_MAX_ATTEMPTS;\n\t\tconst intervalMs = reconnectCfg.intervalMs ?? DEFAULT_RECONNECT_INTERVAL_MS;\n\t\tconst attempt = (this.reconnectAttempts.get(serverName) ?? 0) + 1;\n\n\t\tif (attempt > maxAttempts) {\n\t\t\tconsole.warn(`MCP server \"${serverName}\": max reconnection attempts (${maxAttempts}) reached, giving up.`);\n\t\t\tthis.reconnectAttempts.delete(serverName);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.set(serverName, attempt);\n\t\tthis.setStatus(serverName, \"reconnecting\");\n\n\t\tconst timer = setTimeout(async () => {\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t\tconst cfg = this.serverConfigs.get(serverName);\n\t\t\tif (!cfg) return;\n\n\t\t\t// Remove old client if any\n\t\t\tconst oldClient = this.clients.get(serverName);\n\t\t\tif (oldClient) {\n\t\t\t\toldClient.onDisconnect = null;\n\t\t\t\tawait oldClient.disconnect().catch(() => {});\n\t\t\t\tthis.clients.delete(serverName);\n\t\t\t}\n\n\t\t\tawait this.connectServer(serverName, cfg);\n\t\t}, intervalMs * Math.min(attempt, 5)); // exponential backoff, cap at 5x\n\n\t\tthis.reconnectTimers.set(serverName, timer);\n\t}\n\n\t/** Manually trigger reconnection for a specific server */\n\tasync reconnect(serverName: string): Promise<void> {\n\t\tconst existingTimer = this.reconnectTimers.get(serverName);\n\t\tif (existingTimer) {\n\t\t\tclearTimeout(existingTimer);\n\t\t\tthis.reconnectTimers.delete(serverName);\n\t\t}\n\n\t\tconst oldClient = this.clients.get(serverName);\n\t\tif (oldClient) {\n\t\t\toldClient.onDisconnect = null;\n\t\t\tawait oldClient.disconnect().catch(() => {});\n\t\t\tthis.clients.delete(serverName);\n\t\t}\n\n\t\tconst config = this.serverConfigs.get(serverName);\n\t\tif (!config) {\n\t\t\tthis.setStatus(serverName, \"disconnected\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.reconnectAttempts.delete(serverName);\n\t\tawait this.connectServer(serverName, config);\n\t}\n\n\t// Rebuild tool definitions for a server (used after reconnect to pick up any changes)\n\tgetToolDefinitions(): ToolDefinition[] {\n\t\treturn Array.from(this.tools.values()).flat();\n\t}\n\n\tgetServerNames(): string[] {\n\t\treturn [...this.clients.keys()];\n\t}\n\n\tgetResourceUris(): string[] {\n\t\treturn this.resourceUris;\n\t}\n\n\tasync readResource(uri: string): Promise<string> {\n\t\tconst serverName = this.clients.keys().next().value;\n\t\tif (!serverName) throw new Error(\"No MCP servers connected\");\n\t\tconst mcpUri = uri.startsWith(`mcp://${serverName}`) ? uri.slice(`mcp://${serverName}`.length) : uri;\n\t\tconst client = this.clients.get(serverName);\n\t\tif (!client) throw new Error(`MCP server \"${serverName}\" not connected`);\n\t\tconst result = await client.readResource(mcpUri);\n\t\treturn result.contents.map((c) => c.text ?? \"\").join(\"\\n\");\n\t}\n\n\tasync stop(): Promise<void> {\n\t\t// Cancel all reconnect timers\n\t\tfor (const [, timer] of this.reconnectTimers) {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t\tthis.reconnectTimers.clear();\n\t\tthis.reconnectAttempts.clear();\n\n\t\tconst pending = [...this.clients.entries()].map(async ([name, client]) => {\n\t\t\ttry {\n\t\t\t\tawait client.disconnect();\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`Error disconnecting MCP server \"${name}\": ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t\tthis.clients.clear();\n\t\tthis.tools.clear();\n\t\tthis.resourceUris = [];\n\t\tthis._statuses.clear();\n\t\tawait Promise.all(pending);\n\t}\n}\n"]}
|
package/dist/core/mcp/types.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP (Model Context Protocol) client types.
|
|
3
3
|
*/
|
|
4
|
+
export interface MCPReconnectConfig {
|
|
5
|
+
/** Whether auto-reconnect is enabled (default: true) */
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
/** Maximum reconnection attempts before giving up (default: 5) */
|
|
8
|
+
maxAttempts?: number;
|
|
9
|
+
/** Delay between reconnection attempts in milliseconds (default: 5000) */
|
|
10
|
+
intervalMs?: number;
|
|
11
|
+
}
|
|
4
12
|
export interface MCPServerConfig {
|
|
5
13
|
enabled?: boolean;
|
|
6
14
|
command?: string;
|
|
@@ -11,7 +19,11 @@ export interface MCPServerConfig {
|
|
|
11
19
|
headers?: Record<string, string>;
|
|
12
20
|
/** Request timeout in milliseconds (default: 120000). 0 = no timeout. */
|
|
13
21
|
timeoutMs?: number;
|
|
22
|
+
/** Auto-reconnect configuration */
|
|
23
|
+
reconnect?: MCPReconnectConfig;
|
|
14
24
|
}
|
|
25
|
+
/** Connection status for an MCP server */
|
|
26
|
+
export type MCPConnectionStatus = "connected" | "disconnected" | "reconnecting" | "disabled";
|
|
15
27
|
export interface MCPResource {
|
|
16
28
|
uri: string;
|
|
17
29
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IAClC,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAC/B;AAED,0CAA0C;AAC1C,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,GAAG,UAAU,CAAC;AAE7F,MAAM,WAAW,WAAW;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,KAAK,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;CACH;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACF;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB","sourcesContent":["/**\n * MCP (Model Context Protocol) client types.\n */\n\nexport interface MCPReconnectConfig {\n\t/** Whether auto-reconnect is enabled (default: true) */\n\tenabled?: boolean;\n\t/** Maximum reconnection attempts before giving up (default: 5) */\n\tmaxAttempts?: number;\n\t/** Delay between reconnection attempts in milliseconds (default: 5000) */\n\tintervalMs?: number;\n}\n\nexport interface MCPServerConfig {\n\tenabled?: boolean;\n\tcommand?: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n\turl?: string;\n\ttransport?: \"stdio\" | \"sse\" | \"http\";\n\theaders?: Record<string, string>;\n\t/** Request timeout in milliseconds (default: 120000). 0 = no timeout. */\n\ttimeoutMs?: number;\n\t/** Auto-reconnect configuration */\n\treconnect?: MCPReconnectConfig;\n}\n\n/** Connection status for an MCP server */\nexport type MCPConnectionStatus = \"connected\" | \"disconnected\" | \"reconnecting\" | \"disabled\";\n\nexport interface MCPResource {\n\turi: string;\n\tname: string;\n\tdescription?: string;\n\tmimeType?: string;\n}\n\nexport interface MCPResourceTemplate {\n\turiTemplate: string;\n\tname: string;\n\tdescription?: string;\n}\n\nexport interface MCPReadResourceResult {\n\tcontents: Array<{\n\t\turi: string;\n\t\tmimeType?: string;\n\t\ttext?: string;\n\t\tblob?: string;\n\t}>;\n}\n\nexport interface MCPToolDefinition {\n\tname: string;\n\tdescription?: string;\n\tinputSchema: {\n\t\ttype: string;\n\t\tproperties?: Record<string, unknown>;\n\t\trequired?: string[];\n\t};\n}\n\nexport interface MCPCallToolResult {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tisError?: boolean;\n}\n"]}
|
package/dist/core/mcp/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * MCP (Model Context Protocol) client types.\n */\nexport interface MCPServerConfig {\n\tenabled?: boolean;\n\tcommand?: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n\turl?: string;\n\ttransport?: \"stdio\" | \"sse\" | \"http\";\n\theaders?: Record<string, string>;\n\t/** Request timeout in milliseconds (default: 120000). 0 = no timeout. */\n\ttimeoutMs?: number;\n}\n\nexport interface MCPResource {\n\turi: string;\n\tname: string;\n\tdescription?: string;\n\tmimeType?: string;\n}\n\nexport interface MCPResourceTemplate {\n\turiTemplate: string;\n\tname: string;\n\tdescription?: string;\n}\n\nexport interface MCPReadResourceResult {\n\tcontents: Array<{\n\t\turi: string;\n\t\tmimeType?: string;\n\t\ttext?: string;\n\t\tblob?: string;\n\t}>;\n}\n\nexport interface MCPToolDefinition {\n\tname: string;\n\tdescription?: string;\n\tinputSchema: {\n\t\ttype: string;\n\t\tproperties?: Record<string, unknown>;\n\t\trequired?: string[];\n\t};\n}\n\nexport interface MCPCallToolResult {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tisError?: boolean;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/mcp/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * MCP (Model Context Protocol) client types.\n */\n\nexport interface MCPReconnectConfig {\n\t/** Whether auto-reconnect is enabled (default: true) */\n\tenabled?: boolean;\n\t/** Maximum reconnection attempts before giving up (default: 5) */\n\tmaxAttempts?: number;\n\t/** Delay between reconnection attempts in milliseconds (default: 5000) */\n\tintervalMs?: number;\n}\n\nexport interface MCPServerConfig {\n\tenabled?: boolean;\n\tcommand?: string;\n\targs?: string[];\n\tenv?: Record<string, string>;\n\turl?: string;\n\ttransport?: \"stdio\" | \"sse\" | \"http\";\n\theaders?: Record<string, string>;\n\t/** Request timeout in milliseconds (default: 120000). 0 = no timeout. */\n\ttimeoutMs?: number;\n\t/** Auto-reconnect configuration */\n\treconnect?: MCPReconnectConfig;\n}\n\n/** Connection status for an MCP server */\nexport type MCPConnectionStatus = \"connected\" | \"disconnected\" | \"reconnecting\" | \"disabled\";\n\nexport interface MCPResource {\n\turi: string;\n\tname: string;\n\tdescription?: string;\n\tmimeType?: string;\n}\n\nexport interface MCPResourceTemplate {\n\turiTemplate: string;\n\tname: string;\n\tdescription?: string;\n}\n\nexport interface MCPReadResourceResult {\n\tcontents: Array<{\n\t\turi: string;\n\t\tmimeType?: string;\n\t\ttext?: string;\n\t\tblob?: string;\n\t}>;\n}\n\nexport interface MCPToolDefinition {\n\tname: string;\n\tdescription?: string;\n\tinputSchema: {\n\t\ttype: string;\n\t\tproperties?: Record<string, unknown>;\n\t\trequired?: string[];\n\t};\n}\n\nexport interface MCPCallToolResult {\n\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\tisError?: boolean;\n}\n"]}
|
|
@@ -103,6 +103,11 @@ export interface Settings {
|
|
|
103
103
|
transport?: "stdio" | "sse" | "http";
|
|
104
104
|
headers?: Record<string, string>;
|
|
105
105
|
timeoutMs?: number;
|
|
106
|
+
reconnect?: {
|
|
107
|
+
enabled?: boolean;
|
|
108
|
+
maxAttempts?: number;
|
|
109
|
+
intervalMs?: number;
|
|
110
|
+
};
|
|
106
111
|
}>;
|
|
107
112
|
}
|
|
108
113
|
export type SettingsScope = "global" | "project";
|