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 +68 -39
- 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
|
@@ -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
|
-
|
|
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
|
-
|
|
113
|
-
```bash
|
|
114
|
-
mcp-config-manager web --port 3456
|
|
115
|
-
```
|
|
104
|
+
### Web UI
|
|
116
105
|
|
|
117
|
-
**Start web server
|
|
106
|
+
**Start the web server:**
|
|
118
107
|
```bash
|
|
119
|
-
|
|
120
|
-
|
|
108
|
+
# If installed globally
|
|
109
|
+
mcp-config-manager web
|
|
121
110
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
mcp-config-manager status
|
|
125
|
-
```
|
|
111
|
+
# If running from cloned repository
|
|
112
|
+
npm run web
|
|
126
113
|
|
|
127
|
-
|
|
128
|
-
|
|
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**:
|
|
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.
|
|
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
|
|