mcpick 0.0.2 → 0.0.4
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/.github/copilot-instructions.md +32 -0
- package/CHANGELOG.md +13 -0
- package/README.md +23 -7
- package/dist/commands/add-server.js +41 -24
- package/dist/commands/launch.js +11 -22
- package/dist/core/validation.js +28 -22
- package/dist/index.js +1 -10
- package/package.json +3 -3
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# McPick Development Instructions
|
|
2
|
+
|
|
3
|
+
**Always reference these instructions first and fallback to search or
|
|
4
|
+
bash commands only when you encounter unexpected information that does
|
|
5
|
+
not match the info here.**
|
|
6
|
+
|
|
7
|
+
## Working Effectively
|
|
8
|
+
|
|
9
|
+
### Prerequisites and Setup
|
|
10
|
+
|
|
11
|
+
- Install Node.js >=22.0.0
|
|
12
|
+
- Install pnpm globally: `npm install -g pnpm` (takes ~2 seconds)
|
|
13
|
+
|
|
14
|
+
### Development Commands
|
|
15
|
+
|
|
16
|
+
- **Format code**: `pnpm run format`
|
|
17
|
+
- **Build**: `pnpm run build`
|
|
18
|
+
|
|
19
|
+
## Validation Requirements
|
|
20
|
+
|
|
21
|
+
### ALWAYS run these before submitting changes:
|
|
22
|
+
|
|
23
|
+
1. `pnpm run format` - Auto-format all code
|
|
24
|
+
2. `pnpm run build` - Check for build issues
|
|
25
|
+
|
|
26
|
+
#### Add changeset once you're done
|
|
27
|
+
|
|
28
|
+
Run `pnpm changeset` then follow the prompts. Use this after having
|
|
29
|
+
finished the task. Most of the time this is a patch release for
|
|
30
|
+
`mcpick`. Use a short and descriptive message. Always prefix the
|
|
31
|
+
message with either `fix`, `feat`, `breaking`, or `chore` (most likely
|
|
32
|
+
`fix` since you're mostly working on bugfixes).
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# mcpick
|
|
2
2
|
|
|
3
|
+
## 0.0.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 25f7d07: chore: remove non-functional launch feature
|
|
8
|
+
|
|
9
|
+
## 0.0.3
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 158520e: fix: extract duplicated server details display logic into
|
|
14
|
+
reusable function
|
|
15
|
+
|
|
3
16
|
## 0.0.2
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# McPick
|
|
2
2
|
|
|
3
3
|
A CLI tool for dynamically managing MCP server configurations in
|
|
4
4
|
Claude Code. Enable and disable MCP servers on-demand to optimize
|
|
5
5
|
context usage and performance.
|
|
6
6
|
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
### One-time Usage
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm dlx mcpick
|
|
13
|
+
# or
|
|
14
|
+
npx mcpick
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Global Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pnpm install -g mcpick
|
|
21
|
+
# or
|
|
22
|
+
npm install -g mcpick
|
|
23
|
+
```
|
|
24
|
+
|
|
7
25
|
## The Problem
|
|
8
26
|
|
|
9
27
|
Using the Claude Code `/doctor` command you may see something like
|
|
@@ -31,7 +49,7 @@ This can lead to:
|
|
|
31
49
|
|
|
32
50
|
## The Solution
|
|
33
51
|
|
|
34
|
-
|
|
52
|
+
McPick provides an intuitive CLI menu to:
|
|
35
53
|
|
|
36
54
|
- ✅ **Toggle servers on/off** - Enable only the MCP servers you need
|
|
37
55
|
for your current task
|
|
@@ -41,22 +59,19 @@ MCPick provides an intuitive CLI menu to:
|
|
|
41
59
|
preserving other Claude Code settings
|
|
42
60
|
- 💾 **Backup & restore** - Create focused backups of your MCP server
|
|
43
61
|
configurations
|
|
44
|
-
- 🚀 **Quick launch** - Start Claude Code with your optimized
|
|
45
|
-
configuration
|
|
46
62
|
|
|
47
63
|
## Features
|
|
48
64
|
|
|
49
65
|
### Interactive Menu
|
|
50
66
|
|
|
51
67
|
```bash
|
|
52
|
-
┌
|
|
68
|
+
┌ McPick - MCP Server Configuration Manager
|
|
53
69
|
│
|
|
54
70
|
◆ What would you like to do?
|
|
55
71
|
│ ● Edit config (Toggle MCP servers on/off)
|
|
56
72
|
│ ○ Backup config
|
|
57
73
|
│ ○ Add MCP server
|
|
58
74
|
│ ○ Restore from backup
|
|
59
|
-
│ ○ Launch Claude Code
|
|
60
75
|
│ ○ Exit
|
|
61
76
|
└
|
|
62
77
|
```
|
|
@@ -85,7 +100,8 @@ MCPick provides an intuitive CLI menu to:
|
|
|
85
100
|
|
|
86
101
|
1. **Before a coding session**: Run MCPick and enable only relevant
|
|
87
102
|
servers (e.g., just database tools for DB work)
|
|
88
|
-
2. **Launch Claude Code**:
|
|
103
|
+
2. **Launch Claude Code**: Run `claude` to start with your configured
|
|
104
|
+
servers
|
|
89
105
|
3. **Switch contexts**: Re-run MCPick to enable different servers for
|
|
90
106
|
different tasks
|
|
91
107
|
|
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { confirm, note, select, text } from '@clack/prompts';
|
|
2
2
|
import { add_server_to_registry } from '../core/registry.js';
|
|
3
3
|
import { validate_mcp_server } from '../core/validation.js';
|
|
4
|
+
function format_server_details(server) {
|
|
5
|
+
const details = [`Name: ${server.name}`];
|
|
6
|
+
if ('command' in server) {
|
|
7
|
+
details.push(`Command: ${server.command} ${(server.args || []).join(' ')}`);
|
|
8
|
+
}
|
|
9
|
+
if ('url' in server) {
|
|
10
|
+
details.push(`URL: ${server.url}`);
|
|
11
|
+
}
|
|
12
|
+
details.push(`Description: ${server.description || 'None'}`);
|
|
13
|
+
if (server.type) {
|
|
14
|
+
details.push(`Transport: ${server.type}`);
|
|
15
|
+
}
|
|
16
|
+
if (server.env) {
|
|
17
|
+
details.push(`Environment: ${Object.keys(server.env).length} variables`);
|
|
18
|
+
}
|
|
19
|
+
if ('headers' in server && server.headers) {
|
|
20
|
+
details.push(`Headers: ${Object.keys(server.headers).length} headers`);
|
|
21
|
+
}
|
|
22
|
+
return details;
|
|
23
|
+
}
|
|
4
24
|
export async function add_server() {
|
|
5
25
|
try {
|
|
6
26
|
// First, ask how they want to configure the server
|
|
@@ -75,6 +95,7 @@ export async function add_server() {
|
|
|
75
95
|
return;
|
|
76
96
|
let server_data = {
|
|
77
97
|
name: name.trim(),
|
|
98
|
+
type: 'stdio',
|
|
78
99
|
command: command.trim(),
|
|
79
100
|
args,
|
|
80
101
|
...(description &&
|
|
@@ -97,11 +118,12 @@ export async function add_server() {
|
|
|
97
118
|
});
|
|
98
119
|
if (typeof transport_type === 'symbol')
|
|
99
120
|
return;
|
|
100
|
-
|
|
101
|
-
server_data.type = transport_type;
|
|
102
|
-
}
|
|
121
|
+
server_data.type = transport_type;
|
|
103
122
|
// URL for non-stdio transports
|
|
104
|
-
if (transport_type
|
|
123
|
+
if (transport_type === 'sse' || transport_type === 'http') {
|
|
124
|
+
// Remove stdio-specific fields
|
|
125
|
+
delete server_data.command;
|
|
126
|
+
delete server_data.args;
|
|
105
127
|
const url = await text({
|
|
106
128
|
message: 'Server URL:',
|
|
107
129
|
placeholder: 'e.g., http://localhost:3000',
|
|
@@ -158,10 +180,8 @@ export async function add_server() {
|
|
|
158
180
|
}
|
|
159
181
|
}
|
|
160
182
|
const validated_server = validate_mcp_server(server_data);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
`Command: ${validated_server.command} ${validated_server.args.join(' ')}\n` +
|
|
164
|
-
`Description: ${validated_server.description || 'None'}`);
|
|
183
|
+
const details = format_server_details(validated_server);
|
|
184
|
+
note(`Server to add:\n${details.join('\n')}`);
|
|
165
185
|
const should_add = await confirm({
|
|
166
186
|
message: 'Add this server to the registry?',
|
|
167
187
|
});
|
|
@@ -213,23 +233,20 @@ async function add_server_from_json() {
|
|
|
213
233
|
}
|
|
214
234
|
const parsed = JSON.parse(jsonString);
|
|
215
235
|
const server_data = parsed;
|
|
236
|
+
// Normalize the data to match schema expectations
|
|
237
|
+
if (!server_data.type && server_data.command) {
|
|
238
|
+
server_data.type = 'stdio';
|
|
239
|
+
}
|
|
240
|
+
if (server_data.type !== 'stdio') {
|
|
241
|
+
delete server_data.command;
|
|
242
|
+
delete server_data.args;
|
|
243
|
+
}
|
|
244
|
+
if (server_data.command && !server_data.args) {
|
|
245
|
+
server_data.args = [];
|
|
246
|
+
}
|
|
216
247
|
const validated_server = validate_mcp_server(server_data);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
`Command: ${validated_server.command} ${validated_server.args.join(' ')}\n` +
|
|
220
|
-
`Description: ${validated_server.description || 'None'}` +
|
|
221
|
-
(validated_server.type
|
|
222
|
-
? `\nTransport: ${validated_server.type}`
|
|
223
|
-
: '') +
|
|
224
|
-
(validated_server.url
|
|
225
|
-
? `\nURL: ${validated_server.url}`
|
|
226
|
-
: '') +
|
|
227
|
-
(validated_server.env
|
|
228
|
-
? `\nEnvironment: ${Object.keys(validated_server.env).length} variables`
|
|
229
|
-
: '') +
|
|
230
|
-
(validated_server.headers
|
|
231
|
-
? `\nHeaders: ${Object.keys(validated_server.headers).length} headers`
|
|
232
|
-
: ''));
|
|
248
|
+
const details = format_server_details(validated_server);
|
|
249
|
+
note(`Server to add:\n${details.join('\n')}`);
|
|
233
250
|
const should_add = await confirm({
|
|
234
251
|
message: 'Add this server to the registry?',
|
|
235
252
|
});
|
package/dist/commands/launch.js
CHANGED
|
@@ -1,31 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { outro } from '@clack/prompts';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
3
|
export async function launch_claude_code() {
|
|
4
|
-
const s = spinner();
|
|
5
4
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
outro('Launching Claude Code...');
|
|
6
|
+
// Replace the current process with claude using exec
|
|
7
|
+
// This ensures the terminal stays valid and Claude gets full control
|
|
8
|
+
execSync('claude', {
|
|
8
9
|
stdio: 'inherit',
|
|
9
|
-
detached: true,
|
|
10
10
|
});
|
|
11
|
-
claude_process.unref();
|
|
12
|
-
await new Promise((resolve, reject) => {
|
|
13
|
-
claude_process.on('error', (error) => {
|
|
14
|
-
if (error.message.includes('ENOENT')) {
|
|
15
|
-
reject(new Error('Claude Code not found. Make sure it is installed and in your PATH.'));
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
reject(error);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
setTimeout(() => {
|
|
22
|
-
resolve(void 0);
|
|
23
|
-
}, 1000);
|
|
24
|
-
});
|
|
25
|
-
s.stop('Claude Code launched successfully!');
|
|
26
11
|
}
|
|
27
12
|
catch (error) {
|
|
28
|
-
|
|
13
|
+
if (error instanceof Error && 'status' in error) {
|
|
14
|
+
// Claude exited normally, just return
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// Only throw for actual errors (not exit codes)
|
|
29
18
|
throw new Error(`Failed to launch Claude Code: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
30
19
|
}
|
|
31
20
|
}
|
package/dist/core/validation.js
CHANGED
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
import * as v from 'valibot';
|
|
2
|
-
export const
|
|
3
|
-
|
|
4
|
-
type: v.optional(v.union([
|
|
5
|
-
v.literal('stdio'),
|
|
6
|
-
v.literal('sse'),
|
|
7
|
-
v.literal('http'),
|
|
8
|
-
])),
|
|
2
|
+
export const mcp_server_schema_stdio = v.object({
|
|
3
|
+
type: v.optional(v.literal('stdio')),
|
|
9
4
|
command: v.pipe(v.string(), v.minLength(1)),
|
|
10
|
-
args: v.array(v.string()),
|
|
5
|
+
args: v.optional(v.array(v.string())),
|
|
11
6
|
env: v.optional(v.record(v.string(), v.string())),
|
|
12
|
-
|
|
7
|
+
description: v.optional(v.string()),
|
|
8
|
+
});
|
|
9
|
+
export const mcp_server_schema_sse = v.object({
|
|
10
|
+
type: v.literal('sse'),
|
|
11
|
+
env: v.optional(v.record(v.string(), v.string())),
|
|
12
|
+
url: v.pipe(v.string(), v.minLength(1)),
|
|
13
|
+
headers: v.optional(v.record(v.string(), v.string())),
|
|
14
|
+
description: v.optional(v.string()),
|
|
15
|
+
});
|
|
16
|
+
export const mcp_server_schema_http = v.object({
|
|
17
|
+
type: v.literal('http'),
|
|
18
|
+
env: v.optional(v.record(v.string(), v.string())),
|
|
19
|
+
url: v.pipe(v.string(), v.minLength(1)),
|
|
13
20
|
headers: v.optional(v.record(v.string(), v.string())),
|
|
14
21
|
description: v.optional(v.string()),
|
|
15
22
|
});
|
|
23
|
+
export const mcp_server_schema_base = v.union([
|
|
24
|
+
mcp_server_schema_stdio,
|
|
25
|
+
mcp_server_schema_sse,
|
|
26
|
+
mcp_server_schema_http,
|
|
27
|
+
]);
|
|
28
|
+
export const mcp_server_schema = v.intersect([
|
|
29
|
+
v.object({
|
|
30
|
+
name: v.pipe(v.string(), v.minLength(1)),
|
|
31
|
+
}),
|
|
32
|
+
mcp_server_schema_base,
|
|
33
|
+
]);
|
|
16
34
|
export const claude_config_schema = v.object({
|
|
17
|
-
mcpServers: v.optional(v.record(v.string(),
|
|
18
|
-
type: v.optional(v.union([
|
|
19
|
-
v.literal('stdio'),
|
|
20
|
-
v.literal('sse'),
|
|
21
|
-
v.literal('http'),
|
|
22
|
-
])),
|
|
23
|
-
command: v.pipe(v.string(), v.minLength(1)),
|
|
24
|
-
args: v.array(v.string()),
|
|
25
|
-
env: v.optional(v.record(v.string(), v.string())),
|
|
26
|
-
url: v.optional(v.string()),
|
|
27
|
-
headers: v.optional(v.record(v.string(), v.string())),
|
|
28
|
-
description: v.optional(v.string()),
|
|
29
|
-
}))),
|
|
35
|
+
mcpServers: v.optional(v.record(v.string(), mcp_server_schema_base)),
|
|
30
36
|
});
|
|
31
37
|
export const server_registry_schema = v.object({
|
|
32
38
|
servers: v.array(mcp_server_schema),
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,6 @@ import { cancel, intro, isCancel, outro, select, } from '@clack/prompts';
|
|
|
3
3
|
import { add_server } from './commands/add-server.js';
|
|
4
4
|
import { backup_config } from './commands/backup.js';
|
|
5
5
|
import { edit_config } from './commands/edit-config.js';
|
|
6
|
-
import { launch_claude_code } from './commands/launch.js';
|
|
7
6
|
import { restore_config } from './commands/restore.js';
|
|
8
7
|
async function main() {
|
|
9
8
|
intro('MCPick - MCP Server Configuration Manager');
|
|
@@ -32,15 +31,10 @@ async function main() {
|
|
|
32
31
|
label: 'Restore from backup',
|
|
33
32
|
hint: 'Restore from a previous backup',
|
|
34
33
|
},
|
|
35
|
-
{
|
|
36
|
-
value: 'launch',
|
|
37
|
-
label: 'Launch Claude Code',
|
|
38
|
-
hint: 'Start Claude Code with current config',
|
|
39
|
-
},
|
|
40
34
|
{
|
|
41
35
|
value: 'exit',
|
|
42
36
|
label: 'Exit',
|
|
43
|
-
hint: 'Quit MCPick',
|
|
37
|
+
hint: 'Quit MCPick (Esc)',
|
|
44
38
|
},
|
|
45
39
|
],
|
|
46
40
|
});
|
|
@@ -61,9 +55,6 @@ async function main() {
|
|
|
61
55
|
case 'restore':
|
|
62
56
|
await restore_config();
|
|
63
57
|
break;
|
|
64
|
-
case 'launch':
|
|
65
|
-
await launch_claude_code();
|
|
66
|
-
break;
|
|
67
58
|
case 'exit':
|
|
68
59
|
outro('Goodbye!');
|
|
69
60
|
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcpick",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Dynamic MCP server configuration manager for Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@changesets/cli": "^2.29.7",
|
|
28
|
-
"@types/node": "^24.
|
|
28
|
+
"@types/node": "^24.6.1",
|
|
29
29
|
"prettier": "^3.6.2",
|
|
30
|
-
"typescript": "^5.9.
|
|
30
|
+
"typescript": "^5.9.3"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "tsc",
|