@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 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(`Failed to publish MCP server status ${mcpServerId} -> ${status}: ${publishError}`);
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 mcpServersToStop = Array.from(this.mcpServers.keys()).filter((mcpServerId) => !mcpServerIds.includes(mcpServerId));
152697
- for (const mcpServerId of mcpServersToStop) {
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.spawnMCPServer(mcpServer).catch(async (error48) => {
152703
- this.logger.error(`Failed to spawn MCP Server ${mcpServer.name}: ${error48}`);
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
- * Spawn an MCP Server
152746
- * - the mcpServer argument contains the list of tools and capabilities that the MCP Server advertises
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 spawnMCPServer(mcpServer) {
152789
+ async registerMCPServer(mcpServer) {
152749
152790
  const roots = this.getRoots();
152750
- const existingMcpServer = this.mcpServers.get(mcpServer.id);
152751
- if (existingMcpServer && getConfigSignature(existingMcpServer.getConfig(), existingMcpServer.getRoots()) === getConfigSignature(mcpServer, roots)) {
152752
- this.logger.debug(`MCPServer ${mcpServer.name} already running -> skipping spawn`);
152753
- const tools2 = mcpServer.tools ?? [];
152754
- this.ensureToolsSubscribed(mcpServer.id, tools2);
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(`Spawning MCPServer: ${mcpServer.name} with ${mcpServer.tools?.length ?? 0} tools, and roots: ${JSON.stringify(roots)}`);
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(`Tools observable error for MCP Server ${mcpServer.id}: ${err}`);
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.warn(`Failed to publish discovered tools for ${mcpServer.id}: ${publishError}`);
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.logger.info(`MCPServer ${mcpServer.name} spawned`);
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
- const mcpServer = this.mcpServers.get(mcpServerId);
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(`Tool call ${tool2.name} failed: ${callError}`);
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);