Skip to content

Commit 41c99af

Browse files
authored
fix(amazonq): support mcp config files for backwards compatbility (aws#2292)
1 parent 36f3eed commit 41c99af

File tree

4 files changed

+138
-38
lines changed

4 files changed

+138
-38
lines changed

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

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -898,14 +898,14 @@ export class McpManager {
898898
this.mcpServers.clear()
899899
this.mcpServerStates.clear()
900900
this.agentConfig = {
901-
name: 'amazon_q_default',
901+
name: 'q_ide_default',
902902
description: 'Agent configuration',
903903
mcpServers: {},
904904
tools: [],
905905
allowedTools: [],
906906
toolsSettings: {},
907-
includedFiles: [],
908907
resources: [],
908+
useLegacyMcpJson: true,
909909
}
910910
if (!keepInstance) {
911911
McpManager.#instance = undefined
@@ -959,34 +959,35 @@ export class McpManager {
959959

960960
const serverPrefix = `@${unsanitizedServerName}`
961961

962-
// Process each tool permission using the permission manager
962+
// Check if this is a legacy MCP server (from MCP config file)
963+
const isLegacyMcpServer = serverConfig.__configPath__?.endsWith('mcp.json')
964+
965+
// For agent config servers, use the permission manager
963966
for (const [toolName, permission] of Object.entries(perm.toolPerms || {})) {
964967
this.permissionManager.setToolPermission(unsanitizedServerName, toolName, permission)
965968
}
966969

967970
// Update the agent config from the permission manager
968971
this.agentConfig = this.permissionManager.getAgentConfig()
969972

970-
// Update mcpServerPermissions map immediately to reflect changes
971-
this.mcpServerPermissions.set(serverName, {
972-
enabled: perm.enabled,
973-
toolPerms: perm.toolPerms || {},
974-
})
975-
976-
// Update server enabled/disabled state in agent config
977-
if (this.agentConfig.mcpServers[unsanitizedServerName]) {
978-
this.agentConfig.mcpServers[unsanitizedServerName].disabled = !perm.enabled
979-
}
973+
if (isLegacyMcpServer) {
974+
// For legacy MCP servers, save permissions to agent config file and update MCP config for enable/disable
975+
const mcpConfigPath = serverConfig.__configPath__!
976+
const agentPath = mcpConfigPath.replace(
977+
path.sep + 'mcp.json',
978+
path.sep + 'agents' + path.sep + 'default.json'
979+
)
980980

981-
// Also update the mcpServers map
982-
if (serverConfig) {
983-
serverConfig.disabled = !perm.enabled
984-
}
981+
// Update MCP config for enable/disable
982+
await this.mutateConfigFile(mcpConfigPath, (json: any) => {
983+
if (!json.mcpServers[unsanitizedServerName]) {
984+
json.mcpServers[unsanitizedServerName] = { ...serverConfig }
985+
delete json.mcpServers[unsanitizedServerName].__configPath__
986+
}
987+
json.mcpServers[unsanitizedServerName].disabled = !perm.enabled
988+
})
985989

986-
// Save only server-specific changes to agent config
987-
const agentPath = perm.__configPath__
988-
if (agentPath) {
989-
// Collect server-specific tools and allowedTools
990+
// Use the same function but with corrected agent path
990991
const serverPrefix = `@${unsanitizedServerName}`
991992
const serverTools = this.agentConfig.tools.filter(
992993
tool => tool === serverPrefix || tool.startsWith(`${serverPrefix}/`)
@@ -999,13 +1000,56 @@ export class McpManager {
9991000
this.features.workspace,
10001001
this.features.logging,
10011002
unsanitizedServerName,
1002-
this.agentConfig.mcpServers[unsanitizedServerName],
1003+
null, // Don't save server config to agent file for legacy servers
10031004
serverTools,
10041005
serverAllowedTools,
10051006
agentPath
10061007
)
10071008
}
10081009

1010+
// Update mcpServerPermissions map immediately to reflect changes
1011+
this.mcpServerPermissions.set(serverName, {
1012+
enabled: perm.enabled,
1013+
toolPerms: perm.toolPerms || {},
1014+
})
1015+
1016+
// Update server enabled/disabled state (only for non-legacy servers)
1017+
if (!isLegacyMcpServer) {
1018+
if (this.agentConfig.mcpServers[unsanitizedServerName]) {
1019+
this.agentConfig.mcpServers[unsanitizedServerName].disabled = !perm.enabled
1020+
}
1021+
}
1022+
1023+
// Always update the mcpServers map
1024+
if (serverConfig) {
1025+
serverConfig.disabled = !perm.enabled
1026+
}
1027+
1028+
// Save only server-specific changes to agent config (for non-legacy servers)
1029+
if (!isLegacyMcpServer) {
1030+
const agentPath = perm.__configPath__
1031+
if (agentPath) {
1032+
// Collect server-specific tools and allowedTools
1033+
const serverPrefix = `@${unsanitizedServerName}`
1034+
const serverTools = this.agentConfig.tools.filter(
1035+
tool => tool === serverPrefix || tool.startsWith(`${serverPrefix}/`)
1036+
)
1037+
const serverAllowedTools = this.agentConfig.allowedTools.filter(
1038+
tool => tool === serverPrefix || tool.startsWith(`${serverPrefix}/`)
1039+
)
1040+
1041+
await saveServerSpecificAgentConfig(
1042+
this.features.workspace,
1043+
this.features.logging,
1044+
unsanitizedServerName,
1045+
this.agentConfig.mcpServers[unsanitizedServerName],
1046+
serverTools,
1047+
serverAllowedTools,
1048+
agentPath
1049+
)
1050+
}
1051+
}
1052+
10091053
// enable/disable server
10101054
if (this.isServerDisabled(serverName)) {
10111055
const client = this.clients.get(serverName)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export class AgentModel {
7878

7979
static fromJson(doc: any): AgentModel {
8080
const cfg: AgentConfig = {
81-
name: doc?.['name'] || 'amazon_q_default',
81+
name: doc?.['name'] || 'q_ide_default',
8282
description: doc?.['description'] || 'Default agent configuration',
8383
prompt: doc?.['prompt'],
8484
model: doc?.['model'],

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ describe('loadAgentConfig', () => {
207207
mkdir: (d: string, opts: any) => Promise.resolve(fs.mkdirSync(d, { recursive: opts.recursive })),
208208
getUserHomeDir: () => tmpDir,
209209
},
210+
getAllWorkspaceFolders: () => [],
210211
}
211212
logger = { warn: () => {}, info: () => {}, error: () => {}, debug: () => {} }
212213
})
@@ -697,7 +698,7 @@ describe('convertPersonaToAgent', () => {
697698

698699
const result = convertPersonaToAgent(persona, mcpServers, mockAgent)
699700

700-
expect(result.name).to.equal('amazon_q_default')
701+
expect(result.name).to.equal('q_ide_default')
701702
expect(result.mcpServers).to.have.property('testServer')
702703
expect(result.tools).to.include('@testServer')
703704
expect(result.tools).to.include('fs_read')

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

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ export async function loadMcpServerConfigs(
147147
}
148148

149149
const DEFAULT_AGENT_RAW = `{
150-
"name": "amazon_q_default",
150+
"name": "q_ide_default",
151151
"description": "Default agent configuration",
152152
"prompt": "",
153153
"mcpServers": {},
@@ -179,7 +179,7 @@ const DEFAULT_AGENT_RAW = `{
179179
"agentSpawn": [],
180180
"userPromptSubmit": []
181181
},
182-
"useLegacyMcpJson": false
182+
"useLegacyMcpJson": true
183183
}`
184184

185185
const DEFAULT_PERSONA_RAW = `{
@@ -222,6 +222,7 @@ const DEFAULT_PERSONA_RAW = `{
222222
* - Load global first (if exists), then workspace files—workspace overrides.
223223
* - Combines functionality of loadMcpServerConfigs and loadPersonaPermissions
224224
* - Handles server configurations and permissions from the same agent file
225+
* - Supports backwards compatibility with MCP config files when useLegacyMcpJson is true
225226
*/
226227
export async function loadAgentConfig(
227228
workspace: Workspace,
@@ -240,13 +241,14 @@ export async function loadAgentConfig(
240241

241242
// Create base agent config
242243
const agentConfig: AgentConfig = {
243-
name: 'amazon_q_default',
244+
name: 'q_ide_default',
244245
description: 'Agent configuration',
245246
mcpServers: {},
246247
tools: [],
247248
allowedTools: [],
248249
toolsSettings: {},
249250
resources: [],
251+
useLegacyMcpJson: true, // Default to true for backwards compatibility
250252
}
251253

252254
// Normalize paths
@@ -272,6 +274,9 @@ export async function loadAgentConfig(
272274
return 0
273275
})
274276

277+
// Track useLegacyMcpJson value - workspace takes precedence over global
278+
let useLegacyMcpJsonValue: boolean | undefined
279+
275280
// Process each path like loadMcpServerConfigs
276281
for (const fsPath of sortedPaths) {
277282
// 1) Skip missing files or create default global
@@ -333,6 +338,17 @@ export async function loadAgentConfig(
333338
agentConfig.description = json.description || agentConfig.description
334339
}
335340

341+
// Track useLegacyMcpJson - workspace files take precedence
342+
if (json.useLegacyMcpJson !== undefined) {
343+
if (fsPath !== globalConfigPath) {
344+
// Workspace file - always takes precedence
345+
useLegacyMcpJsonValue = json.useLegacyMcpJson
346+
} else if (useLegacyMcpJsonValue === undefined) {
347+
// Global file - only use if no workspace value set
348+
useLegacyMcpJsonValue = json.useLegacyMcpJson
349+
}
350+
}
351+
336352
// 4) Process permissions (tools and allowedTools)
337353
if (Array.isArray(json.tools)) {
338354
for (const tool of json.tools) {
@@ -460,8 +476,46 @@ export async function loadAgentConfig(
460476
}
461477
}
462478

479+
// Set final useLegacyMcpJson value - default to true if not specified anywhere
480+
agentConfig.useLegacyMcpJson = useLegacyMcpJsonValue !== undefined ? useLegacyMcpJsonValue : true
481+
482+
// Load MCP config files if useLegacyMcpJson is true
483+
if (agentConfig.useLegacyMcpJson) {
484+
const wsUris = workspace.getAllWorkspaceFolders()?.map(f => f.uri) ?? []
485+
const mcpPaths = [...getWorkspaceMcpConfigPaths(wsUris), getGlobalMcpConfigPath(workspace.fs.getUserHomeDir())]
486+
487+
const mcpResult = await loadMcpServerConfigs(workspace, logging, mcpPaths)
488+
489+
// MCP configs have precedence over agent configs
490+
// Merge MCP servers into the result, overriding any from agent config
491+
for (const [sanitizedName, mcpConfig] of mcpResult.servers) {
492+
const originalName = mcpResult.serverNameMapping.get(sanitizedName)
493+
if (originalName) {
494+
servers.set(sanitizedName, mcpConfig)
495+
serverNameMapping.set(sanitizedName, originalName)
496+
497+
// Add MCP server tools to agent config for permission management
498+
const serverPrefix = `@${originalName}`
499+
if (!agentConfig.tools.includes(serverPrefix)) {
500+
agentConfig.tools.push(serverPrefix)
501+
}
502+
503+
logging.info(`Loaded MCP server '${originalName}' from legacy MCP config`)
504+
}
505+
}
506+
507+
// Merge MCP config errors
508+
for (const [key, error] of mcpResult.errors) {
509+
configErrors.set(`mcp_${key}`, error)
510+
}
511+
512+
logging.info(`Loaded ${mcpResult.servers.size} servers from legacy MCP configs`)
513+
}
514+
463515
// Return the agent config, servers, server name mapping, and errors
464-
logging.info(`Successfully processed ${uniquePaths.length} agent config files`)
516+
logging.info(
517+
`Successfully processed ${uniquePaths.length} agent config files and ${agentConfig.useLegacyMcpJson ? 'legacy MCP configs' : 'no legacy MCP configs'}`
518+
)
465519
return {
466520
servers,
467521
serverNameMapping,
@@ -631,7 +685,7 @@ export function convertPersonaToAgent(
631685
featureAgent: Agent
632686
): AgentConfig {
633687
const agent: AgentConfig = {
634-
name: 'amazon_q_default',
688+
name: 'q_ide_default',
635689
description: 'Default agent configuration',
636690
prompt: '',
637691
mcpServers: {},
@@ -644,7 +698,7 @@ export function convertPersonaToAgent(
644698
agentSpawn: [],
645699
userPromptSubmit: [],
646700
},
647-
useLegacyMcpJson: false,
701+
useLegacyMcpJson: true,
648702
}
649703

650704
// Include all servers from MCP config
@@ -969,7 +1023,7 @@ export async function saveServerSpecificAgentConfig(
9691023
} catch {
9701024
// If file doesn't exist, create minimal config
9711025
existingConfig = {
972-
name: 'amazon_q_default',
1026+
name: 'q_ide_default',
9731027
description: 'Agent configuration',
9741028
mcpServers: {},
9751029
tools: [],
@@ -995,11 +1049,12 @@ export async function saveServerSpecificAgentConfig(
9951049
} else {
9961050
// Update or add server
9971051
existingConfig.mcpServers[serverName] = serverConfig
998-
// Add new server tools
999-
existingConfig.tools.push(...serverTools)
1000-
existingConfig.allowedTools.push(...serverAllowedTools)
10011052
}
10021053

1054+
// Add new server tools
1055+
existingConfig.tools.push(...serverTools)
1056+
existingConfig.allowedTools.push(...serverAllowedTools)
1057+
10031058
await workspace.fs.writeFile(configPath, JSON.stringify(existingConfig, null, 2))
10041059
logging.info(`Saved server-specific agent config for ${serverName} to ${configPath}`)
10051060
} catch (err: any) {
@@ -1025,9 +1080,9 @@ export async function migrateAgentConfigToCLIFormat(
10251080

10261081
let updated = false
10271082

1028-
// Rename default-agent to amazon_q_default
1029-
if (config.name === 'default-agent') {
1030-
config.name = 'amazon_q_default'
1083+
// Rename default-agent to q_ide_default
1084+
if (config.name !== 'q_ide_default') {
1085+
config.name = 'q_ide_default'
10311086
updated = true
10321087
}
10331088

@@ -1041,7 +1096,7 @@ export async function migrateAgentConfigToCLIFormat(
10411096
updated = true
10421097
}
10431098
if (!config.hasOwnProperty('useLegacyMcpJson')) {
1044-
config.useLegacyMcpJson = false
1099+
config.useLegacyMcpJson = true
10451100
updated = true
10461101
}
10471102

0 commit comments

Comments
 (0)