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.
@@ -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
- # MCPick
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
- MCPick provides an intuitive CLI menu to:
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
- MCPick - MCP Server Configuration Manager
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**: Use MCPick's "Launch Claude Code" option
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
- if (transport_type !== 'stdio') {
101
- server_data.type = transport_type;
102
- }
121
+ server_data.type = transport_type;
103
122
  // URL for non-stdio transports
104
- if (transport_type !== 'stdio') {
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
- note(`Server to add:\n` +
162
- `Name: ${validated_server.name}\n` +
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
- note(`Server to add:\n` +
218
- `Name: ${validated_server.name}\n` +
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
  });
@@ -1,31 +1,20 @@
1
- import { spinner } from '@clack/prompts';
2
- import { spawn } from 'node:child_process';
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
- s.start('Launching Claude Code...');
7
- const claude_process = spawn('claude', ['code'], {
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
- s.stop('Failed to launch Claude Code');
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
  }
@@ -1,32 +1,38 @@
1
1
  import * as v from 'valibot';
2
- export const mcp_server_schema = v.object({
3
- name: v.pipe(v.string(), v.minLength(1)),
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
- url: v.optional(v.string()),
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(), v.object({
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.2",
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.5.2",
28
+ "@types/node": "^24.6.1",
29
29
  "prettier": "^3.6.2",
30
- "typescript": "^5.9.2"
30
+ "typescript": "^5.9.3"
31
31
  },
32
32
  "scripts": {
33
33
  "build": "tsc",