From 7440e306f98389591602d3c0412e18a63ff8c747 Mon Sep 17 00:00:00 2001 From: Jonas Helming Date: Thu, 16 Jan 2025 23:05:05 +0100 Subject: [PATCH] Allow to auto-start MCP servers on frontend start-up fixed #14692 --- .../mcp-frontend-application-contribution.ts | 29 ++++++++++++++----- .../ai-mcp/src/browser/mcp-preferences.ts | 19 ++++++++---- .../ai-mcp/src/common/mcp-server-manager.ts | 5 ++++ 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/packages/ai-mcp/src/browser/mcp-frontend-application-contribution.ts b/packages/ai-mcp/src/browser/mcp-frontend-application-contribution.ts index a0c4206bebbbc..e6fbfedc60535 100644 --- a/packages/ai-mcp/src/browser/mcp-frontend-application-contribution.ts +++ b/packages/ai-mcp/src/browser/mcp-frontend-application-contribution.ts @@ -13,6 +13,7 @@ // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** + import { FrontendApplicationContribution, PreferenceProvider, PreferenceService } from '@theia/core/lib/browser'; import { inject, injectable } from '@theia/core/shared/inversify'; import { MCPServerDescription, MCPServerManager } from '../common'; @@ -24,6 +25,7 @@ interface MCPServersPreferenceValue { command: string; args?: string[]; env?: { [key: string]: string }; + autostart?: boolean; // Add autostart property }; interface MCPServersPreference { @@ -35,7 +37,8 @@ namespace MCPServersPreference { return !!obj && typeof obj === 'object' && 'command' in obj && typeof obj.command === 'string' && (!('args' in obj) || Array.isArray(obj.args) && obj.args.every(arg => typeof arg === 'string')) && - (!('env' in obj) || !!obj.env && typeof obj.env === 'object' && Object.values(obj.env).every(value => typeof value === 'string')); + (!('env' in obj) || !!obj.env && typeof obj.env === 'object' && Object.values(obj.env).every(value => typeof value === 'string')) && + (!('autostart' in obj) || typeof obj.autostart === 'boolean'); } } @@ -72,8 +75,9 @@ export class McpFrontendApplicationContribution implements FrontendApplicationCo MCP_SERVERS_PREF, {} )); - this.syncServers(servers); this.prevServers = this.convertToMap(servers); + this.syncServers(this.prevServers); + this.autoStartServers(this.prevServers); this.preferenceService.onPreferenceChanged(event => { if (event.preferenceName === MCP_SERVERS_PREF) { @@ -83,6 +87,18 @@ export class McpFrontendApplicationContribution implements FrontendApplicationCo }); this.frontendMCPService.registerToolsForAllStartedServers(); } + protected async autoStartServers(servers: Map): Promise { + const serverNames = servers.keys(); + const startedServers = await this.frontendMCPService.getStartedServers(); + for (const name of serverNames) { + const serverDesc = servers.get(name); + if (serverDesc && serverDesc.autostart) { + if (!startedServers.includes(name)) { + await this.frontendMCPService.startServer(name); + } + } + } + } protected handleServerChanges(newServers: MCPServersPreference): void { const oldServers = this.prevServers; @@ -116,20 +132,17 @@ export class McpFrontendApplicationContribution implements FrontendApplicationCo this.prevServers = updatedServers; } - protected syncServers(servers: MCPServersPreference): void { - const updatedServers = this.convertToMap(servers); + protected syncServers(servers: Map): void { - for (const [, description] of updatedServers) { + for (const [, description] of servers) { this.manager.addOrUpdateServer(description); } for (const [name] of this.prevServers) { - if (!updatedServers.has(name)) { + if (!servers.has(name)) { this.manager.removeServer(name); } } - - this.prevServers = updatedServers; } protected convertToMap(servers: MCPServersPreference): Map { diff --git a/packages/ai-mcp/src/browser/mcp-preferences.ts b/packages/ai-mcp/src/browser/mcp-preferences.ts index b3ff702defbf9..27f39e3f15a10 100644 --- a/packages/ai-mcp/src/browser/mcp-preferences.ts +++ b/packages/ai-mcp/src/browser/mcp-preferences.ts @@ -24,8 +24,8 @@ export const McpServersPreferenceSchema: PreferenceSchema = { [MCP_SERVERS_PREF]: { type: 'object', title: 'MCP Servers Configuration', - markdownDescription: 'Configure MCP servers with command, arguments and optionally environment variables. Each server is identified by a unique key, such as\ - "brave-search" or "filesystem".\ + markdownDescription: 'Configure MCP servers with command, arguments, optionally environment variables, and autostart.\ + Each server is identified by a unique key, such as "brave-search" or "filesystem".\ To start a server, use the "MCP: Start MCP Server" command, which enables you to select the desired server.\ To stop a server, use the "MCP: Stop MCP Server" command.\ \n\ @@ -40,17 +40,18 @@ export const McpServersPreferenceSchema: PreferenceSchema = { ],\n\ "env": {\n\ "BRAVE_API_KEY": "YOUR_API_KEY"\n\ - }\n\ + },\n\ + "autostart": true\n\ },\n\ "filesystem": {\n\ "command": "npx",\n\ "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/YOUR_USERNAME/Desktop"],\n\ "env": {\n\ "CUSTOM_ENV_VAR": "custom-value"\n\ - }\n\ + },\n\ + "autostart": false\n\ }\n\ - }\ - ```', + }\n ```', additionalProperties: { type: 'object', properties: { @@ -74,6 +75,12 @@ export const McpServersPreferenceSchema: PreferenceSchema = { additionalProperties: { type: 'string' } + }, + autostart: { + type: 'boolean', + title: 'Autostart', + markdownDescription: 'Automatically start this server when the frontend starts. Newly added servers are not immediatly auto stated.', + default: false } }, required: ['command', 'args'] diff --git a/packages/ai-mcp/src/common/mcp-server-manager.ts b/packages/ai-mcp/src/common/mcp-server-manager.ts index e316a644a27c6..18414a3312781 100644 --- a/packages/ai-mcp/src/common/mcp-server-manager.ts +++ b/packages/ai-mcp/src/common/mcp-server-manager.ts @@ -52,6 +52,11 @@ export interface MCPServerDescription { * Optional environment variables to set when starting the server. */ env?: { [key: string]: string }; + + /** + * Flag indicating whether the server should automatically start when the application starts. + */ + autostart?: boolean; } export const MCPServerManager = Symbol('MCPServerManager');