mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-21 02:24:09 -07:00
fix(mcp): fix MCP server removal not persisting to settings (#10098)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
@@ -42,6 +42,9 @@ describe('commentJson', () => {
|
||||
|
||||
updateSettingsFilePreservingFormat(testFilePath, {
|
||||
model: 'gemini-2.5-flash',
|
||||
ui: {
|
||||
theme: 'dark',
|
||||
},
|
||||
});
|
||||
|
||||
const updatedContent = fs.readFileSync(testFilePath, 'utf-8');
|
||||
@@ -178,5 +181,191 @@ describe('commentJson', () => {
|
||||
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should handle array updates while preserving comments', () => {
|
||||
const originalContent = `{
|
||||
// Server configurations
|
||||
"servers": [
|
||||
// First server
|
||||
"server1",
|
||||
"server2" // Second server
|
||||
]
|
||||
}`;
|
||||
|
||||
fs.writeFileSync(testFilePath, originalContent, 'utf-8');
|
||||
|
||||
updateSettingsFilePreservingFormat(testFilePath, {
|
||||
servers: ['server1', 'server3'],
|
||||
});
|
||||
|
||||
const updatedContent = fs.readFileSync(testFilePath, 'utf-8');
|
||||
expect(updatedContent).toContain('// Server configurations');
|
||||
expect(updatedContent).toContain('"server1"');
|
||||
expect(updatedContent).toContain('"server3"');
|
||||
expect(updatedContent).not.toContain('"server2"');
|
||||
});
|
||||
|
||||
it('should sync nested objects, removing omitted fields', () => {
|
||||
const originalContent = `{
|
||||
// Configuration
|
||||
"model": "gemini-2.5-pro",
|
||||
"ui": {
|
||||
"theme": "dark",
|
||||
"existingSetting": "value"
|
||||
},
|
||||
"preservedField": "keep me"
|
||||
}`;
|
||||
|
||||
fs.writeFileSync(testFilePath, originalContent, 'utf-8');
|
||||
|
||||
updateSettingsFilePreservingFormat(testFilePath, {
|
||||
model: 'gemini-2.5-flash',
|
||||
ui: {
|
||||
theme: 'light',
|
||||
},
|
||||
preservedField: 'keep me',
|
||||
});
|
||||
|
||||
const updatedContent = fs.readFileSync(testFilePath, 'utf-8');
|
||||
expect(updatedContent).toContain('// Configuration');
|
||||
expect(updatedContent).toContain('"model": "gemini-2.5-flash"');
|
||||
expect(updatedContent).toContain('"theme": "light"');
|
||||
expect(updatedContent).not.toContain('"existingSetting": "value"');
|
||||
expect(updatedContent).toContain('"preservedField": "keep me"');
|
||||
});
|
||||
|
||||
it('should handle mcpServers field deletion properly', () => {
|
||||
const originalContent = `{
|
||||
"model": "gemini-2.5-pro",
|
||||
"mcpServers": {
|
||||
// Server to keep
|
||||
"context7": {
|
||||
"command": "node",
|
||||
"args": ["server.js"]
|
||||
},
|
||||
// Server to remove
|
||||
"oldServer": {
|
||||
"command": "old",
|
||||
"args": ["old.js"]
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
fs.writeFileSync(testFilePath, originalContent, 'utf-8');
|
||||
|
||||
updateSettingsFilePreservingFormat(testFilePath, {
|
||||
model: 'gemini-2.5-pro',
|
||||
mcpServers: {
|
||||
context7: {
|
||||
command: 'node',
|
||||
args: ['server.js'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const updatedContent = fs.readFileSync(testFilePath, 'utf-8');
|
||||
expect(updatedContent).toContain('// Server to keep');
|
||||
expect(updatedContent).toContain('"context7"');
|
||||
expect(updatedContent).not.toContain('"oldServer"');
|
||||
// The comment for the removed server should still be preserved
|
||||
expect(updatedContent).toContain('// Server to remove');
|
||||
});
|
||||
|
||||
it('preserves sibling-level commented-out blocks when removing another key', () => {
|
||||
const originalContent = `{
|
||||
"mcpServers": {
|
||||
// "sleep": {
|
||||
// "command": "node",
|
||||
// "args": [
|
||||
// "/Users/testUser/test-mcp-server/sleep-mcp/build/index.js"
|
||||
// ],
|
||||
// "timeout": 300000
|
||||
// },
|
||||
"playwright": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"@playwright/mcp@latest",
|
||||
"--headless",
|
||||
"--isolated"
|
||||
]
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
fs.writeFileSync(testFilePath, originalContent, 'utf-8');
|
||||
|
||||
updateSettingsFilePreservingFormat(testFilePath, {
|
||||
mcpServers: {},
|
||||
});
|
||||
|
||||
const updatedContent = fs.readFileSync(testFilePath, 'utf-8');
|
||||
expect(updatedContent).toContain('// "sleep": {');
|
||||
expect(updatedContent).toContain('"mcpServers"');
|
||||
expect(updatedContent).not.toContain('"playwright"');
|
||||
});
|
||||
|
||||
it('should handle type conversion from object to array', () => {
|
||||
const originalContent = `{
|
||||
"data": {
|
||||
"key": "value"
|
||||
}
|
||||
}`;
|
||||
|
||||
fs.writeFileSync(testFilePath, originalContent, 'utf-8');
|
||||
|
||||
updateSettingsFilePreservingFormat(testFilePath, {
|
||||
data: ['item1', 'item2'],
|
||||
});
|
||||
|
||||
const updatedContent = fs.readFileSync(testFilePath, 'utf-8');
|
||||
expect(updatedContent).toContain('"data": [');
|
||||
expect(updatedContent).toContain('"item1"');
|
||||
expect(updatedContent).toContain('"item2"');
|
||||
});
|
||||
|
||||
it('should remove both nested and non-nested objects when omitted', () => {
|
||||
const originalContent = `{
|
||||
// Top-level config
|
||||
"topLevelObject": {
|
||||
"field1": "value1",
|
||||
"field2": "value2"
|
||||
},
|
||||
// Parent object
|
||||
"parent": {
|
||||
"nestedObject": {
|
||||
"nestedField1": "value1",
|
||||
"nestedField2": "value2"
|
||||
},
|
||||
"keepThis": "value"
|
||||
},
|
||||
// This should be preserved
|
||||
"preservedObject": {
|
||||
"data": "keep"
|
||||
}
|
||||
}`;
|
||||
|
||||
fs.writeFileSync(testFilePath, originalContent, 'utf-8');
|
||||
|
||||
updateSettingsFilePreservingFormat(testFilePath, {
|
||||
parent: {
|
||||
keepThis: 'value',
|
||||
},
|
||||
preservedObject: {
|
||||
data: 'keep',
|
||||
},
|
||||
});
|
||||
|
||||
const updatedContent = fs.readFileSync(testFilePath, 'utf-8');
|
||||
|
||||
expect(updatedContent).not.toContain('"topLevelObject"');
|
||||
|
||||
expect(updatedContent).not.toContain('"nestedObject"');
|
||||
|
||||
expect(updatedContent).toContain('"keepThis": "value"');
|
||||
expect(updatedContent).toContain('"preservedObject"');
|
||||
expect(updatedContent).toContain('"data": "keep"');
|
||||
|
||||
expect(updatedContent).toContain('// This should be preserved');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user