mcp-config-manager 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -2
- package/package.json +2 -1
- package/src/claude-client.js +35 -0
- package/src/clients.js +21 -3
- package/src/config-manager.js +29 -5
package/README.md
CHANGED
|
@@ -149,11 +149,13 @@ This tool supports auto-detection of any client that follows the Model Context P
|
|
|
149
149
|
|
|
150
150
|
- **Amazon Q Developer**: `~/.aws/amazonq/mcp.json`
|
|
151
151
|
- **Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
152
|
-
- **Claude Code**:
|
|
152
|
+
- **Claude Code**: `~/.claude.json` (global settings and MCP servers)
|
|
153
153
|
- **Cline**: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
|
|
154
|
+
- **Codex**: `~/.codex/config.toml` (TOML with `[mcp_servers.<name>]` tables)
|
|
154
155
|
- **Cursor**: `.cursor/mcp.json` (project-specific) or `~/.cursor/mcp.json` (global)
|
|
155
156
|
- **Factory Bridge**: `~/Library/Application Support/Factory Bridge/mcp.json`
|
|
156
157
|
- **Gemini**: `~/.gemini/settings.json`
|
|
158
|
+
- **Google AntiGravity**: `~/.gemini/antigravity/mcp_config.json`
|
|
157
159
|
- **Roo Code**: `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`
|
|
158
160
|
- **VS Code**: `.vscode/mcp.json`
|
|
159
161
|
- **Windsurf**: `~/.codeium/windsurf/mcp_config.json` or `~/AppData/Roaming/WindSurf/mcp_settings.json` (Windows)
|
|
@@ -162,7 +164,7 @@ This tool supports auto-detection of any client that follows the Model Context P
|
|
|
162
164
|
|
|
163
165
|
## Configuration Format
|
|
164
166
|
|
|
165
|
-
Standard MCP server configuration:
|
|
167
|
+
Standard MCP server configuration (JSON-based clients like Claude Desktop, Claude Code, Cursor, VS Code, etc.):
|
|
166
168
|
```json
|
|
167
169
|
{
|
|
168
170
|
"mcpServers": {
|
|
@@ -177,6 +179,18 @@ Standard MCP server configuration:
|
|
|
177
179
|
}
|
|
178
180
|
```
|
|
179
181
|
|
|
182
|
+
Codex uses a TOML configuration file at `~/.codex/config.toml`, where MCP servers are defined under the `[mcp_servers.<server-name>]` tables. For example:
|
|
183
|
+
|
|
184
|
+
```toml
|
|
185
|
+
[mcp_servers.context7]
|
|
186
|
+
command = "npx"
|
|
187
|
+
args = ["-y", "@upstash/context7-mcp"]
|
|
188
|
+
|
|
189
|
+
[mcp_servers.figma]
|
|
190
|
+
url = "https://mcp.figma.com/mcp"
|
|
191
|
+
bearer_token_env_var = "FIGMA_OAUTH_TOKEN"
|
|
192
|
+
```
|
|
193
|
+
|
|
180
194
|
## Security Note
|
|
181
195
|
|
|
182
196
|
Environment variables may contain sensitive data like API keys. The tool masks values containing "KEY" or "SECRET" in the display but stores them in plain text in config files.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-config-manager",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Simple CLI and web UI to manage MCP configs across multiple AI clients",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
],
|
|
41
41
|
"type": "module",
|
|
42
42
|
"dependencies": {
|
|
43
|
+
"@iarna/toml": "^3.0.0",
|
|
43
44
|
"commander": "^14.0.1",
|
|
44
45
|
"cors": "^2.8.5",
|
|
45
46
|
"express": "^5.1.0"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
|
|
5
|
+
async function getProjectConfig(projectPath) {
|
|
6
|
+
const configPath = path.join(projectPath, '.mcp.json');
|
|
7
|
+
try {
|
|
8
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
9
|
+
return JSON.parse(content);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (error.code === 'ENOENT') {
|
|
12
|
+
return null; // File not found
|
|
13
|
+
}
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function getUserConfig() {
|
|
19
|
+
const homeDir = os.homedir();
|
|
20
|
+
const configPath = path.join(homeDir, '.mcp.json');
|
|
21
|
+
try {
|
|
22
|
+
const content = await fs.readFile(configPath, 'utf-8');
|
|
23
|
+
return JSON.parse(content);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error.code === 'ENOENT') {
|
|
26
|
+
return null; // File not found
|
|
27
|
+
}
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
getProjectConfig,
|
|
34
|
+
getUserConfig,
|
|
35
|
+
};
|
package/src/clients.js
CHANGED
|
@@ -14,9 +14,9 @@ export const CLIENTS = {
|
|
|
14
14
|
'claude-code': {
|
|
15
15
|
name: 'Claude Code',
|
|
16
16
|
configPaths: {
|
|
17
|
-
darwin: '.
|
|
18
|
-
win32: '.
|
|
19
|
-
linux: '.
|
|
17
|
+
darwin: path.join(os.homedir(), '.claude.json'),
|
|
18
|
+
win32: path.join(os.homedir(), '.claude.json'),
|
|
19
|
+
linux: path.join(os.homedir(), '.claude.json')
|
|
20
20
|
},
|
|
21
21
|
format: 'mcpServers'
|
|
22
22
|
},
|
|
@@ -74,6 +74,15 @@ export const CLIENTS = {
|
|
|
74
74
|
},
|
|
75
75
|
format: 'mcpServers'
|
|
76
76
|
},
|
|
77
|
+
codex: {
|
|
78
|
+
name: 'Codex',
|
|
79
|
+
configPaths: {
|
|
80
|
+
darwin: path.join(os.homedir(), '.codex/config.toml'),
|
|
81
|
+
win32: path.join(os.homedir(), '.codex/config.toml'),
|
|
82
|
+
linux: path.join(os.homedir(), '.codex/config.toml')
|
|
83
|
+
},
|
|
84
|
+
format: 'codex'
|
|
85
|
+
},
|
|
77
86
|
'5ire': {
|
|
78
87
|
name: '5ire',
|
|
79
88
|
configPaths: {
|
|
@@ -109,5 +118,14 @@ export const CLIENTS = {
|
|
|
109
118
|
linux: path.join(os.homedir(), '.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json')
|
|
110
119
|
},
|
|
111
120
|
format: 'mcpServers'
|
|
121
|
+
},
|
|
122
|
+
'google-antigravity': {
|
|
123
|
+
name: 'Google AntiGravity',
|
|
124
|
+
configPaths: {
|
|
125
|
+
darwin: path.join(os.homedir(), '.gemini/antigravity/mcp_config.json'),
|
|
126
|
+
win32: path.join(os.homedir(), '.gemini/antigravity/mcp_config.json'),
|
|
127
|
+
linux: path.join(os.homedir(), '.gemini/antigravity/mcp_config.json')
|
|
128
|
+
},
|
|
129
|
+
format: 'mcpServers'
|
|
112
130
|
}
|
|
113
131
|
};
|
package/src/config-manager.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import os from 'os';
|
|
4
|
+
import TOML from '@iarna/toml';
|
|
4
5
|
|
|
5
6
|
import { CLIENTS as PROD_CLIENTS } from './clients.js';
|
|
6
7
|
|
|
@@ -223,11 +224,15 @@ export class MCPConfigManager {
|
|
|
223
224
|
const configPath = await this.getConfigPath(client);
|
|
224
225
|
let clientConfig = { servers: {} };
|
|
225
226
|
|
|
227
|
+
const clients = await this.getClients();
|
|
228
|
+
const clientInfo = clients[client];
|
|
229
|
+
|
|
226
230
|
try {
|
|
227
231
|
const content = await fs.readFile(configPath, 'utf-8');
|
|
228
|
-
const parsedContent =
|
|
229
|
-
|
|
230
|
-
|
|
232
|
+
const parsedContent = clientInfo.format === 'codex'
|
|
233
|
+
? TOML.parse(content)
|
|
234
|
+
: JSON.parse(content);
|
|
235
|
+
clientConfig = this.normalizeConfig(parsedContent, clientInfo.format);
|
|
231
236
|
} catch (error) {
|
|
232
237
|
if (error.code !== 'ENOENT') {
|
|
233
238
|
throw error;
|
|
@@ -251,6 +256,9 @@ export class MCPConfigManager {
|
|
|
251
256
|
return { servers: config.mcpServers || {} };
|
|
252
257
|
} else if (format === 'mcp.servers') {
|
|
253
258
|
return { servers: config.mcp?.servers || {} };
|
|
259
|
+
} else if (format === 'codex') {
|
|
260
|
+
// Codex stores MCP servers under [mcp_servers.<server-name>] tables in config.toml
|
|
261
|
+
return { servers: config.mcp_servers || {} };
|
|
254
262
|
}
|
|
255
263
|
return { servers: {} };
|
|
256
264
|
}
|
|
@@ -266,6 +274,15 @@ export class MCPConfigManager {
|
|
|
266
274
|
servers: normalizedConfig.servers
|
|
267
275
|
}
|
|
268
276
|
};
|
|
277
|
+
} else if (format === 'codex') {
|
|
278
|
+
// Preserve other Codex settings and update only the mcp_servers table
|
|
279
|
+
return {
|
|
280
|
+
...originalConfig,
|
|
281
|
+
mcp_servers: {
|
|
282
|
+
...(originalConfig.mcp_servers || {}),
|
|
283
|
+
...normalizedConfig.servers
|
|
284
|
+
}
|
|
285
|
+
};
|
|
269
286
|
}
|
|
270
287
|
return originalConfig;
|
|
271
288
|
}
|
|
@@ -278,7 +295,9 @@ export class MCPConfigManager {
|
|
|
278
295
|
let originalConfig = {};
|
|
279
296
|
try {
|
|
280
297
|
const content = await fs.readFile(configPath, 'utf-8');
|
|
281
|
-
originalConfig =
|
|
298
|
+
originalConfig = clientConfig.format === 'codex'
|
|
299
|
+
? TOML.parse(content)
|
|
300
|
+
: JSON.parse(content);
|
|
282
301
|
} catch (error) {
|
|
283
302
|
// File doesn't exist, that's OK
|
|
284
303
|
}
|
|
@@ -286,7 +305,12 @@ export class MCPConfigManager {
|
|
|
286
305
|
const finalConfig = this.denormalizeConfig(config, clientConfig.format, originalConfig);
|
|
287
306
|
|
|
288
307
|
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
289
|
-
|
|
308
|
+
|
|
309
|
+
if (clientConfig.format === 'codex') {
|
|
310
|
+
await fs.writeFile(configPath, TOML.stringify(finalConfig));
|
|
311
|
+
} else {
|
|
312
|
+
await fs.writeFile(configPath, JSON.stringify(finalConfig, null, 2));
|
|
313
|
+
}
|
|
290
314
|
}
|
|
291
315
|
|
|
292
316
|
|