From 088d6371670fc79b7faed778ac1f1cf7ac810c72 Mon Sep 17 00:00:00 2001 From: Brijesh K R Date: Thu, 27 Nov 2025 19:13:46 +0530 Subject: [PATCH 1/4] fix: Fix blank configuration screen for stdio mcp servers without any fields. --- source/wizard/steps/mcp-step.tsx | 34 +++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/source/wizard/steps/mcp-step.tsx b/source/wizard/steps/mcp-step.tsx index 2e18a24..c8e6645 100644 --- a/source/wizard/steps/mcp-step.tsx +++ b/source/wizard/steps/mcp-step.tsx @@ -129,14 +129,30 @@ export function McpStep({ // Adding new server const template = MCP_TEMPLATES.find(t => t.id === item.value); if (template) { - setEditingServerName(null); // Not editing - setSelectedTemplate(template); - setCurrentFieldIndex(0); - setFieldAnswers({}); - setCurrentValue(template.fields[0]?.default || ''); - setMultilineBuffer(''); - setError(null); - setMode('field-input'); + // Check if template has no fields + if (template.fields.length === 0) { + // Automatically build config and add server when no fields are required + try { + const serverConfig = template.buildConfig({}); + setServers({...servers, [serverConfig.name]: serverConfig}); + // Stay in tabs mode to allow adding more servers + setMode('tabs'); + } catch (err) { + setError( + err instanceof Error ? err.message : 'Failed to build configuration', + ); + } + } else { + // Template has fields, proceed with normal flow + setEditingServerName(null); // Not editing + setSelectedTemplate(template); + setCurrentFieldIndex(0); + setFieldAnswers({}); + setCurrentValue(template.fields[0]?.default || ''); + setMultilineBuffer(''); + setError(null); + setMode('field-input'); + } } }; @@ -611,4 +627,4 @@ export function McpStep({ } return null; -} +} \ No newline at end of file From d66ef064fe9cb870198a55175973471b2e9c4795 Mon Sep 17 00:00:00 2001 From: Brijesh K R Date: Thu, 27 Nov 2025 19:46:07 +0530 Subject: [PATCH 2/4] fix: Fix passing Auth headers for github remote mcp template. --- source/mcp/transport-factory.spec.ts | 72 ++++++++-------------------- source/mcp/transport-factory.ts | 34 ++++--------- source/wizard/steps/mcp-step.tsx | 6 ++- 3 files changed, 35 insertions(+), 77 deletions(-) diff --git a/source/mcp/transport-factory.spec.ts b/source/mcp/transport-factory.spec.ts index 232d9ee..1519f3c 100644 --- a/source/mcp/transport-factory.spec.ts +++ b/source/mcp/transport-factory.spec.ts @@ -305,34 +305,19 @@ test('TransportFactory.createTransport: warns about auth config for websocket tr } }); -test('TransportFactory.createTransport: warns about headers for http transport', t => { - // Capture console.warn calls - const originalWarn = console.warn; - let warningMessage = ''; - console.warn = (message: string) => { - warningMessage = message; +test('TransportFactory.createTransport: creates http transport with headers', t => { + const server: MCPServer = { + name: 'test-http-with-headers', + transport: 'http', + url: 'https://example.com/mcp', + headers: { + Authorization: 'Bearer token123', + }, }; - try { - const server: MCPServer = { - name: 'test-http-with-headers', - transport: 'http', - url: 'https://example.com/mcp', - headers: { - Authorization: 'Bearer token123', - }, - }; - - const transport = TransportFactory.createTransport(server); + const transport = TransportFactory.createTransport(server); - t.truthy(transport); - t.true(warningMessage.includes('custom headers')); - t.true(warningMessage.includes('HTTP transport')); - t.true(warningMessage.includes('Authorization')); - } finally { - // Restore original console.warn - console.warn = originalWarn; - } + t.truthy(transport); }); test('TransportFactory.createTransport: warns about auth config for http transport', t => { @@ -365,33 +350,18 @@ test('TransportFactory.createTransport: warns about auth config for http transpo } }); -test('TransportFactory.validateServerConfig: warns about headers for http transport', t => { - // Capture console.warn calls - const originalWarn = console.warn; - let warningMessage = ''; - console.warn = (message: string) => { - warningMessage = message; +test('TransportFactory.validateServerConfig: validates http config with headers', t => { + const server: MCPServer = { + name: 'test-http-with-headers-validation', + transport: 'http', + url: 'https://example.com/mcp', + headers: { + 'Custom-Header': 'value', + }, }; - try { - const server: MCPServer = { - name: 'test-http-with-headers-validation', - transport: 'http', - url: 'https://example.com/mcp', - headers: { - 'Custom-Header': 'value', - }, - }; - - const result = TransportFactory.validateServerConfig(server); + const result = TransportFactory.validateServerConfig(server); - t.true(result.valid); - t.is(result.errors.length, 0); - t.true(warningMessage.includes('custom headers')); - t.true(warningMessage.includes('HTTP transport')); - t.true(warningMessage.includes('Custom-Header')); - } finally { - // Restore original console.warn - console.warn = originalWarn; - } + t.true(result.valid); + t.is(result.errors.length, 0); }); diff --git a/source/mcp/transport-factory.ts b/source/mcp/transport-factory.ts index 776fb09..d63e166 100644 --- a/source/mcp/transport-factory.ts +++ b/source/mcp/transport-factory.ts @@ -110,9 +110,11 @@ export class TransportFactory { ); } - // Note: The StreamableHTTPClientTransport doesn't directly support custom headers in the current SDK - // This would need to be handled via the MCP client or additional transport configuration - const transport = new StreamableHTTPClientTransport(url); + // Create transport with headers if provided + const transportOptions = server.headers + ? {requestInit: {headers: server.headers}} + : undefined; + const transport = new StreamableHTTPClientTransport(url, transportOptions); if (server.auth) { console.warn( @@ -124,13 +126,8 @@ export class TransportFactory { if (server.headers) { const headerKeys = Object.keys(server.headers); if (headerKeys.length > 0) { - console.warn( - `HTTP transport for server "${ - server.name - }" has custom headers [${headerKeys.join( - ', ', - )}], but current SDK doesn't support custom headers for HTTP transport. These headers will be ignored.`, - ); + // Headers are now being used, so don't show the warning anymore + // console.warn(...) - Commented out since headers are now supported } } @@ -182,19 +179,8 @@ export class TransportFactory { } } - // Warn if headers are specified but won't be used - if (server.headers) { - const headerKeys = Object.keys(server.headers); - if (headerKeys.length > 0) { - console.warn( - `HTTP transport for server "${ - server.name - }" has custom headers [${headerKeys.join( - ', ', - )}], but current SDK doesn't support custom headers for HTTP transport. These headers will be ignored.`, - ); - } - } + // Headers are now supported, so we don't need to warn about them being ignored + // The actual warning logic has been moved to the createHTTPTransport method break; } @@ -232,7 +218,7 @@ export class TransportFactory { 'Requires an http:// or https:// URL', 'Uses the StreamableHTTP protocol from MCP specification', 'Best for stateless remote services and APIs', - 'Note: Custom headers are not currently supported by the SDK and will be ignored', + 'Custom headers are now supported for authentication', ]; default: diff --git a/source/wizard/steps/mcp-step.tsx b/source/wizard/steps/mcp-step.tsx index c8e6645..cfe9f1e 100644 --- a/source/wizard/steps/mcp-step.tsx +++ b/source/wizard/steps/mcp-step.tsx @@ -139,7 +139,9 @@ export function McpStep({ setMode('tabs'); } catch (err) { setError( - err instanceof Error ? err.message : 'Failed to build configuration', + err instanceof Error + ? err.message + : 'Failed to build configuration', ); } } else { @@ -627,4 +629,4 @@ export function McpStep({ } return null; -} \ No newline at end of file +} From 630ebf720b310efa519ea6b3560b768e6a883fe1 Mon Sep 17 00:00:00 2001 From: Brijesh K R Date: Thu, 27 Nov 2025 22:23:10 +0530 Subject: [PATCH 3/4] fix: Fixed the vscode*, pyright & yaml lsp servers not being detected due to unsupported --version argument. --- source/lsp/lsp-manager.ts | 4 +- source/lsp/server-discovery.ts | 78 +++++++++++++++++++++++++++++++--- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/source/lsp/lsp-manager.ts b/source/lsp/lsp-manager.ts index 14c7bc1..0aa64f7 100644 --- a/source/lsp/lsp-manager.ts +++ b/source/lsp/lsp-manager.ts @@ -72,7 +72,7 @@ export class LSPManager extends EventEmitter { // Auto-discover additional servers if enabled (default: true) if (config.autoDiscover !== false) { - const discovered = discoverLanguageServers(); + const discovered = await discoverLanguageServers(); // Only add discovered servers for languages not already covered const coveredLanguages = new Set(); @@ -416,4 +416,4 @@ export async function resetLSPManager(): Promise { await lspManagerInstance.shutdown(); lspManagerInstance = null; } -} +} \ No newline at end of file diff --git a/source/lsp/server-discovery.ts b/source/lsp/server-discovery.ts index 811b2b1..ccfb92a 100644 --- a/source/lsp/server-discovery.ts +++ b/source/lsp/server-discovery.ts @@ -3,7 +3,7 @@ * Detects installed language servers on the system */ -import {execSync} from 'child_process'; +import {execSync, spawn} from 'child_process'; import {existsSync} from 'fs'; import {join} from 'path'; import type {LSPServerConfig} from './lsp-client'; @@ -14,6 +14,7 @@ interface LanguageServerDefinition { args: string[]; languages: string[]; checkCommand?: string; // Command to verify installation + verificationMethod?: 'version' | 'lsp' | 'none'; // New verification method installHint?: string; } @@ -37,6 +38,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: ['--stdio'], languages: ['py', 'pyi'], checkCommand: 'pyright-langserver --version', + verificationMethod: 'lsp', installHint: 'npm install -g pyright', }, // Python - pylsp (alternative) @@ -82,6 +84,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: ['--stdio'], languages: ['json', 'jsonc'], checkCommand: 'vscode-json-language-server --version', + verificationMethod: 'lsp', installHint: 'npm install -g vscode-langservers-extracted', }, // HTML @@ -91,6 +94,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: ['--stdio'], languages: ['html', 'htm'], checkCommand: 'vscode-html-language-server --version', + verificationMethod: 'lsp', installHint: 'npm install -g vscode-langservers-extracted', }, // CSS @@ -100,6 +104,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: ['--stdio'], languages: ['css', 'scss', 'less'], checkCommand: 'vscode-css-language-server --version', + verificationMethod: 'lsp', installHint: 'npm install -g vscode-langservers-extracted', }, // YAML @@ -109,6 +114,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: ['--stdio'], languages: ['yaml', 'yml'], checkCommand: 'yaml-language-server --version', + verificationMethod: 'lsp', installHint: 'npm install -g yaml-language-server', }, // Bash/Shell @@ -165,10 +171,49 @@ function verifyServer(checkCommand: string): boolean { } } +/** + * Verify an LSP server by attempting to start it with its required LSP arguments + * and confirming that the process spawns successfully without immediate errors. + */ +function verifyLSPServerWithCommunication(command: string, args: string[]): Promise { + return new Promise((resolve) => { + const child = spawn(command, args, {stdio: ['pipe', 'pipe', 'pipe']}); + + // Set a timeout to prevent the process from hanging indefinitely + const timeout = setTimeout(() => { + child.kill(); + resolve(false); + }, 2000); + + // Listen for errors during startup (e.g., command not found) + child.on('error', () => { + clearTimeout(timeout); + child.kill(); + resolve(false); + }); + + // If the process spawns successfully, we consider it valid. + // We can then kill it immediately. + child.on('spawn', () => { + clearTimeout(timeout); + child.kill(); // Clean up the successfully spawned process + resolve(true); + }); + + // Handle cases where the process exits very quickly (either success or failure) + child.on('exit', (_code) => { + clearTimeout(timeout); + // A clean exit can also indicate success for some servers + // However, for LSP servers waiting for input, an immediate exit is often a failure + // The 'spawn' event is a more reliable indicator for our purpose + }); + }); +} + /** * Discover all installed language servers */ -export function discoverLanguageServers(): LSPServerConfig[] { +export async function discoverLanguageServers(): Promise { const discovered: LSPServerConfig[] = []; const coveredLanguages = new Set(); @@ -183,12 +228,31 @@ export function discoverLanguageServers(): LSPServerConfig[] { const commandPath = findCommand(server.command); if (!commandPath) continue; - // Verify server works if check command provided + // Verify server works based on verification method // Use the resolved command path for verification - if (server.checkCommand) { - const checkCmd = server.checkCommand.replace(server.command, commandPath); - if (!verifyServer(checkCmd)) continue; + const verificationMethod = server.verificationMethod || 'version'; + + let verified = true; + switch (verificationMethod) { + case 'version': + // Use the existing check command approach + if (server.checkCommand) { + const checkCmd = server.checkCommand.replace(server.command, commandPath); + verified = verifyServer(checkCmd); + } + break; + + case 'lsp': + // Use the new LSP verification approach + verified = await verifyLSPServerWithCommunication(commandPath, server.args); + break; + + case 'none': + // Skip verification, only check if command exists + break; } + + if (!verified) continue; // Add to discovered servers with resolved command path discovered.push({ @@ -330,4 +394,4 @@ export function getKnownServersStatus(): Array<{ languages: server.languages, installHint: server.installHint, })); -} +} \ No newline at end of file From 236628b8cd06c14df99c38187d421a9dab28ab15 Mon Sep 17 00:00:00 2001 From: Brijesh K R Date: Thu, 27 Nov 2025 22:38:47 +0530 Subject: [PATCH 4/4] chore: Fixed formatting for modified files. --- source/lsp/lsp-manager.ts | 2 +- source/lsp/server-discovery.spec.ts | 38 +++++++++++++++++++++++++ source/lsp/server-discovery.ts | 44 ++++++++++++++++++++--------- 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/source/lsp/lsp-manager.ts b/source/lsp/lsp-manager.ts index 0aa64f7..ddc574c 100644 --- a/source/lsp/lsp-manager.ts +++ b/source/lsp/lsp-manager.ts @@ -416,4 +416,4 @@ export async function resetLSPManager(): Promise { await lspManagerInstance.shutdown(); lspManagerInstance = null; } -} \ No newline at end of file +} diff --git a/source/lsp/server-discovery.spec.ts b/source/lsp/server-discovery.spec.ts index 2e20456..75b113b 100644 --- a/source/lsp/server-discovery.spec.ts +++ b/source/lsp/server-discovery.spec.ts @@ -465,3 +465,41 @@ test('getServerForLanguage - handles empty extension', t => { const result = getServerForLanguage(servers, ''); t.is(result, undefined); }); + +// Tests for verificationMethod functionality +test('getKnownServersStatus - all servers have proper structure', t => { + const result = getKnownServersStatus(); + + // Check that servers have proper structure + for (const server of result) { + t.truthy(server.name); + t.true(typeof server.available === 'boolean'); + t.true(Array.isArray(server.languages)); + } +}); + +test('getKnownServersStatus - key servers are present with correct names', t => { + const result = getKnownServersStatus(); + + // Check that key servers are present with their correct names + const keyServers = [ + 'typescript-language-server', + 'pyright', + 'pylsp', + 'rust-analyzer', + 'gopls', + 'clangd', + 'vscode-json-languageserver', + 'vscode-html-languageserver', + 'vscode-css-languageserver', + 'yaml-language-server', + 'bash-language-server', + 'lua-language-server', + ]; + + for (const serverName of keyServers) { + const server = result.find(s => s.name === serverName); + t.truthy(server, `Server ${serverName} should be present`); + t.is(server!.name, serverName); + } +}); diff --git a/source/lsp/server-discovery.ts b/source/lsp/server-discovery.ts index ccfb92a..81a6622 100644 --- a/source/lsp/server-discovery.ts +++ b/source/lsp/server-discovery.ts @@ -29,6 +29,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: ['--stdio'], languages: ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs'], checkCommand: 'typescript-language-server --version', + verificationMethod: 'version', installHint: 'npm install -g typescript-language-server typescript', }, // Python - Pyright (preferred) @@ -48,6 +49,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: [], languages: ['py', 'pyi'], checkCommand: 'pylsp --version', + verificationMethod: 'version', installHint: 'pip install python-lsp-server', }, // Rust @@ -57,6 +59,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: [], languages: ['rs'], checkCommand: 'rust-analyzer --version', + verificationMethod: 'version', installHint: 'rustup component add rust-analyzer', }, // Go @@ -66,6 +69,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: ['serve'], languages: ['go'], checkCommand: 'gopls version', + verificationMethod: 'version', installHint: 'go install golang.org/x/tools/gopls@latest', }, // C/C++ @@ -75,6 +79,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: ['--background-index'], languages: ['c', 'cpp', 'cc', 'cxx', 'h', 'hpp', 'hxx'], checkCommand: 'clangd --version', + verificationMethod: 'version', installHint: 'Install via system package manager (apt, brew, etc.)', }, // JSON @@ -124,6 +129,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: ['start'], languages: ['sh', 'bash', 'zsh'], checkCommand: 'bash-language-server --version', + verificationMethod: 'version', installHint: 'npm install -g bash-language-server', }, // Lua @@ -133,6 +139,7 @@ const KNOWN_SERVERS: LanguageServerDefinition[] = [ args: [], languages: ['lua'], checkCommand: 'lua-language-server --version', + verificationMethod: 'version', installHint: 'Install from https://github.com/LuaLS/lua-language-server', }, ]; @@ -175,23 +182,26 @@ function verifyServer(checkCommand: string): boolean { * Verify an LSP server by attempting to start it with its required LSP arguments * and confirming that the process spawns successfully without immediate errors. */ -function verifyLSPServerWithCommunication(command: string, args: string[]): Promise { - return new Promise((resolve) => { +function verifyLSPServerWithCommunication( + command: string, + args: string[], +): Promise { + return new Promise(resolve => { const child = spawn(command, args, {stdio: ['pipe', 'pipe', 'pipe']}); - + // Set a timeout to prevent the process from hanging indefinitely const timeout = setTimeout(() => { child.kill(); resolve(false); }, 2000); - + // Listen for errors during startup (e.g., command not found) child.on('error', () => { clearTimeout(timeout); child.kill(); resolve(false); }); - + // If the process spawns successfully, we consider it valid. // We can then kill it immediately. child.on('spawn', () => { @@ -199,9 +209,9 @@ function verifyLSPServerWithCommunication(command: string, args: string[]): Prom child.kill(); // Clean up the successfully spawned process resolve(true); }); - + // Handle cases where the process exits very quickly (either success or failure) - child.on('exit', (_code) => { + child.on('exit', _code => { clearTimeout(timeout); // A clean exit can also indicate success for some servers // However, for LSP servers waiting for input, an immediate exit is often a failure @@ -231,27 +241,33 @@ export async function discoverLanguageServers(): Promise { // Verify server works based on verification method // Use the resolved command path for verification const verificationMethod = server.verificationMethod || 'version'; - + let verified = true; switch (verificationMethod) { case 'version': // Use the existing check command approach if (server.checkCommand) { - const checkCmd = server.checkCommand.replace(server.command, commandPath); + const checkCmd = server.checkCommand.replace( + server.command, + commandPath, + ); verified = verifyServer(checkCmd); } break; - + case 'lsp': // Use the new LSP verification approach - verified = await verifyLSPServerWithCommunication(commandPath, server.args); + verified = await verifyLSPServerWithCommunication( + commandPath, + server.args, + ); break; - + case 'none': // Skip verification, only check if command exists break; } - + if (!verified) continue; // Add to discovered servers with resolved command path @@ -394,4 +410,4 @@ export function getKnownServersStatus(): Array<{ languages: server.languages, installHint: server.installHint, })); -} \ No newline at end of file +}