Skip to content

Commit 3ea60c1

Browse files
committed
Merge remote-tracking branch 'upstream/main' into feature/mcp
2 parents 071a34e + 3a4b8df commit 3ea60c1

31 files changed

+1480
-403
lines changed

chat-client/src/client/mcpMynahUi.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,8 +577,9 @@ describe('McpMynahUi', () => {
577577
assert.strictEqual(detailedList.header.actions[0].id, 'mcp-details-menu')
578578

579579
// Verify the mcp-details-menu items
580-
assert.strictEqual(detailedList.header.actions[0].items.length, 1)
581-
assert.strictEqual(detailedList.header.actions[0].items[0].id, 'mcp-delete-server')
580+
assert.strictEqual(detailedList.header.actions[0].items.length, 2)
581+
assert.strictEqual(detailedList.header.actions[0].items[0].id, 'mcp-disable-server')
582+
assert.strictEqual(detailedList.header.actions[0].items[1].id, 'mcp-delete-server')
582583

583584
assert.strictEqual(detailedList.filterOptions.length, 1)
584585
assert.strictEqual(detailedList.filterOptions[0].id, 'permission')

chat-client/src/client/mcpMynahUi.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,11 @@ export class McpMynahUi {
167167
id: MCP_IDS.DETAILS_MENU,
168168
icon: toMynahIcon('ellipsis'),
169169
items: [
170-
// {
171-
// id: MCP_IDS.DISABLE_SERVER,
172-
// text: `Disable MCP server`,
173-
// data: { serverName },
174-
// },
170+
{
171+
id: MCP_IDS.DISABLE_SERVER,
172+
text: `Disable MCP server`,
173+
data: { serverName },
174+
},
175175
{
176176
id: MCP_IDS.DELETE_SERVER,
177177
confirmation: {
@@ -220,10 +220,10 @@ export class McpMynahUi {
220220
...(action.id === MCP_IDS.DETAILS_MENU
221221
? {
222222
items: [
223-
// {
224-
// id: MCP_IDS.DISABLE_SERVER,
225-
// text: `Disable MCP server`,
226-
// },
223+
{
224+
id: MCP_IDS.DISABLE_SERVER,
225+
text: `Disable MCP server`,
226+
},
227227
{
228228
id: MCP_IDS.DELETE_SERVER,
229229
confirmation: {

chat-client/src/client/tabs/tabFactory.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ Select code & ask me to explain, debug or optimize it, or type \`/\` for quick a
187187
'MCP is available in Amazon Q!',
188188
'Pinned context is always included in future chat messages',
189189
'Create and add Saved Prompts using the @ context menu',
190+
'Compact your conversation with /compact',
191+
'Ask Q to review your code and see results in the code issues panel!',
190192
]
191193

192194
const randomIndex = Math.floor(Math.random() * hints.length)

chat-client/src/client/texts/modelSelection.test.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,15 @@ describe('modelSelection', () => {
3939
)
4040
})
4141

42-
it('should provide limited models for eu-central-1 region', () => {
42+
it('should provide all models for eu-central-1 region', () => {
4343
const euCentral1ModelSelection = modelSelectionForRegion['eu-central-1']
4444
assert.ok(euCentral1ModelSelection, 'euCentral1ModelSelection should exist')
4545
assert.ok(euCentral1ModelSelection.type === 'select', 'euCentral1ModelSelection should be type select')
4646
assert.ok(Array.isArray(euCentral1ModelSelection.options), 'options should be an array')
47-
assert.strictEqual(euCentral1ModelSelection.options.length, 1, 'should have 1 option')
47+
assert.strictEqual(euCentral1ModelSelection.options.length, 2, 'should have 2 option')
4848

4949
const modelIds = euCentral1ModelSelection.options.map(option => option.value)
50-
assert.ok(
51-
!modelIds.includes(BedrockModel.CLAUDE_SONNET_4_20250514_V1_0),
52-
'should not include Claude Sonnet 4'
53-
)
50+
assert.ok(modelIds.includes(BedrockModel.CLAUDE_SONNET_4_20250514_V1_0), 'should include Claude Sonnet 4')
5451
assert.ok(
5552
modelIds.includes(BedrockModel.CLAUDE_3_7_SONNET_20250219_V1_0),
5653
'should include Claude Sonnet 3.7'

chat-client/src/client/texts/modelSelection.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,7 @@ const modelSelection: ChatItemFormItem = {
3737
*/
3838
export const modelSelectionForRegion: Record<string, ChatItemFormItem> = {
3939
'us-east-1': modelSelection,
40-
'eu-central-1': {
41-
...modelSelection,
42-
options: modelOptions.filter(option => option.value !== BedrockModel.CLAUDE_SONNET_4_20250514_V1_0),
43-
},
40+
'eu-central-1': modelSelection,
4441
}
4542

4643
export const getModelSelectionChatItem = (modelName: string): ChatItem => ({

server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.test.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3028,7 +3028,7 @@ ${' '.repeat(8)}}
30283028
assert.ok(modelIds.includes('CLAUDE_3_7_SONNET_20250219_V1_0'))
30293029
})
30303030

3031-
it('should return limited models for eu-central-1 region', async () => {
3031+
it('should return all available models for eu-central-1 region', async () => {
30323032
// Set up the region to be eu-central-1
30333033
tokenServiceManagerStub.returns('eu-central-1')
30343034

@@ -3038,12 +3038,12 @@ ${' '.repeat(8)}}
30383038

30393039
// Verify the result
30403040
assert.strictEqual(result.tabId, mockTabId)
3041-
assert.strictEqual(result.models.length, 1)
3042-
assert.strictEqual(result.selectedModelId, 'CLAUDE_3_7_SONNET_20250219_V1_0')
3041+
assert.strictEqual(result.models.length, 2)
3042+
assert.strictEqual(result.selectedModelId, 'CLAUDE_SONNET_4_20250514_V1_0')
30433043

3044-
// Check that the models only include Claude 3.7
3044+
// Check that the models include both Claude versions
30453045
const modelIds = result.models.map(model => model.id)
3046-
assert.ok(!modelIds.includes('CLAUDE_SONNET_4_20250514_V1_0'))
3046+
assert.ok(modelIds.includes('CLAUDE_SONNET_4_20250514_V1_0'))
30473047
assert.ok(modelIds.includes('CLAUDE_3_7_SONNET_20250219_V1_0'))
30483048
})
30493049

@@ -3058,7 +3058,7 @@ ${' '.repeat(8)}}
30583058
// Verify the result
30593059
assert.strictEqual(result.tabId, mockTabId)
30603060
assert.strictEqual(result.models.length, 2)
3061-
assert.strictEqual(result.selectedModelId, 'CLAUDE_3_7_SONNET_20250219_V1_0')
3061+
assert.strictEqual(result.selectedModelId, 'CLAUDE_SONNET_4_20250514_V1_0')
30623062
})
30633063

30643064
it('should return undefined for selectedModelId when no session data exists', async () => {
@@ -3083,10 +3083,29 @@ ${' '.repeat(8)}}
30833083
})
30843084

30853085
it('should fallback to latest available model when saved model is not available in current region', async () => {
3086-
// Set up the region to be eu-central-1 (which only has Claude 3.7)
3087-
tokenServiceManagerStub.returns('eu-central-1')
3086+
// Import the module to stub
3087+
const modelSelection = await import('./constants/modelSelection')
3088+
3089+
// Create a mock region with only Claude 3.7
3090+
const mockModelOptionsForRegion = {
3091+
...modelSelection.MODEL_OPTIONS_FOR_REGION,
3092+
'test-region-limited': [
3093+
{
3094+
id: 'CLAUDE_3_7_SONNET_20250219_V1_0',
3095+
name: 'Claude Sonnet 3.7',
3096+
},
3097+
],
3098+
}
3099+
3100+
// Stub the MODEL_OPTIONS_FOR_REGION
3101+
const modelOptionsStub = sinon
3102+
.stub(modelSelection, 'MODEL_OPTIONS_FOR_REGION')
3103+
.value(mockModelOptionsForRegion)
3104+
3105+
// Set up the region to be the test region (which only has Claude 3.7)
3106+
tokenServiceManagerStub.returns('test-region-limited')
30883107

3089-
// Mock database to return Claude Sonnet 4 (not available in eu-central-1)
3108+
// Mock database to return Claude Sonnet 4 (not available in test-region-limited)
30903109
const getModelIdStub = sinon.stub(ChatDatabase.prototype, 'getModelId')
30913110
getModelIdStub.returns('CLAUDE_SONNET_4_20250514_V1_0')
30923111

@@ -3100,6 +3119,7 @@ ${' '.repeat(8)}}
31003119
assert.strictEqual(result.selectedModelId, 'CLAUDE_3_7_SONNET_20250219_V1_0')
31013120

31023121
getModelIdStub.restore()
3122+
modelOptionsStub.restore()
31033123
})
31043124

31053125
it('should use saved model when it is available in current region', async () => {

server/aws-lsp-codewhisperer/src/language-server/agenticChat/constants/modelSelection.test.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,19 @@ describe('modelSelection', () => {
4343
)
4444
})
4545

46-
it('should provide limited models for eu-central-1 region', () => {
46+
it('should provide all models for eu-central-1 region', () => {
4747
const euCentral1Models = MODEL_OPTIONS_FOR_REGION['eu-central-1']
48-
assert.ok(Array.isArray(euCentral1Models), 'eu-central-1 models should be an array')
49-
assert.strictEqual(euCentral1Models.length, 1, 'eu-central-1 should have 1 model')
48+
assert.deepStrictEqual(euCentral1Models, MODEL_OPTIONS, 'us-east-1 should have all models')
49+
assert.strictEqual(euCentral1Models.length, 2, 'us-east-1 should have 2 models')
5050

5151
const modelIds = euCentral1Models.map(model => model.id)
52-
assert.ok(
53-
!modelIds.includes('CLAUDE_SONNET_4_20250514_V1_0'),
54-
'eu-central-1 should not include Claude Sonnet 4'
55-
)
52+
assert.ok(modelIds.includes('CLAUDE_SONNET_4_20250514_V1_0'), 'eu-central-1 should include Claude Sonnet 4')
5653
assert.ok(
5754
modelIds.includes('CLAUDE_3_7_SONNET_20250219_V1_0'),
5855
'eu-central-1 should include Claude Sonnet 3.7'
5956
)
6057
})
6158

62-
it('should filter out Claude Sonnet 4 for eu-central-1 region', () => {
63-
const euCentral1Models = MODEL_OPTIONS_FOR_REGION['eu-central-1']
64-
const claudeSonnet4 = euCentral1Models.find(model => model.id === 'CLAUDE_SONNET_4_20250514_V1_0')
65-
assert.strictEqual(claudeSonnet4, undefined, 'Claude Sonnet 4 should be filtered out for eu-central-1')
66-
})
67-
6859
it('should fall back to all models for unknown regions', () => {
6960
// Test with a region that doesn't exist in the modelOptionsForRegion map
7061
const unknownRegionModels = MODEL_OPTIONS_FOR_REGION['unknown-region']

server/aws-lsp-codewhisperer/src/language-server/agenticChat/constants/modelSelection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ export const MODEL_OPTIONS: ListAvailableModelsResult['models'] = Object.entries
2323

2424
export const MODEL_OPTIONS_FOR_REGION: Record<string, ListAvailableModelsResult['models']> = {
2525
'us-east-1': MODEL_OPTIONS,
26-
'eu-central-1': MODEL_OPTIONS.filter(option => option.id !== BedrockModel.CLAUDE_SONNET_4_20250514_V1_0),
26+
'eu-central-1': MODEL_OPTIONS,
2727
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ describe('McpEventHandler error handling', () => {
139139
command: '', // Invalid - missing command
140140
args: [],
141141
env: {},
142+
disabled: false,
142143
__configPath__: 'config.json',
143144
},
144145
],

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

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -188,13 +188,14 @@ export class McpEventHandler {
188188
],
189189
}
190190

191-
// if (mcpManager.isServerDisabled(serverName)) {
192-
// disabledItems.push(item)
193-
// } else {
194-
activeItems.push({
195-
...item,
196-
description: `${toolsCount}`,
197-
})
191+
if (mcpManager.isServerDisabled(serverName)) {
192+
disabledItems.push(item)
193+
} else {
194+
activeItems.push({
195+
...item,
196+
description: `${toolsCount}`,
197+
})
198+
}
198199
})
199200

200201
// Create the groups
@@ -865,8 +866,10 @@ export class McpEventHandler {
865866
return { id: params.id }
866867
}
867868

869+
const mcpManager = McpManager.instance
870+
868871
// Get the appropriate agent path
869-
const agentPath = await this.#getAgentPath()
872+
const agentPath = mcpManager.getAllServerConfigs().get(serverName)?.__configPath__
870873

871874
const perm: MCPServerPermission = {
872875
enabled: true,
@@ -878,12 +881,12 @@ export class McpEventHandler {
878881
this.#isProgrammaticChange = true
879882

880883
try {
881-
await McpManager.instance.updateServerPermission(serverName, perm)
884+
await mcpManager.updateServerPermission(serverName, perm)
882885
this.#emitMCPConfigEvent()
883886
} catch (error) {
884887
this.#features.logging.error(`Failed to enable MCP server: ${error}`)
888+
this.#isProgrammaticChange = false
885889
}
886-
this.#isProgrammaticChange = false
887890
return { id: params.id }
888891
}
889892

@@ -896,8 +899,9 @@ export class McpEventHandler {
896899
return { id: params.id }
897900
}
898901

902+
const mcpManager = McpManager.instance
899903
// Get the appropriate agent path
900-
const agentPath = await this.#getAgentPath()
904+
const agentPath = mcpManager.getAllServerConfigs().get(serverName)?.__configPath__
901905

902906
const perm: MCPServerPermission = {
903907
enabled: false,
@@ -909,13 +913,13 @@ export class McpEventHandler {
909913
this.#isProgrammaticChange = true
910914

911915
try {
912-
await McpManager.instance.updateServerPermission(serverName, perm)
916+
await mcpManager.updateServerPermission(serverName, perm)
913917
this.#emitMCPConfigEvent()
914918
} catch (error) {
915919
this.#features.logging.error(`Failed to disable MCP server: ${error}`)
920+
this.#isProgrammaticChange = false
916921
}
917922

918-
this.#isProgrammaticChange = false
919923
return { id: params.id }
920924
}
921925

@@ -1229,7 +1233,9 @@ export class McpEventHandler {
12291233
// Emit MCP config event after reinitialization
12301234
const mcpManager = McpManager.instance
12311235
const serverConfigs = mcpManager.getAllServerConfigs()
1232-
const activeServers = Array.from(serverConfigs.entries())
1236+
const activeServers = Array.from(serverConfigs.entries()).filter(
1237+
([name, _]) => !mcpManager.isServerDisabled(name)
1238+
)
12331239

12341240
// Get the global agent path
12351241
const globalAgentPath = getGlobalAgentConfigPath(this.#features.workspace.fs.getUserHomeDir())
@@ -1267,12 +1273,12 @@ export class McpEventHandler {
12671273
// Emit server initialize events for all active servers
12681274
for (const [serverName, config] of serverConfigs.entries()) {
12691275
const transportType = config.command ? 'stdio' : 'http'
1270-
// const enabled = !mcpManager.isServerDisabled(serverName)
1276+
const enabled = !mcpManager.isServerDisabled(serverName)
12711277
this.#telemetryController?.emitMCPServerInitializeEvent({
12721278
source: 'reload',
12731279
command: transportType === 'stdio' ? config.command : undefined,
12741280
url: transportType === 'http' ? config.url : undefined,
1275-
enabled: true,
1281+
enabled: enabled,
12761282
numTools: mcpManager.getAllToolsWithPermissions(serverName).length,
12771283
scope: config.__configPath__ === globalAgentPath ? 'global' : 'workspace',
12781284
transportType: 'stdio',
@@ -1319,12 +1325,10 @@ export class McpEventHandler {
13191325
* @returns The agent path to use (workspace if exists, otherwise global)
13201326
*/
13211327
async #getAgentPath(isGlobal: boolean = true): Promise<string> {
1328+
const globalAgentPath = getGlobalAgentConfigPath(this.#features.workspace.fs.getUserHomeDir())
13221329
if (isGlobal) {
1323-
return getGlobalAgentConfigPath(this.#features.workspace.fs.getUserHomeDir())
1330+
return globalAgentPath
13241331
}
1325-
1326-
const globalAgentPath = getGlobalAgentConfigPath(this.#features.workspace.fs.getUserHomeDir())
1327-
13281332
// Get workspace folders and check for workspace agent path
13291333
const workspaceFolders = this.#features.workspace.getAllWorkspaceFolders()
13301334
if (workspaceFolders && workspaceFolders.length > 0) {

0 commit comments

Comments
 (0)