mcp-config-manager 1.0.6 → 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 CHANGED
@@ -63,70 +63,56 @@ npm link
63
63
 
64
64
  ```bash
65
65
  # List all clients and their status
66
- mcp-manager list
66
+ mcp-config-manager list
67
67
 
68
68
  # Show servers for a specific client
69
- mcp-manager show claude
69
+ mcp-config-manager show claude
70
70
 
71
71
  # Add a new server
72
- mcp-manager add claude my-server -c npx -a "-y" "@example/server" -e API_KEY=abc123
72
+ mcp-config-manager add claude my-server -c npx -a "-y" "@example/server" -e API_KEY=abc123
73
73
 
74
74
  # Remove a server from one client
75
- mcp-manager remove claude my-server
75
+ mcp-config-manager remove claude my-server
76
76
 
77
77
  # Remove a server from ALL clients
78
- mcp-manager remove all my-server
78
+ mcp-config-manager remove all my-server
79
79
 
80
80
  # Copy server between clients
81
- mcp-manager copy claude my-server cursor
81
+ mcp-config-manager copy claude my-server cursor
82
82
 
83
83
  # Copy server to ALL other clients
84
- mcp-manager copy claude my-server all
84
+ mcp-config-manager copy claude my-server all
85
85
 
86
86
  # Manage environment variables
87
- mcp-manager env claude my-server set API_KEY new-value
88
- mcp-manager env claude my-server unset API_KEY
89
- mcp-manager env claude my-server list
87
+ mcp-config-manager env claude my-server set API_KEY new-value
88
+ mcp-config-manager env claude my-server unset API_KEY
89
+ mcp-config-manager env claude my-server list
90
90
 
91
91
  # Export/Import configurations
92
- mcp-manager export claude config.json
93
- mcp-manager export claude --server my-server server-config.json
94
- mcp-manager import cursor config.json
92
+ mcp-config-manager export claude config.json
93
+ mcp-config-manager export claude --server my-server server-config.json
94
+ mcp-config-manager import cursor config.json
95
95
 
96
96
  # Start web UI
97
- mcp-manager web
97
+ mcp-config-manager web
98
98
  # or
99
99
  npm run web
100
100
  ```
101
101
 
102
- ### Web UI
103
-
104
- #### Development (when cloning repository)
105
- ```bash
106
- npm run web
107
- ```
108
-
109
- #### Global Installation
110
- After installing globally with `npm install -g mcp-config-manager`:
102
+ **Note:** `mcp-manager` also works as a shorter alias for `mcp-config-manager`.
111
103
 
112
- **Start web server in foreground:**
113
- ```bash
114
- mcp-config-manager web --port 3456
115
- ```
104
+ ### Web UI
116
105
 
117
- **Start web server as background daemon:**
106
+ **Start the web server:**
118
107
  ```bash
119
- mcp-config-manager start --port 3456
120
- ```
108
+ # If installed globally
109
+ mcp-config-manager web
121
110
 
122
- **Check daemon status:**
123
- ```bash
124
- mcp-config-manager status
125
- ```
111
+ # If running from cloned repository
112
+ npm run web
126
113
 
127
- **Stop daemon:**
128
- ```bash
129
- mcp-config-manager stop
114
+ # Custom port (default is 3456)
115
+ PORT=3457 npm run web
130
116
  ```
131
117
 
132
118
  Then open http://localhost:3456 in your browser.
@@ -163,7 +149,7 @@ This tool supports auto-detection of any client that follows the Model Context P
163
149
 
164
150
  - **Amazon Q Developer**: `~/.aws/amazonq/mcp.json`
165
151
  - **Claude Desktop**: `~/Library/Application Support/Claude/claude_desktop_config.json`
166
- - **Claude Code**: `.mcp.json` in project root
152
+ - **Claude Code**: `~/.claude.json` (global settings and MCP servers)
167
153
  - **Cline**: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
168
154
  - **Cursor**: `.cursor/mcp.json` (project-specific) or `~/.cursor/mcp.json` (global)
169
155
  - **Factory Bridge**: `~/Library/Application Support/Factory Bridge/mcp.json`
@@ -171,12 +157,13 @@ This tool supports auto-detection of any client that follows the Model Context P
171
157
  - **Roo Code**: `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`
172
158
  - **VS Code**: `.vscode/mcp.json`
173
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)
174
161
 
175
162
  *Note: Paths may vary based on your operating system. The tool will attempt to find the correct path automatically.*
176
163
 
177
164
  ## Configuration Format
178
165
 
179
- Standard MCP server configuration:
166
+ Standard MCP server configuration (JSON-based clients like Claude Desktop, Claude Code, Cursor, VS Code, etc.):
180
167
  ```json
181
168
  {
182
169
  "mcpServers": {
@@ -191,12 +178,54 @@ Standard MCP server configuration:
191
178
  }
192
179
  ```
193
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
+
194
193
  ## Security Note
195
194
 
196
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.
197
196
 
198
197
  ## Development & Testing
199
198
 
199
+ ### Development Workflow
200
+
201
+ **Complete workflow for changes:**
202
+ ```bash
203
+ # 1. Make your changes to the code
204
+
205
+ # 2. Run tests to verify everything works
206
+ npm test
207
+
208
+ # 3. Update README.md with any new features or changes
209
+
210
+ # 4. Commit changes
211
+ git add .
212
+ git commit -m "feat: Your feature description
213
+
214
+ - Detailed changes
215
+ - Tests: 17/17 CLI ✅, 2/2 UI ✅
216
+
217
+ 🤖 Generated with [Claude Code](https://claude.ai/code)
218
+
219
+ Co-Authored-By: Claude <noreply@anthropic.com>"
220
+
221
+ # 5. Push to repository
222
+ git push
223
+
224
+ # 6. Publish to npm (maintainers only)
225
+ npm version patch # or minor/major
226
+ npm publish
227
+ ```
228
+
200
229
  ### Running Tests
201
230
 
202
231
  The project includes comprehensive test coverage:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-config-manager",
3
- "version": "1.0.6",
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: '.mcp.json',
18
- win32: '.mcp.json',
19
- linux: '.mcp.json'
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: {
@@ -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 = JSON.parse(content);
229
- const clients = await this.getClients();
230
- clientConfig = this.normalizeConfig(parsedContent, clients[client].format);
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 = JSON.parse(content);
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
- await fs.writeFile(configPath, JSON.stringify(finalConfig, null, 2));
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