Skip to content

Commit e2d0799

Browse files
committed
feat: ux changes for mcp http/sse support
1 parent 5d4f4ae commit e2d0799

File tree

5 files changed

+67
-58
lines changed

5 files changed

+67
-58
lines changed

chat-client/src/client/mcpMynahUi.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -461,12 +461,13 @@ export class McpMynahUi {
461461
this.messager.onListMcpServers()
462462
},
463463
onFilterValueChange: (filterValues: Record<string, any>) => {
464-
const newTransport = filterValues.transport
465-
// only fire when transport actually changes
466-
if (newTransport !== _lastTransport) {
467-
_lastTransport = newTransport
468-
this.messager.onMcpServerClick(MCP_IDS.CHANGE_TRANSPORT, filterValues.name, filterValues)
464+
const newTransport = filterValues?.transport
465+
if (!newTransport || newTransport === _lastTransport) {
466+
return
469467
}
468+
469+
_lastTransport = newTransport
470+
this.messager.onMcpServerClick(MCP_IDS.CHANGE_TRANSPORT, filterValues.name, filterValues)
470471
},
471472
onFilterActionClick: (
472473
actionParams: McpServerClickResult,

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/mcp/mcpEventHandler.ts

Lines changed: 35 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -552,26 +552,17 @@ export class McpEventHandler {
552552
}
553553

554554
const transport = values.transport
555-
const usingStdio = !!values.command?.trim()
556-
const usingHttp = !!values.url?.trim()
555+
const command = values.command?.trim() || ''
556+
const url = values.url?.trim() || ''
557557

558-
if (transport) {
559-
if (transport === 'stdio') {
560-
if (!usingStdio) {
561-
errors.push('Command is required for stdio transport')
562-
}
563-
} else {
564-
if (!usingHttp) {
565-
errors.push(`URL is required for ${transport} transport`)
566-
}
567-
}
568-
}
569-
570-
// Validate mutual exclusivity and presence of either
571-
if (usingStdio && usingHttp) {
572-
errors.push('Provide either command OR url, not both')
573-
} else if (!usingStdio && !usingHttp) {
558+
if (!command && !url) {
574559
errors.push('Either command or url is required')
560+
} else if (command && url) {
561+
errors.push('Provide either command OR url, not both')
562+
} else if (transport === 'stdio' && !command) {
563+
errors.push('Command is required for stdio transport')
564+
} else if (transport !== 'stdio' && !url) {
565+
errors.push(`URL is required for ${transport} transport`)
575566
}
576567

577568
if (values.timeout && values.timeout.trim() !== '') {
@@ -606,17 +597,19 @@ export class McpEventHandler {
606597

607598
if (Array.isArray(values.headers)) {
608599
const hdrs = values.headers as Array<{ key: string; value: string }>
609-
const emptyKeyWithValue = hdrs.some(
610-
h => (!h.key || h.key.trim() === '') && h.value && h.value.trim() !== ''
611-
)
612-
const keyWithEmptyValue = hdrs.some(
613-
h => h.key && h.key.trim() !== '' && (!h.value || h.value.trim() === '')
614-
)
615-
if (emptyKeyWithValue) {
616-
errors.push('Header key cannot be empty when value is provided')
617-
}
618-
if (keyWithEmptyValue) {
619-
errors.push('Header value cannot be empty when key is provided')
600+
const invalidHeaders = hdrs.find(h => {
601+
const key = h.key?.trim() || ''
602+
const value = h.value?.trim() || ''
603+
return (key === '' && value !== '') || (key !== '' && value === '')
604+
})
605+
606+
if (invalidHeaders) {
607+
const hasKey = invalidHeaders.key?.trim()
608+
errors.push(
609+
hasKey
610+
? 'Header value cannot be empty when key is provided'
611+
: 'Header key cannot be empty when value is provided'
612+
)
620613
}
621614
}
622615

@@ -665,7 +658,7 @@ export class McpEventHandler {
665658
)
666659
.filter(Boolean)
667660
} catch (e) {
668-
this.#features.logging.warn(`Failed to process args: ${e}`)
661+
this.#features.logging.warn(`MCP: Failed to process args: ${e}`)
669662
}
670663

671664
try {
@@ -678,7 +671,7 @@ export class McpEventHandler {
678671
return acc
679672
}, {})
680673
} catch (e) {
681-
this.#features.logging.warn(`Failed to process env variables: ${e}`)
674+
this.#features.logging.warn(`MCP: Failed to process env variables: ${e}`)
682675
}
683676
}
684677

@@ -699,7 +692,7 @@ export class McpEventHandler {
699692
return acc
700693
}, {})
701694
} catch (e) {
702-
this.#features.logging.warn(`Failed to process headers: ${e}`)
695+
this.#features.logging.warn(`MCP: Failed to process headers: ${e}`)
703696
}
704697
}
705698

@@ -1072,29 +1065,26 @@ export class McpEventHandler {
10721065
}
10731066

10741067
async #handleChangeTransport(params: McpServerClickParams) {
1068+
const { optionsValues, title } = params
10751069
const editingServerName = this.#currentEditingServerName
10761070

1077-
if (params.optionsValues) {
1078-
const transport = params.optionsValues.transport ?? 'stdio'
1079-
if (transport === 'http') {
1080-
delete params.optionsValues.command
1081-
delete params.optionsValues.args
1082-
delete params.optionsValues.env_variables
1083-
} else {
1084-
delete params.optionsValues.url
1085-
delete params.optionsValues.headers
1086-
}
1071+
// Clean up transport-specific fields
1072+
if (optionsValues) {
1073+
const transport = optionsValues.transport ?? 'stdio' // Maintain default to 'stdio'
1074+
const fieldsToDelete = transport === 'http' ? ['command', 'args', 'env_variables'] : ['url', 'headers']
1075+
1076+
fieldsToDelete.forEach(field => delete optionsValues[field])
10871077
}
10881078

1089-
// Handle server name change if in edit mode
1090-
if (editingServerName && params.title && editingServerName !== params.title) {
1079+
// Handle server name change in edit mode
1080+
if (editingServerName && title && editingServerName !== title) {
10911081
const servers = McpManager.instance.getAllServerConfigs()
10921082
const existingConfig = servers.get(editingServerName)
10931083

10941084
if (existingConfig) {
10951085
const updatedServers = new Map(servers)
10961086
updatedServers.delete(editingServerName)
1097-
updatedServers.set(params.title, existingConfig)
1087+
updatedServers.set(title, existingConfig)
10981088
await McpManager.instance.updateServerMap(updatedServers)
10991089
}
11001090
this.#serverNameBeforeUpdate = editingServerName

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/mcp/mcpManager.test.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,22 @@ describe('addServer()', () => {
235235
})
236236

237237
it('persists and initializes an HTTP server', async () => {
238-
loadStub.resolves({ servers: new Map(), serverNameMapping: new Map(), errors: new Map() })
238+
loadStub.resolves({
239+
servers: new Map(),
240+
serverNameMapping: new Map(),
241+
errors: new Map(),
242+
agentConfig: {
243+
name: 'test-agent',
244+
version: '1.0.0',
245+
description: 'Test agent',
246+
mcpServers: {},
247+
tools: [],
248+
allowedTools: [],
249+
toolsSettings: {},
250+
includedFiles: [],
251+
resources: [],
252+
},
253+
})
239254
const mgr = await McpManager.init([], features)
240255

241256
const httpCfg: MCPServerConfig = {

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/mcp/mcpManager.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,6 @@ export class McpManager {
633633

634634
// We don't need to store configPath anymore as we're using agent config
635635
const newCfg: MCPServerConfig = { ...cfg, __configPath__: agentPath }
636-
637636
this.mcpServers.set(sanitizedName, newCfg)
638637
this.serverNameMapping.set(sanitizedName, serverName)
639638

server/aws-lsp-codewhisperer/src/language-server/agenticChat/tools/mcp/mcpUtils.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -408,13 +408,17 @@ export async function loadAgentConfig(
408408
serverNameMapping.set(sanitizedName, name)
409409

410410
// Add to agent config
411-
agentConfig.mcpServers[name] = {
412-
command: cfg.command,
413-
args: cfg.args,
414-
env: cfg.env,
415-
initializationTimeout: cfg.initializationTimeout,
416-
timeout: cfg.timeout,
411+
const agentEntry: any = {}
412+
if (cfg.command) agentEntry.command = cfg.command
413+
if (cfg.url) agentEntry.url = cfg.url
414+
if (cfg.args && cfg.args.length) agentEntry.args = cfg.args
415+
if (cfg.env && Object.keys(cfg.env).length) agentEntry.env = cfg.env
416+
if (cfg.headers && Object.keys(cfg.headers).length) agentEntry.headers = cfg.headers
417+
if (typeof cfg.initializationTimeout === 'number') {
418+
agentEntry.initializationTimeout = cfg.initializationTimeout
417419
}
420+
if (typeof cfg.timeout === 'number') agentEntry.timeout = cfg.timeout
421+
agentConfig.mcpServers[name] = agentEntry
418422

419423
logging.info(
420424
`Loaded MCP server with sanitizedName: '${sanitizedName}' and originalName: '${name}' from ${fsPath}`

0 commit comments

Comments
 (0)