mcp-config-manager 1.0.7 → 1.0.8
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 +15 -2
- package/package.json +2 -1
- package/src/claude-client.js +35 -0
- package/src/clients.js +12 -3
- package/src/config-manager.js +29 -5
package/README.md
CHANGED
|
@@ -149,7 +149,7 @@ 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
154
|
- **Cursor**: `.cursor/mcp.json` (project-specific) or `~/.cursor/mcp.json` (global)
|
|
155
155
|
- **Factory Bridge**: `~/Library/Application Support/Factory Bridge/mcp.json`
|
|
@@ -157,12 +157,13 @@ This tool supports auto-detection of any client that follows the Model Context P
|
|
|
157
157
|
- **Roo Code**: `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`
|
|
158
158
|
- **VS Code**: `.vscode/mcp.json`
|
|
159
159
|
- **Windsurf**: `~/.codeium/windsurf/mcp_config.json` or `~/AppData/Roaming/WindSurf/mcp_settings.json` (Windows)
|
|
160
|
+
- **Codex**: `~/.codex/config.toml` (TOML with `[mcp_servers.<name>]` tables)
|
|
160
161
|
|
|
161
162
|
*Note: Paths may vary based on your operating system. The tool will attempt to find the correct path automatically.*
|
|
162
163
|
|
|
163
164
|
## Configuration Format
|
|
164
165
|
|
|
165
|
-
Standard MCP server configuration:
|
|
166
|
+
Standard MCP server configuration (JSON-based clients like Claude Desktop, Claude Code, Cursor, VS Code, etc.):
|
|
166
167
|
```json
|
|
167
168
|
{
|
|
168
169
|
"mcpServers": {
|
|
@@ -177,6 +178,18 @@ Standard MCP server configuration:
|
|
|
177
178
|
}
|
|
178
179
|
```
|
|
179
180
|
|
|
181
|
+
Codex uses a TOML configuration file at `~/.codex/config.toml`, where MCP servers are defined under the `[mcp_servers.<server-name>]` tables. For example:
|
|
182
|
+
|
|
183
|
+
```toml
|
|
184
|
+
[mcp_servers.context7]
|
|
185
|
+
command = "npx"
|
|
186
|
+
args = ["-y", "@upstash/context7-mcp"]
|
|
187
|
+
|
|
188
|
+
[mcp_servers.figma]
|
|
189
|
+
url = "https://mcp.figma.com/mcp"
|
|
190
|
+
bearer_token_env_var = "FIGMA_OAUTH_TOKEN"
|
|
191
|
+
```
|
|
192
|
+
|
|
180
193
|
## Security Note
|
|
181
194
|
|
|
182
195
|
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.8",
|
|
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: {
|
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
|
|