mcpick 0.0.1 → 0.0.3

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,22 @@
1
1
  # mcpick
2
2
 
3
+ ## 0.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 158520e: fix: extract duplicated server details display logic into
8
+ reusable function
9
+
10
+ ## 0.0.2
11
+
12
+ ### Patch Changes
13
+
14
+ - 6a3eec7: paste json server config
15
+ - a62953b: Removed estimated_tokens prompt and references, Enhanced
16
+ add-server dialogue
17
+ - 491d543: make json first options for adding server
18
+ - 3242ff1: remove unused docs
19
+
3
20
  ## 0.0.1
4
21
 
5
22
  ### 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
@@ -49,7 +67,7 @@ MCPick provides an intuitive CLI menu to:
49
67
  ### Interactive Menu
50
68
 
51
69
  ```bash
52
- MCPick - MCP Server Configuration Manager
70
+ McPick - MCP Server Configuration Manager
53
71
 
54
72
  ◆ What would you like to do?
55
73
  │ ● Edit config (Toggle MCP servers on/off)
@@ -1,8 +1,50 @@
1
- import { confirm, note, text } from '@clack/prompts';
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 {
26
+ // First, ask how they want to configure the server
27
+ const config_method = await select({
28
+ message: 'How would you like to add the server?',
29
+ options: [
30
+ {
31
+ value: 'json',
32
+ label: 'Paste JSON configuration',
33
+ hint: 'Paste complete server config as JSON',
34
+ },
35
+ {
36
+ value: 'form',
37
+ label: 'Step-by-step form',
38
+ hint: 'Fill out fields one by one',
39
+ },
40
+ ],
41
+ initialValue: 'json',
42
+ });
43
+ if (typeof config_method === 'symbol')
44
+ return;
45
+ if (config_method === 'json') {
46
+ return await add_server_from_json();
47
+ }
6
48
  const name = await text({
7
49
  message: 'Server name:',
8
50
  placeholder: 'e.g., mcp-sqlite-tools',
@@ -44,37 +86,102 @@ export async function add_server() {
44
86
  });
45
87
  if (typeof description === 'symbol')
46
88
  return;
47
- const estimated_tokens_input = await text({
48
- message: 'Estimated tokens (optional):',
49
- placeholder: 'e.g., 5000',
50
- validate: (value) => {
51
- if (value && value.trim().length > 0) {
52
- const num = parseInt(value.trim());
53
- if (isNaN(num) || num < 0) {
54
- return 'Must be a positive number';
55
- }
56
- }
57
- return undefined;
58
- },
89
+ // Advanced configuration
90
+ const configure_advanced = await confirm({
91
+ message: 'Configure advanced settings (env variables, transport, etc.)?',
92
+ initialValue: false,
59
93
  });
60
- if (typeof estimated_tokens_input === 'symbol')
94
+ if (typeof configure_advanced === 'symbol')
61
95
  return;
62
- const server_data = {
96
+ let server_data = {
63
97
  name: name.trim(),
98
+ type: 'stdio',
64
99
  command: command.trim(),
65
100
  args,
66
101
  ...(description &&
67
102
  description.trim() && { description: description.trim() }),
68
- ...(estimated_tokens_input &&
69
- estimated_tokens_input.trim() && {
70
- estimated_tokens: parseInt(estimated_tokens_input.trim()),
71
- }),
72
103
  };
104
+ if (configure_advanced) {
105
+ // Transport type
106
+ const transport_type = await select({
107
+ message: 'Transport type:',
108
+ options: [
109
+ {
110
+ value: 'stdio',
111
+ label: 'stdio (default)',
112
+ hint: 'Standard input/output',
113
+ },
114
+ { value: 'sse', label: 'sse', hint: 'Server-sent events' },
115
+ { value: 'http', label: 'http', hint: 'HTTP transport' },
116
+ ],
117
+ initialValue: 'stdio',
118
+ });
119
+ if (typeof transport_type === 'symbol')
120
+ return;
121
+ server_data.type = transport_type;
122
+ // URL for non-stdio transports
123
+ if (transport_type === 'sse' || transport_type === 'http') {
124
+ // Remove stdio-specific fields
125
+ delete server_data.command;
126
+ delete server_data.args;
127
+ const url = await text({
128
+ message: 'Server URL:',
129
+ placeholder: 'e.g., http://localhost:3000',
130
+ validate: (value) => {
131
+ if (!value || value.trim().length === 0) {
132
+ return 'URL is required for non-stdio transport';
133
+ }
134
+ return undefined;
135
+ },
136
+ });
137
+ if (typeof url === 'symbol')
138
+ return;
139
+ server_data.url = url.trim();
140
+ }
141
+ // Environment variables
142
+ const env_input = await text({
143
+ message: 'Environment variables (KEY=value, comma-separated):',
144
+ placeholder: 'e.g., API_KEY=abc123, TIMEOUT=30',
145
+ });
146
+ if (typeof env_input === 'symbol')
147
+ return;
148
+ if (env_input && env_input.trim()) {
149
+ const env = {};
150
+ env_input.split(',').forEach((pair) => {
151
+ const [key, ...valueParts] = pair.split('=');
152
+ if (key && valueParts.length > 0) {
153
+ env[key.trim()] = valueParts.join('=').trim();
154
+ }
155
+ });
156
+ if (Object.keys(env).length > 0) {
157
+ server_data.env = env;
158
+ }
159
+ }
160
+ // Headers for HTTP transport
161
+ if (transport_type === 'http') {
162
+ const headers_input = await text({
163
+ message: 'HTTP headers (KEY=value, comma-separated):',
164
+ placeholder: 'e.g., Authorization=Bearer token, Content-Type=application/json',
165
+ });
166
+ if (typeof headers_input === 'symbol')
167
+ return;
168
+ if (headers_input && headers_input.trim()) {
169
+ const headers = {};
170
+ headers_input.split(',').forEach((pair) => {
171
+ const [key, ...valueParts] = pair.split('=');
172
+ if (key && valueParts.length > 0) {
173
+ headers[key.trim()] = valueParts.join('=').trim();
174
+ }
175
+ });
176
+ if (Object.keys(headers).length > 0) {
177
+ server_data.headers = headers;
178
+ }
179
+ }
180
+ }
181
+ }
73
182
  const validated_server = validate_mcp_server(server_data);
74
- note(`Server to add:\n` +
75
- `Name: ${validated_server.name}\n` +
76
- `Command: ${validated_server.command} ${validated_server.args.join(' ')}\n` +
77
- `Description: ${validated_server.description || 'None'}`);
183
+ const details = format_server_details(validated_server);
184
+ note(`Server to add:\n${details.join('\n')}`);
78
185
  const should_add = await confirm({
79
186
  message: 'Add this server to the registry?',
80
187
  });
@@ -88,4 +195,69 @@ export async function add_server() {
88
195
  throw new Error(`Failed to add server: ${error instanceof Error ? error.message : 'Unknown error'}`);
89
196
  }
90
197
  }
198
+ async function add_server_from_json() {
199
+ const json_input = await text({
200
+ message: 'Paste JSON configuration:',
201
+ placeholder: '{ "name": "mcp-sqlite-tools", "command": "npx", "args": ["-y", "mcp-sqlite-tools"] }',
202
+ validate: (value) => {
203
+ if (!value || value.trim().length === 0) {
204
+ return 'JSON configuration is required';
205
+ }
206
+ let jsonString = value.trim();
207
+ // If it doesn't start with {, wrap it in braces
208
+ if (!jsonString.startsWith('{')) {
209
+ jsonString = `{${jsonString}}`;
210
+ }
211
+ try {
212
+ const parsed = JSON.parse(jsonString);
213
+ if (typeof parsed !== 'object' || parsed === null) {
214
+ return 'JSON must be an object';
215
+ }
216
+ if (!parsed.command) {
217
+ return 'Server configuration must include a "command" field';
218
+ }
219
+ }
220
+ catch (error) {
221
+ return 'Invalid JSON format';
222
+ }
223
+ return undefined;
224
+ },
225
+ });
226
+ if (typeof json_input === 'symbol')
227
+ return;
228
+ try {
229
+ let jsonString = json_input.trim();
230
+ // If it doesn't start with {, wrap it in braces
231
+ if (!jsonString.startsWith('{')) {
232
+ jsonString = `{${jsonString}}`;
233
+ }
234
+ const parsed = JSON.parse(jsonString);
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
+ }
247
+ const validated_server = validate_mcp_server(server_data);
248
+ const details = format_server_details(validated_server);
249
+ note(`Server to add:\n${details.join('\n')}`);
250
+ const should_add = await confirm({
251
+ message: 'Add this server to the registry?',
252
+ });
253
+ if (typeof should_add === 'symbol' || !should_add) {
254
+ return;
255
+ }
256
+ await add_server_to_registry(validated_server);
257
+ note(`Server "${validated_server.name}" added to registry successfully!`);
258
+ }
259
+ catch (error) {
260
+ throw new Error(`Failed to parse or validate JSON: ${error instanceof Error ? error.message : 'Unknown error'}`);
261
+ }
262
+ }
91
263
  //# sourceMappingURL=add-server.js.map
@@ -5,7 +5,7 @@ export async function launch_claude_code() {
5
5
  try {
6
6
  s.start('Launching Claude Code...');
7
7
  const claude_process = spawn('claude', ['code'], {
8
- stdio: 'inherit',
8
+ stdio: 'ignore',
9
9
  detached: true,
10
10
  });
11
11
  claude_process.unref();
@@ -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
@@ -40,7 +40,7 @@ async function main() {
40
40
  {
41
41
  value: 'exit',
42
42
  label: 'Exit',
43
- hint: 'Quit MCPick',
43
+ hint: 'Quit MCPick (Esc)',
44
44
  },
45
45
  ],
46
46
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcpick",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
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",
package/mcpick-plan.md DELETED
@@ -1,223 +0,0 @@
1
- # MCPick Implementation Plan
2
-
3
- ## Overview
4
-
5
- Build a minimal Node.js CLI tool using TypeScript and Clack prompts to
6
- dynamically manage MCP server configurations for Claude Code sessions.
7
-
8
- ## The Problem
9
-
10
- Claude Code loads ALL configured MCP servers at session startup,
11
- consuming massive amounts of context tokens (66,687+ tokens reported)
12
- regardless of whether you actually use those tools. Users need a way
13
- to dynamically select which MCP servers to load per session.
14
-
15
- ## Architecture - Minimal Dependencies
16
-
17
- **Only 2 Dependencies:**
18
-
19
- ```json
20
- {
21
- "@clack/prompts": "^0.7.0",
22
- "valibot": "^0.25.0"
23
- }
24
- ```
25
-
26
- **Use Node.js Built-ins:**
27
-
28
- - `fs/promises` for all file operations
29
- - `path`, `os.homedir()` for path handling
30
- - `child_process.spawn` to launch Claude Code
31
- - `JSON.parse/stringify` for config files
32
-
33
- ## TypeScript Configuration
34
-
35
- **tsconfig.json:**
36
-
37
- ```json
38
- {
39
- "compilerOptions": {
40
- "target": "ES2022",
41
- "module": "ESNext",
42
- "moduleResolution": "node16",
43
- "strict": true,
44
- "outDir": "./dist",
45
- "sourceMap": true,
46
- "esModuleInterop": true,
47
- "allowSyntheticDefaultImports": true,
48
- "skipLibCheck": true
49
- },
50
- "include": ["src/**/*"],
51
- "exclude": ["node_modules", "dist"]
52
- }
53
- ```
54
-
55
- **package.json additions:**
56
-
57
- - `"type": "module"` for ESM
58
- - `"bin": { "mcpick": "./dist/index.js" }`
59
- - `"engines": { "node": ">=22.0.0" }`
60
-
61
- ## User Flow - Pure Interactive
62
-
63
- ```bash
64
- mcpick # Single entry point - no CLI arguments
65
- ```
66
-
67
- **Main Menu (Clack select):**
68
-
69
- ```
70
- ┌ What would you like to do?
71
- │ ○ Edit config
72
- │ ○ Backup config
73
- │ ○ Add MCP server
74
- │ ○ Restore from backup
75
- │ ○ Launch Claude Code
76
- └ ○ Exit
77
- ```
78
-
79
- ### Edit Config Flow:
80
-
81
- 1. Read `.claude.json` from current directory
82
- 2. Show multiselect with all servers (currently enabled ones checked)
83
- 3. User toggles servers on/off with spacebar
84
- 4. Save deselected servers to `~/.claude/mcpick/servers.json` registry
85
- 5. Update `.claude.json` with only selected servers
86
- 6. Show token count reduction
87
-
88
- ### Backup Config Flow:
89
-
90
- 1. Create timestamped backup of current `.claude.json`
91
- 2. Store in `~/.claude/mcpick/backups/`
92
- 3. Confirm backup location to user
93
-
94
- ### Add MCP Server Flow:
95
-
96
- 1. Text prompts for server details:
97
- - Name
98
- - Command
99
- - Arguments (array)
100
- - Description (optional)
101
- 2. Validate configuration with Valibot
102
- 3. Add to both `.claude.json` and servers registry
103
-
104
- ### Restore Flow:
105
-
106
- 1. List available backups with timestamps
107
- 2. User selects backup to restore
108
- 3. Confirm destructive operation
109
- 4. Restore `.claude.json` from backup
110
-
111
- ## File Structure
112
-
113
- ```
114
- mcpick/
115
- ├── src/
116
- │ ├── commands/
117
- │ │ ├── edit-config.ts # Toggle servers on/off
118
- │ │ ├── backup.ts # Create config backups
119
- │ │ ├── add-server.ts # Add new MCP server
120
- │ │ ├── restore.ts # Restore from backup
121
- │ │ └── launch.ts # Launch Claude Code
122
- │ ├── core/
123
- │ │ ├── config.ts # .claude.json read/write operations
124
- │ │ ├── registry.ts # servers.json management
125
- │ │ └── validation.ts # Valibot schemas
126
- │ ├── utils/
127
- │ │ └── paths.ts # Path resolution utilities
128
- │ ├── types.ts # TypeScript type definitions
129
- │ └── index.ts # Main entry point with menu
130
- ├── package.json
131
- ├── tsconfig.json
132
- └── README.md
133
- ```
134
-
135
- ## Configuration Storage
136
-
137
- **Current directory: `.claude.json`**
138
-
139
- - Standard Claude Code configuration file
140
- - Only contains currently selected MCP servers
141
-
142
- **~/.claude/mcpick/servers.json** - Registry of all available servers:
143
-
144
- ```json
145
- {
146
- "servers": [
147
- {
148
- "name": "mcp-sqlite-tools",
149
- "command": "uvx",
150
- "args": ["mcp-sqlite-tools"],
151
- "description": "SQLite database tools",
152
- "estimatedTokens": 9872
153
- },
154
- {
155
- "name": "mcp-omnisearch",
156
- "command": "npx",
157
- "args": ["-y", "@modelcontextprotocol/server-omnisearch"],
158
- "description": "Web search capabilities",
159
- "estimatedTokens": 10454
160
- }
161
- ]
162
- }
163
- ```
164
-
165
- **~/.claude/mcpick/backups/** - Timestamped backup files:
166
-
167
- - Format: `claude-YYYY-MM-DD-HHMMSS.json`
168
- - Keep last 10 backups automatically
169
-
170
- ## Error Handling Strategy
171
-
172
- - **Valibot schemas** for all configuration validation
173
- - **Try/catch blocks** with Clack `cancel()` for user-friendly error
174
- messages
175
- - **File permission checks** before attempting operations
176
- - **Create missing directories/files** with sensible defaults
177
- - **Graceful handling** of malformed JSON files
178
-
179
- ## Token Estimation
180
-
181
- - Static analysis of MCP server tool definitions
182
- - Cache estimates in servers.json to avoid repeated calculations
183
- - Display real-time totals during server selection
184
- - Show before/after token counts
185
-
186
- ## Implementation Steps
187
-
188
- 1. **Project Setup**
189
- - Initialize Node.js/TypeScript project with minimal dependencies
190
- - Configure ESM, TypeScript, and build scripts
191
-
192
- 2. **Core Infrastructure**
193
- - Implement config file I/O using only Node.js built-ins
194
- - Create server registry management
195
- - Build backup/restore functionality
196
-
197
- 3. **Clack Interface**
198
- - Main interactive menu system
199
- - Multiselect for server toggling
200
- - Text prompts for server addition
201
-
202
- 4. **Config Management**
203
- - Edit existing configurations
204
- - Add new MCP servers
205
- - Backup and restore operations
206
-
207
- 5. **Polish & Testing**
208
- - Error handling and validation
209
- - User experience improvements
210
- - Documentation
211
-
212
- ## Benefits
213
-
214
- - **95% token reduction** by loading only needed tools
215
- - **Zero session restarts** for MCP configuration changes
216
- - **Intuitive interface** with beautiful Clack prompts
217
- - **Minimal dependencies** - only 2 external packages
218
- - **Fast startup** with lightweight codebase
219
- - **Backup safety** for configuration changes
220
-
221
- This approach transforms MCP configuration from a static, manual
222
- process into a dynamic, user-friendly workflow that maximizes both
223
- functionality and efficiency.
package/plan.md DELETED
@@ -1,102 +0,0 @@
1
- # MCP Session Manager: Dynamic Tool Configuration for Claude Code
2
-
3
- ## The Problem
4
-
5
- Claude Code loads ALL configured MCP servers at session startup,
6
- consuming massive amounts of context tokens regardless of whether you
7
- actually use those tools. Users report:
8
-
9
- - **66,687 tokens consumed** by 20+ MCP tools before even starting
10
- work
11
- - **25-30% of context window** used by unused tool definitions
12
- - **No way to dynamically enable/disable** MCP servers during sessions
13
- - **Session restart required** for any MCP configuration changes
14
- - **Forced to choose** between comprehensive tool access and efficient
15
- resource usage
16
-
17
- This creates a fundamental workflow problem: you either load
18
- everything (wasting tokens) or manually edit JSON configs before each
19
- session (time-consuming and error-prone).
20
-
21
- ## The Solution: Session-Specific MCP Configuration Manager
22
-
23
- A CLI tool that manages your MCP server configurations dynamically by
24
- manipulating the `.claude.json` file before Claude Code sessions
25
- start.
26
-
27
- ### Core Concept
28
-
29
- Instead of fighting Claude Code's static loading, work with it by
30
- making configuration changes fast and intelligent:
31
-
32
- 1. **Store all available MCP servers** in a separate configuration
33
- repository
34
- 2. **Select servers per session** using an interactive interface
35
- 3. **Automatically update** `.claude.json` with only selected servers
36
- 4. **Launch Claude Code** with optimized configuration
37
- 5. **Save successful combinations** as reusable presets
38
-
39
- ### Key Features
40
-
41
- **Interactive Server Selection**
42
-
43
- - Checkbox interface showing all available MCP servers
44
- - Real-time token usage estimates for each server
45
- - Smart warnings when approaching context limits
46
- - Tag-based filtering (e.g., "web-dev", "data-analysis", "automation")
47
-
48
- **Preset Management**
49
-
50
- - Save frequently used server combinations
51
- - Load preset configurations instantly
52
- - Project-specific presets (auto-detect based on directory)
53
- - Share presets with team members
54
-
55
- **Intelligent Recommendations**
56
-
57
- - Suggest optimal server combinations for detected project types
58
- - Learn from usage patterns to recommend relevant tools
59
- - Context-aware suggestions based on file types in current directory
60
-
61
- **Seamless Integration**
62
-
63
- - One command to select servers and launch Claude Code
64
- - Backup and restore previous configurations
65
- - Zero impact on existing Claude Code functionality
66
-
67
- ### User Experience
68
-
69
- ```bash
70
- # Interactive selection for this session
71
- mcp-manager select
72
-
73
- # Quick preset loading
74
- mcp-manager start --preset "web-development"
75
-
76
- # Enable specific servers by name
77
- mcp-manager enable context7 github filesystem
78
-
79
- # Save current successful combination
80
- mcp-manager save-preset "data-pipeline" --current
81
- ```
82
-
83
- ### Benefits
84
-
85
- - **95% token reduction** by loading only needed tools
86
- - **No session restarts** for configuration changes
87
- - **Faster startup times** with fewer servers to initialize
88
- - **Better resource utilization** and longer conversation capacity
89
- - **Experimentation-friendly** - try new tool combinations easily
90
- - **Team collaboration** through shared preset configurations
91
-
92
- ### Storage Location
93
-
94
- Extends existing Claude configuration structure:
95
-
96
- - Available servers stored in `~/.claude/mcp-manager/`
97
- - Presets and settings in `~/.claude/settings.json`
98
- - No modification to Claude Code's core configuration patterns
99
-
100
- This solution transforms the MCP configuration experience from a
101
- static, all-or-nothing choice into a dynamic, session-optimized
102
- workflow that maximizes both functionality and efficiency.