@skilder-ai/runtime 0.7.4 → 0.7.5
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/dist/index.js +274 -19
- package/dist/index.js.map +3 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -138392,6 +138392,7 @@ var McpServerStatus = /* @__PURE__ */ ((McpServerStatus2) => {
|
|
|
138392
138392
|
McpServerStatus2["Connected"] = "CONNECTED";
|
|
138393
138393
|
McpServerStatus2["Disconnected"] = "DISCONNECTED";
|
|
138394
138394
|
McpServerStatus2["Error"] = "ERROR";
|
|
138395
|
+
McpServerStatus2["Idle"] = "IDLE";
|
|
138395
138396
|
McpServerStatus2["Pending"] = "PENDING";
|
|
138396
138397
|
McpServerStatus2["Starting"] = "STARTING";
|
|
138397
138398
|
return McpServerStatus2;
|
|
@@ -152624,10 +152625,29 @@ var ToolService = class ToolService2 extends Service {
|
|
|
152624
152625
|
this.rxSubscriptions = [];
|
|
152625
152626
|
this.sdkBundleContent = null;
|
|
152626
152627
|
this.sdkBundleLoadPromise = null;
|
|
152628
|
+
this.dormantConfigs = /* @__PURE__ */ new Map();
|
|
152629
|
+
this.idleTimers = /* @__PURE__ */ new Map();
|
|
152630
|
+
this.startingServers = /* @__PURE__ */ new Map();
|
|
152631
|
+
this.stoppingServers = /* @__PURE__ */ new Map();
|
|
152632
|
+
this.activationFailures = /* @__PURE__ */ new Map();
|
|
152633
|
+
this.maxActivationRetries = 3;
|
|
152634
|
+
this.activationCooldownMs = 6e4;
|
|
152635
|
+
this.lazyStartEnabled = true;
|
|
152636
|
+
this.idleTimeoutMs = 3e5;
|
|
152627
152637
|
this.logger = this.loggerService.getLogger(this.name);
|
|
152628
152638
|
}
|
|
152629
152639
|
async initialize() {
|
|
152630
152640
|
this.logger.info("Starting");
|
|
152641
|
+
const lazyStartRaw = (process.env.MCP_LAZY_START ?? "true").toLowerCase();
|
|
152642
|
+
this.lazyStartEnabled = !["false", "0", "no", "off"].includes(lazyStartRaw);
|
|
152643
|
+
const parsedTimeout = parseInt(process.env.MCP_IDLE_TIMEOUT_MS ?? "300000", 10);
|
|
152644
|
+
if (Number.isNaN(parsedTimeout) || parsedTimeout <= 0) {
|
|
152645
|
+
this.logger.warn({ rawValue: process.env.MCP_IDLE_TIMEOUT_MS }, "Invalid MCP_IDLE_TIMEOUT_MS value, falling back to default 300000ms");
|
|
152646
|
+
this.idleTimeoutMs = 3e5;
|
|
152647
|
+
} else {
|
|
152648
|
+
this.idleTimeoutMs = parsedTimeout;
|
|
152649
|
+
}
|
|
152650
|
+
this.logger.info({ lazyStart: this.lazyStartEnabled, idleTimeoutMs: this.idleTimeoutMs }, "Lazy-start configuration");
|
|
152631
152651
|
await this.authService.waitForStarted();
|
|
152632
152652
|
await this.natsService.waitForStarted();
|
|
152633
152653
|
await this.healthService.waitForStarted();
|
|
@@ -152662,7 +152682,7 @@ var ToolService = class ToolService2 extends Service {
|
|
|
152662
152682
|
this.natsService.publish(message);
|
|
152663
152683
|
this.logger.debug(`Published MCP server status: ${mcpServerId} -> ${status}${error48 ? ` (${error48})` : ""}`);
|
|
152664
152684
|
} catch (publishError) {
|
|
152665
|
-
this.logger.error(
|
|
152685
|
+
this.logger.error({ mcpServerId, status, err: publishError }, "Failed to publish MCP server status");
|
|
152666
152686
|
}
|
|
152667
152687
|
}
|
|
152668
152688
|
async shutdown() {
|
|
@@ -152693,19 +152713,25 @@ var ToolService = class ToolService2 extends Service {
|
|
|
152693
152713
|
this.logger.debug(`Processing MCP Servers update with ${msg.data.mcpServers.length} MCP Servers`);
|
|
152694
152714
|
this.roots = msg.data.roots;
|
|
152695
152715
|
const mcpServerIds = msg.data.mcpServers.map((mcpServer) => mcpServer.id);
|
|
152696
|
-
const
|
|
152697
|
-
|
|
152716
|
+
const runningToStop = Array.from(this.mcpServers.keys()).filter((id) => !mcpServerIds.includes(id));
|
|
152717
|
+
const dormantToRemove = Array.from(this.dormantConfigs.keys()).filter((id) => !mcpServerIds.includes(id));
|
|
152718
|
+
for (const mcpServerId of runningToStop) {
|
|
152698
152719
|
const service = this.mcpServers.get(mcpServerId);
|
|
152699
152720
|
await this.stopMCPServer({ id: mcpServerId, name: service.getName() });
|
|
152700
152721
|
}
|
|
152722
|
+
for (const mcpServerId of dormantToRemove) {
|
|
152723
|
+
this.removeDormantServer(mcpServerId);
|
|
152724
|
+
}
|
|
152701
152725
|
for (const mcpServer of msg.data.mcpServers) {
|
|
152702
|
-
await this.
|
|
152703
|
-
this.logger.error(
|
|
152726
|
+
await this.registerMCPServer(mcpServer).catch(async (error48) => {
|
|
152727
|
+
this.logger.error({ mcpServerId: mcpServer.id, mcpServerName: mcpServer.name, err: error48 }, "Failed to register MCP Server");
|
|
152728
|
+
this.publishMCPServerStatus(mcpServer.workspace?.id, mcpServer.id, "ERROR", error48 instanceof Error ? error48.message : String(error48));
|
|
152704
152729
|
const service = this.mcpServers.get(mcpServer.id);
|
|
152705
152730
|
if (service) {
|
|
152706
152731
|
await this.stopService(service);
|
|
152707
152732
|
}
|
|
152708
152733
|
this.mcpServers.delete(mcpServer.id);
|
|
152734
|
+
this.removeDormantServer(mcpServer.id);
|
|
152709
152735
|
});
|
|
152710
152736
|
}
|
|
152711
152737
|
}
|
|
@@ -152733,6 +152759,13 @@ var ToolService = class ToolService2 extends Service {
|
|
|
152733
152759
|
}
|
|
152734
152760
|
this.cacheSubscriptions = [];
|
|
152735
152761
|
}
|
|
152762
|
+
if (this.startingServers.size > 0 || this.stoppingServers.size > 0) {
|
|
152763
|
+
this.logger.debug({ starting: this.startingServers.size, stopping: this.stoppingServers.size }, "Waiting for in-flight MCP server activations/stops to settle");
|
|
152764
|
+
await Promise.allSettled([
|
|
152765
|
+
...Array.from(this.startingServers.values()),
|
|
152766
|
+
...Array.from(this.stoppingServers.values())
|
|
152767
|
+
]);
|
|
152768
|
+
}
|
|
152736
152769
|
const serverIds = Array.from(this.mcpServers.keys());
|
|
152737
152770
|
for (const mcpServerId of serverIds) {
|
|
152738
152771
|
const service = this.mcpServers.get(mcpServerId);
|
|
@@ -152740,20 +152773,76 @@ var ToolService = class ToolService2 extends Service {
|
|
|
152740
152773
|
continue;
|
|
152741
152774
|
await this.stopMCPServer({ id: mcpServerId, name: service.getName() });
|
|
152742
152775
|
}
|
|
152776
|
+
for (const mcpServerId of Array.from(this.dormantConfigs.keys())) {
|
|
152777
|
+
this.removeDormantServer(mcpServerId);
|
|
152778
|
+
}
|
|
152779
|
+
for (const timer of this.idleTimers.values()) {
|
|
152780
|
+
clearTimeout(timer);
|
|
152781
|
+
}
|
|
152782
|
+
this.idleTimers.clear();
|
|
152743
152783
|
}
|
|
152744
152784
|
/**
|
|
152745
|
-
*
|
|
152746
|
-
*
|
|
152785
|
+
* Register an MCP Server. For STDIO servers with lazy-start enabled, the server is registered
|
|
152786
|
+
* as dormant (config stored, NATS tool subscriptions active, no subprocess spawned).
|
|
152787
|
+
* For non-STDIO servers or when lazy-start is disabled, the server is activated immediately.
|
|
152747
152788
|
*/
|
|
152748
|
-
async
|
|
152789
|
+
async registerMCPServer(mcpServer) {
|
|
152749
152790
|
const roots = this.getRoots();
|
|
152750
|
-
const
|
|
152751
|
-
|
|
152752
|
-
|
|
152753
|
-
|
|
152754
|
-
this.ensureToolsSubscribed(mcpServer.id,
|
|
152791
|
+
const newSignature = getConfigSignature(mcpServer, roots);
|
|
152792
|
+
const existingRunning = this.mcpServers.get(mcpServer.id);
|
|
152793
|
+
if (existingRunning && getConfigSignature(existingRunning.getConfig(), existingRunning.getRoots()) === newSignature) {
|
|
152794
|
+
this.logger.debug(`MCPServer ${mcpServer.name} already running -> skipping`);
|
|
152795
|
+
this.ensureToolsSubscribed(mcpServer.id, mcpServer.tools ?? []);
|
|
152796
|
+
return;
|
|
152797
|
+
}
|
|
152798
|
+
const existingDormant = this.dormantConfigs.get(mcpServer.id);
|
|
152799
|
+
if (existingDormant && existingDormant.configSignature === newSignature) {
|
|
152800
|
+
this.logger.debug(`MCPServer ${mcpServer.name} already dormant with same config -> skipping`);
|
|
152801
|
+
this.ensureToolsSubscribed(mcpServer.id, mcpServer.tools ?? []);
|
|
152802
|
+
return;
|
|
152803
|
+
}
|
|
152804
|
+
if (existingRunning) {
|
|
152805
|
+
this.logger.debug(`MCPServer ${mcpServer.name} config changed -> stopping`);
|
|
152806
|
+
await this.stopMCPServer(mcpServer);
|
|
152807
|
+
}
|
|
152808
|
+
if (existingDormant) {
|
|
152809
|
+
this.dormantConfigs.delete(mcpServer.id);
|
|
152810
|
+
}
|
|
152811
|
+
this.activationFailures.delete(mcpServer.id);
|
|
152812
|
+
const isStdio = mcpServer.transport === dgraph_resolvers_types_exports.McpTransportType.Stdio;
|
|
152813
|
+
const hasKnownTools = (mcpServer.tools?.length ?? 0) > 0;
|
|
152814
|
+
if (this.lazyStartEnabled && isStdio && hasKnownTools) {
|
|
152815
|
+
this.logger.info(`Registering MCPServer ${mcpServer.name} as dormant (lazy-start)`);
|
|
152816
|
+
this.dormantConfigs.set(mcpServer.id, { config: mcpServer, configSignature: newSignature });
|
|
152817
|
+
if (mcpServer.workspace?.id) {
|
|
152818
|
+
this.mcpServerWorkspaces.set(mcpServer.id, mcpServer.workspace.id);
|
|
152819
|
+
}
|
|
152820
|
+
this.ensureToolsSubscribed(mcpServer.id, mcpServer.tools ?? []);
|
|
152821
|
+
this.publishMCPServerStatus(mcpServer.workspace?.id, mcpServer.id, "IDLE");
|
|
152755
152822
|
return;
|
|
152756
152823
|
}
|
|
152824
|
+
if (this.lazyStartEnabled && isStdio && !hasKnownTools) {
|
|
152825
|
+
this.logger.info(`MCPServer ${mcpServer.name} has no known tools -> activating eagerly for tool discovery`);
|
|
152826
|
+
}
|
|
152827
|
+
await this.activateMCPServer(mcpServer);
|
|
152828
|
+
}
|
|
152829
|
+
/**
|
|
152830
|
+
* Activate an MCP Server: spawn the subprocess, connect, and start observing tools.
|
|
152831
|
+
* Can be called eagerly (from registerMCPServer) or on-demand (from ensureServerActive).
|
|
152832
|
+
*/
|
|
152833
|
+
async activateMCPServer(mcpServerOrId) {
|
|
152834
|
+
let mcpServer;
|
|
152835
|
+
let isDormantActivation = false;
|
|
152836
|
+
if (typeof mcpServerOrId === "string") {
|
|
152837
|
+
const dormant = this.dormantConfigs.get(mcpServerOrId);
|
|
152838
|
+
if (!dormant)
|
|
152839
|
+
throw new Error(`No dormant config found for MCP Server ${mcpServerOrId}`);
|
|
152840
|
+
mcpServer = dormant.config;
|
|
152841
|
+
isDormantActivation = true;
|
|
152842
|
+
} else {
|
|
152843
|
+
mcpServer = mcpServerOrId;
|
|
152844
|
+
}
|
|
152845
|
+
const roots = this.getRoots();
|
|
152757
152846
|
let authProvider;
|
|
152758
152847
|
if (mcpServer.oauthProvider === "MCP_OAUTH") {
|
|
152759
152848
|
const identity = this.authService.getIdentity();
|
|
@@ -152786,7 +152875,7 @@ var ToolService = class ToolService2 extends Service {
|
|
|
152786
152875
|
}
|
|
152787
152876
|
}
|
|
152788
152877
|
const mcpServerService = this.toolServerServiceFactory(mcpServer, roots, authProvider);
|
|
152789
|
-
this.logger.info(`
|
|
152878
|
+
this.logger.info(`Activating MCPServer: ${mcpServer.name} with ${mcpServer.tools?.length ?? 0} tools, and roots: ${JSON.stringify(roots)}`);
|
|
152790
152879
|
if (this.mcpServers.has(mcpServer.id)) {
|
|
152791
152880
|
this.logger.debug(`MCPServer ${mcpServer.name} already running -> shutting down`);
|
|
152792
152881
|
await this.stopMCPServer(mcpServer);
|
|
@@ -152804,11 +152893,32 @@ var ToolService = class ToolService2 extends Service {
|
|
|
152804
152893
|
this.mcpServerWorkspaces.delete(mcpServer.id);
|
|
152805
152894
|
throw error48;
|
|
152806
152895
|
}
|
|
152896
|
+
if (isDormantActivation) {
|
|
152897
|
+
this.dormantConfigs.delete(mcpServer.id);
|
|
152898
|
+
}
|
|
152807
152899
|
let hasPublishedConnected = false;
|
|
152808
152900
|
mcpServerService.observeTools().subscribe({
|
|
152809
152901
|
error: (err) => {
|
|
152810
|
-
this.logger.error(
|
|
152902
|
+
this.logger.error({ mcpServerId: mcpServer.id, err }, "Tools observable error for MCP Server");
|
|
152811
152903
|
this.publishMCPServerStatus(mcpServer.workspace?.id, mcpServer.id, "ERROR", err instanceof Error ? err.message : String(err));
|
|
152904
|
+
this.mcpServers.delete(mcpServer.id);
|
|
152905
|
+
this.mcpTools.delete(mcpServer.id);
|
|
152906
|
+
const idleTimer = this.idleTimers.get(mcpServer.id);
|
|
152907
|
+
if (idleTimer) {
|
|
152908
|
+
clearTimeout(idleTimer);
|
|
152909
|
+
this.idleTimers.delete(mcpServer.id);
|
|
152910
|
+
}
|
|
152911
|
+
const stopPromise = this.stopService(mcpServerService).catch((stopErr) => this.logger.error({ mcpServerId: mcpServer.id, err: stopErr }, "Failed to stop errored MCP server")).then(() => {
|
|
152912
|
+
this.stoppingServers.delete(mcpServer.id);
|
|
152913
|
+
if (this.lazyStartEnabled && mcpServer.transport === dgraph_resolvers_types_exports.McpTransportType.Stdio) {
|
|
152914
|
+
this.dormantConfigs.set(mcpServer.id, {
|
|
152915
|
+
config: mcpServer,
|
|
152916
|
+
configSignature: getConfigSignature(mcpServer, roots)
|
|
152917
|
+
});
|
|
152918
|
+
this.publishMCPServerStatus(mcpServer.workspace?.id, mcpServer.id, "IDLE");
|
|
152919
|
+
}
|
|
152920
|
+
});
|
|
152921
|
+
this.stoppingServers.set(mcpServer.id, stopPromise);
|
|
152812
152922
|
},
|
|
152813
152923
|
next: (tools2) => {
|
|
152814
152924
|
this.logger.debug(`Updating tools for MCP Server ${mcpServer.id} with ${tools2.length} tools`);
|
|
@@ -152829,13 +152939,119 @@ var ToolService = class ToolService2 extends Service {
|
|
|
152829
152939
|
});
|
|
152830
152940
|
this.natsService.publish(message);
|
|
152831
152941
|
} catch (publishError) {
|
|
152832
|
-
this.logger.
|
|
152942
|
+
this.logger.error({ mcpServerId: mcpServer.id, err: publishError }, "Failed to publish discovered tools \u2014 backend may show stale tool list");
|
|
152833
152943
|
}
|
|
152834
152944
|
}
|
|
152835
152945
|
});
|
|
152836
152946
|
const tools = mcpServer.tools ?? [];
|
|
152837
152947
|
this.ensureToolsSubscribed(mcpServer.id, tools);
|
|
152838
|
-
this.
|
|
152948
|
+
if (this.lazyStartEnabled && mcpServer.transport === dgraph_resolvers_types_exports.McpTransportType.Stdio) {
|
|
152949
|
+
this.resetIdleTimer(mcpServer.id);
|
|
152950
|
+
}
|
|
152951
|
+
this.logger.info(`MCPServer ${mcpServer.name} activated`);
|
|
152952
|
+
return mcpServerService;
|
|
152953
|
+
}
|
|
152954
|
+
/**
|
|
152955
|
+
* Ensure a dormant server is activated, deduplicating concurrent activation requests.
|
|
152956
|
+
* Waits for any in-flight stop to complete before re-activating.
|
|
152957
|
+
*/
|
|
152958
|
+
async ensureServerActive(mcpServerId) {
|
|
152959
|
+
const existing = this.startingServers.get(mcpServerId);
|
|
152960
|
+
if (existing)
|
|
152961
|
+
return existing;
|
|
152962
|
+
const stopping = this.stoppingServers.get(mcpServerId);
|
|
152963
|
+
if (stopping) {
|
|
152964
|
+
await stopping;
|
|
152965
|
+
}
|
|
152966
|
+
const promise2 = this.activateMCPServer(mcpServerId);
|
|
152967
|
+
this.startingServers.set(mcpServerId, promise2);
|
|
152968
|
+
try {
|
|
152969
|
+
const service = await promise2;
|
|
152970
|
+
if (!this.mcpServers.has(mcpServerId)) {
|
|
152971
|
+
throw new Error(`MCP server ${mcpServerId} was deregistered during activation`);
|
|
152972
|
+
}
|
|
152973
|
+
return service;
|
|
152974
|
+
} finally {
|
|
152975
|
+
this.startingServers.delete(mcpServerId);
|
|
152976
|
+
}
|
|
152977
|
+
}
|
|
152978
|
+
/**
|
|
152979
|
+
* Reset the idle timer for a lazy-started server.
|
|
152980
|
+
*/
|
|
152981
|
+
resetIdleTimer(mcpServerId) {
|
|
152982
|
+
const existing = this.idleTimers.get(mcpServerId);
|
|
152983
|
+
if (existing)
|
|
152984
|
+
clearTimeout(existing);
|
|
152985
|
+
const timer = setTimeout(() => {
|
|
152986
|
+
this.idleShutdown(mcpServerId).catch((err) => this.logger.error({ mcpServerId, err }, "Idle shutdown failed"));
|
|
152987
|
+
}, this.idleTimeoutMs);
|
|
152988
|
+
this.idleTimers.set(mcpServerId, timer);
|
|
152989
|
+
}
|
|
152990
|
+
/**
|
|
152991
|
+
* Shut down an idle MCP server and move it back to dormant state.
|
|
152992
|
+
* NATS tool subscriptions and mcpTools are kept alive for on-demand re-activation.
|
|
152993
|
+
*/
|
|
152994
|
+
async idleShutdown(mcpServerId) {
|
|
152995
|
+
if (this.startingServers.has(mcpServerId)) {
|
|
152996
|
+
this.resetIdleTimer(mcpServerId);
|
|
152997
|
+
return;
|
|
152998
|
+
}
|
|
152999
|
+
const server = this.mcpServers.get(mcpServerId);
|
|
153000
|
+
if (!server) {
|
|
153001
|
+
this.logger.debug({ mcpServerId }, "Idle timer fired for server not in mcpServers map, ignoring");
|
|
153002
|
+
this.idleTimers.delete(mcpServerId);
|
|
153003
|
+
return;
|
|
153004
|
+
}
|
|
153005
|
+
const config2 = server.getConfig();
|
|
153006
|
+
const roots = server.getRoots();
|
|
153007
|
+
this.logger.info(`Idle shutdown for MCP server ${config2.name}`);
|
|
153008
|
+
try {
|
|
153009
|
+
await this.stopService(server);
|
|
153010
|
+
} catch (err) {
|
|
153011
|
+
this.logger.error({ mcpServerId, err }, "Error stopping service during idle shutdown");
|
|
153012
|
+
this.mcpServers.delete(mcpServerId);
|
|
153013
|
+
this.idleTimers.delete(mcpServerId);
|
|
153014
|
+
this.dormantConfigs.set(mcpServerId, {
|
|
153015
|
+
config: config2,
|
|
153016
|
+
configSignature: getConfigSignature(config2, roots)
|
|
153017
|
+
});
|
|
153018
|
+
const workspaceId2 = this.mcpServerWorkspaces.get(mcpServerId);
|
|
153019
|
+
this.publishMCPServerStatus(workspaceId2, mcpServerId, "IDLE");
|
|
153020
|
+
return;
|
|
153021
|
+
}
|
|
153022
|
+
this.mcpServers.delete(mcpServerId);
|
|
153023
|
+
this.dormantConfigs.set(mcpServerId, {
|
|
153024
|
+
config: config2,
|
|
153025
|
+
configSignature: getConfigSignature(config2, roots)
|
|
153026
|
+
});
|
|
153027
|
+
const workspaceId = this.mcpServerWorkspaces.get(mcpServerId);
|
|
153028
|
+
this.publishMCPServerStatus(workspaceId, mcpServerId, "IDLE");
|
|
153029
|
+
this.idleTimers.delete(mcpServerId);
|
|
153030
|
+
}
|
|
153031
|
+
/**
|
|
153032
|
+
* Remove a dormant server completely: clean up NATS tool subscriptions, mcpTools, and config.
|
|
153033
|
+
*/
|
|
153034
|
+
removeDormantServer(mcpServerId) {
|
|
153035
|
+
this.dormantConfigs.delete(mcpServerId);
|
|
153036
|
+
this.activationFailures.delete(mcpServerId);
|
|
153037
|
+
const serverToolSubs = this.toolSubscriptions.get(mcpServerId);
|
|
153038
|
+
if (serverToolSubs) {
|
|
153039
|
+
for (const [toolId, subscription] of serverToolSubs.entries()) {
|
|
153040
|
+
try {
|
|
153041
|
+
subscription.unsubscribe();
|
|
153042
|
+
} catch (error48) {
|
|
153043
|
+
this.logger.warn({ toolId, err: error48 }, "Failed to unsubscribe dormant tool");
|
|
153044
|
+
}
|
|
153045
|
+
}
|
|
153046
|
+
this.toolSubscriptions.delete(mcpServerId);
|
|
153047
|
+
}
|
|
153048
|
+
this.mcpTools.delete(mcpServerId);
|
|
153049
|
+
this.mcpServerWorkspaces.delete(mcpServerId);
|
|
153050
|
+
const timer = this.idleTimers.get(mcpServerId);
|
|
153051
|
+
if (timer) {
|
|
153052
|
+
clearTimeout(timer);
|
|
153053
|
+
this.idleTimers.delete(mcpServerId);
|
|
153054
|
+
}
|
|
152839
153055
|
}
|
|
152840
153056
|
// Subscribe to a tool and return the subscription
|
|
152841
153057
|
subscribeToTool(tool2) {
|
|
@@ -152897,11 +153113,45 @@ var ToolService = class ToolService2 extends Service {
|
|
|
152897
153113
|
continue;
|
|
152898
153114
|
}
|
|
152899
153115
|
this.logger.debug(`Found tool ${tool2.name} in MCP Server ${mcpServerId}`);
|
|
152900
|
-
|
|
153116
|
+
let mcpServer = this.mcpServers.get(mcpServerId);
|
|
153117
|
+
if (!mcpServer && this.dormantConfigs.has(mcpServerId)) {
|
|
153118
|
+
const failures = this.activationFailures.get(mcpServerId);
|
|
153119
|
+
if (failures && failures.count >= this.maxActivationRetries) {
|
|
153120
|
+
const elapsed = Date.now() - failures.lastAttempt;
|
|
153121
|
+
if (elapsed < this.activationCooldownMs) {
|
|
153122
|
+
const retryInSec = Math.ceil((this.activationCooldownMs - elapsed) / 1e3);
|
|
153123
|
+
msg.respond(new ErrorResponse({
|
|
153124
|
+
error: `MCP server failed to start after ${failures.count} attempts. Will retry in ${retryInSec}s.`
|
|
153125
|
+
}));
|
|
153126
|
+
toolCalled = true;
|
|
153127
|
+
continue;
|
|
153128
|
+
}
|
|
153129
|
+
this.activationFailures.delete(mcpServerId);
|
|
153130
|
+
}
|
|
153131
|
+
this.logger.info(`Activating dormant MCP server ${mcpServerId} for tool call`);
|
|
153132
|
+
try {
|
|
153133
|
+
mcpServer = await this.ensureServerActive(mcpServerId);
|
|
153134
|
+
this.activationFailures.delete(mcpServerId);
|
|
153135
|
+
} catch (activationError) {
|
|
153136
|
+
const prev = this.activationFailures.get(mcpServerId);
|
|
153137
|
+
this.activationFailures.set(mcpServerId, {
|
|
153138
|
+
count: (prev?.count ?? 0) + 1,
|
|
153139
|
+
lastAttempt: Date.now()
|
|
153140
|
+
});
|
|
153141
|
+
this.logger.error({ mcpServerId, err: activationError }, "Failed to activate dormant MCP server");
|
|
153142
|
+
const userMessage = activationError instanceof Error && activationError.message.includes("No dormant config") ? "MCP server is being reconfigured, please retry" : `MCP server activation failed: ${activationError instanceof Error ? activationError.message : String(activationError)}`;
|
|
153143
|
+
msg.respond(new ErrorResponse({ error: userMessage }));
|
|
153144
|
+
toolCalled = true;
|
|
153145
|
+
continue;
|
|
153146
|
+
}
|
|
153147
|
+
}
|
|
152901
153148
|
if (!mcpServer) {
|
|
152902
153149
|
this.logger.warn(`MCP Server ${mcpServerId} not found`);
|
|
152903
153150
|
continue;
|
|
152904
153151
|
}
|
|
153152
|
+
if (this.idleTimers.has(mcpServerId)) {
|
|
153153
|
+
this.resetIdleTimer(mcpServerId);
|
|
153154
|
+
}
|
|
152905
153155
|
let _meta;
|
|
152906
153156
|
const mcpServerConfig = mcpServer.getConfig();
|
|
152907
153157
|
if (mcpServerConfig.oauthProvider && userId) {
|
|
@@ -153023,7 +153273,7 @@ var ToolService = class ToolService2 extends Service {
|
|
|
153023
153273
|
executedBy
|
|
153024
153274
|
}));
|
|
153025
153275
|
} catch (callError) {
|
|
153026
|
-
this.logger.error(
|
|
153276
|
+
this.logger.error({ mcpServerId, toolName: tool2.name, err: callError }, "Tool call failed");
|
|
153027
153277
|
msg.respond(new ErrorResponse({
|
|
153028
153278
|
error: `Tool call failed: ${callError instanceof Error ? callError.message : String(callError)}`
|
|
153029
153279
|
}));
|
|
@@ -153310,6 +153560,11 @@ ${errorOutput}`;
|
|
|
153310
153560
|
await this.stopService(service);
|
|
153311
153561
|
}
|
|
153312
153562
|
this.mcpServers.delete(mcpServer.id);
|
|
153563
|
+
const idleTimer = this.idleTimers.get(mcpServer.id);
|
|
153564
|
+
if (idleTimer) {
|
|
153565
|
+
clearTimeout(idleTimer);
|
|
153566
|
+
this.idleTimers.delete(mcpServer.id);
|
|
153567
|
+
}
|
|
153313
153568
|
const workspaceId = this.mcpServerWorkspaces.get(mcpServer.id);
|
|
153314
153569
|
this.publishMCPServerStatus(workspaceId, mcpServer.id, "DISCONNECTED");
|
|
153315
153570
|
this.mcpServerWorkspaces.delete(mcpServer.id);
|