Skip to content

Commit b99df82

Browse files
feat: update MCP manager and utilities (aws#2158)
Co-authored-by: Boyu <[email protected]>
1 parent 472220a commit b99df82

File tree

4 files changed

+56
-103
lines changed

4 files changed

+56
-103
lines changed

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

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ describe('removeServer()', () => {
368368
expect((mgr as any).clients.has('x')).to.be.false
369369
})
370370

371-
it('removes server from all config files', async () => {
371+
it('removes server from agent config', async () => {
372372
const mgr = await McpManager.init([], features)
373373
const dummy = new Client({ name: 'c', version: 'v' })
374374
;(mgr as any).clients.set('x', dummy)
@@ -395,14 +395,13 @@ describe('removeServer()', () => {
395395

396396
await mgr.removeServer('x')
397397

398-
// Verify that writeFile was called for each config path (2 workspace + 1 global)
399-
expect(writeFileStub.callCount).to.equal(3)
398+
// Verify that saveAgentConfig was called
399+
expect(saveAgentConfigStub.calledOnce).to.be.true
400+
expect((mgr as any).clients.has('x')).to.be.false
400401

401-
// Verify the content of the writes (should have removed the server)
402-
writeFileStub.getCalls().forEach(call => {
403-
const content = JSON.parse(call.args[1])
404-
expect(content.mcpServers).to.not.have.property('x')
405-
})
402+
// Verify server was removed from agent config
403+
expect((mgr as any).agentConfig.mcpServers).to.not.have.property('x')
404+
expect((mgr as any).agentConfig.tools).to.not.include('@x')
406405
})
407406
})
408407

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

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -796,30 +796,6 @@ export class McpManager {
796796

797797
// Save agent config
798798
await saveAgentConfig(this.features.workspace, this.features.logging, this.agentConfig, cfg.__configPath__)
799-
800-
// Get all config paths and delete the server from each one
801-
const wsUris = this.features.workspace.getAllWorkspaceFolders()?.map(f => f.uri) ?? []
802-
const wsConfigPaths = getWorkspaceMcpConfigPaths(wsUris)
803-
const globalConfigPath = getGlobalMcpConfigPath(this.features.workspace.fs.getUserHomeDir())
804-
const allConfigPaths = [...wsConfigPaths, globalConfigPath]
805-
806-
// Delete the server from all config files
807-
for (const configPath of allConfigPaths) {
808-
try {
809-
await this.mutateConfigFile(configPath, json => {
810-
if (json.mcpServers && json.mcpServers[unsanitizedName]) {
811-
delete json.mcpServers[unsanitizedName]
812-
this.features.logging.info(
813-
`Deleted server '${unsanitizedName}' from config file: ${configPath}`
814-
)
815-
}
816-
})
817-
} catch (err) {
818-
this.features.logging.warn(
819-
`Failed to delete server '${unsanitizedName}' from config file ${configPath}: ${err}`
820-
)
821-
}
822-
}
823799
}
824800

825801
this.mcpServers.delete(serverName)

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,12 @@ describe('migrateToAgentConfig', () => {
753753
})
754754

755755
it('migrates when no existing configs exist', async () => {
756+
// Create empty MCP config to trigger migration
757+
const mcpDir = path.join(tmpDir, '.aws', 'amazonq')
758+
fs.mkdirSync(mcpDir, { recursive: true })
759+
const mcpPath = path.join(mcpDir, 'mcp.json')
760+
fs.writeFileSync(mcpPath, JSON.stringify({ mcpServers: {} }))
761+
756762
await migrateToAgentConfig(workspace, logger, mockAgent)
757763

758764
// Should create default agent config

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

Lines changed: 43 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,9 @@ const DEFAULT_AGENT_RAW = `{
175175
"README.md",
176176
".amazonq/rules/**/*.md"
177177
],
178-
"resources": []
178+
"resources": [],
179+
"createHooks": [],
180+
"promptHooks": []
179181
}`
180182

181183
const DEFAULT_PERSONA_RAW = `{
@@ -840,48 +842,40 @@ async function migrateConfigToAgent(
840842
const normalizedPersonaPath = normalizePathFromUri(personaPath, logging)
841843
agentPath = normalizePathFromUri(agentPath)
842844

843-
// Check if agent config exists
845+
// Check if config and agent files exist
846+
const configExists = await workspace.fs.exists(normalizedConfigPath).catch(() => false)
844847
const agentExists = await workspace.fs.exists(agentPath).catch(() => false)
845848

846-
// Load existing agent config if it exists
847-
let existingAgentConfig: AgentConfig | undefined
849+
// Only migrate if agent file does not exist
850+
// If config exists, migrate from it; if not, create default agent config
848851
if (agentExists) {
849-
try {
850-
const raw = (await workspace.fs.readFile(agentPath)).toString().trim()
851-
existingAgentConfig = raw ? JSON.parse(raw) : undefined
852-
} catch (err) {
853-
logging.warn(`Failed to read existing agent config at ${agentPath}: ${err}`)
854-
}
852+
return
855853
}
856854

857855
// Read MCP server configs directly from file
858856
const serverConfigs: Record<string, MCPServerConfig> = {}
859857
try {
860-
const configExists = await workspace.fs.exists(normalizedConfigPath)
861-
862-
if (configExists) {
863-
const raw = (await workspace.fs.readFile(normalizedConfigPath)).toString().trim()
864-
if (raw) {
865-
const config = JSON.parse(raw)
866-
867-
if (config.mcpServers && typeof config.mcpServers === 'object') {
868-
// Add each server to the serverConfigs
869-
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
870-
serverConfigs[name] = {
871-
command: (serverConfig as any).command,
872-
args: Array.isArray((serverConfig as any).args) ? (serverConfig as any).args : undefined,
873-
env: typeof (serverConfig as any).env === 'object' ? (serverConfig as any).env : undefined,
874-
initializationTimeout:
875-
typeof (serverConfig as any).initializationTimeout === 'number'
876-
? (serverConfig as any).initializationTimeout
877-
: undefined,
878-
timeout:
879-
typeof (serverConfig as any).timeout === 'number'
880-
? (serverConfig as any).timeout
881-
: undefined,
882-
}
883-
logging.info(`Added server ${name} to serverConfigs`)
858+
const raw = (await workspace.fs.readFile(normalizedConfigPath)).toString().trim()
859+
if (raw) {
860+
const config = JSON.parse(raw)
861+
862+
if (config.mcpServers && typeof config.mcpServers === 'object') {
863+
// Add each server to the serverConfigs
864+
for (const [name, serverConfig] of Object.entries(config.mcpServers)) {
865+
serverConfigs[name] = {
866+
command: (serverConfig as any).command,
867+
args: Array.isArray((serverConfig as any).args) ? (serverConfig as any).args : undefined,
868+
env: typeof (serverConfig as any).env === 'object' ? (serverConfig as any).env : undefined,
869+
initializationTimeout:
870+
typeof (serverConfig as any).initializationTimeout === 'number'
871+
? (serverConfig as any).initializationTimeout
872+
: undefined,
873+
timeout:
874+
typeof (serverConfig as any).timeout === 'number'
875+
? (serverConfig as any).timeout
876+
: undefined,
884877
}
878+
logging.info(`Added server ${name} to serverConfigs`)
885879
}
886880
}
887881
}
@@ -908,46 +902,24 @@ async function migrateConfigToAgent(
908902
}
909903

910904
// Convert to agent config
911-
const newAgentConfig = convertPersonaToAgent(personaConfig, serverConfigs, agent)
912-
newAgentConfig.includedFiles = ['AmazonQ.md', 'README.md', '.amazonq/rules/**/*.md']
913-
newAgentConfig.resources = [] // Initialize with empty array
914-
915-
// Merge with existing config if available
916-
let finalAgentConfig: AgentConfig
917-
if (existingAgentConfig) {
918-
// Keep existing metadata
919-
finalAgentConfig = {
920-
...existingAgentConfig,
921-
// Merge MCP servers, keeping existing ones if they exist
922-
mcpServers: {
923-
...newAgentConfig.mcpServers,
924-
...existingAgentConfig.mcpServers,
925-
},
926-
// Merge tools lists without duplicates
927-
tools: [...new Set([...existingAgentConfig.tools, ...newAgentConfig.tools])],
928-
allowedTools: [...new Set([...existingAgentConfig.allowedTools, ...newAgentConfig.allowedTools])],
929-
// Merge tool settings, preferring existing ones
930-
toolsSettings: {
931-
...newAgentConfig.toolsSettings,
932-
...existingAgentConfig.toolsSettings,
933-
},
934-
// Keep other properties from existing config
935-
includedFiles: existingAgentConfig.includedFiles || newAgentConfig.includedFiles,
936-
createHooks: existingAgentConfig.createHooks || newAgentConfig.createHooks,
937-
promptHooks: [
938-
...new Set([...(existingAgentConfig.promptHooks || []), ...(newAgentConfig.promptHooks || [])]),
939-
],
940-
resources: [...new Set([...(existingAgentConfig.resources || []), ...(newAgentConfig.resources || [])])],
941-
}
942-
} else {
943-
finalAgentConfig = newAgentConfig
944-
logging.info(`Using new config (no existing config to merge)`)
945-
}
905+
const agentConfig = convertPersonaToAgent(personaConfig, serverConfigs, agent)
906+
907+
// Parse default values from DEFAULT_AGENT_RAW
908+
const defaultAgent = JSON.parse(DEFAULT_AGENT_RAW)
909+
910+
// Add complete agent format sections using default values
911+
agentConfig.name = defaultAgent.name
912+
agentConfig.description = defaultAgent.description
913+
agentConfig.version = defaultAgent.version
914+
agentConfig.includedFiles = defaultAgent.includedFiles
915+
agentConfig.resources = defaultAgent.resources
916+
agentConfig.createHooks = defaultAgent.createHooks
917+
agentConfig.promptHooks = defaultAgent.promptHooks
946918

947919
// Save agent config
948920
try {
949-
await saveAgentConfig(workspace, logging, finalAgentConfig, agentPath)
950-
logging.info(`Successfully ${existingAgentConfig ? 'updated' : 'created'} agent config at ${agentPath}`)
921+
await saveAgentConfig(workspace, logging, agentConfig, agentPath)
922+
logging.info(`Successfully created agent config at ${agentPath}`)
951923
} catch (err) {
952924
logging.error(`Failed to save agent config to ${agentPath}: ${err}`)
953925
throw err

0 commit comments

Comments
 (0)