mcpick 0.0.10 → 0.0.12
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/CHANGELOG.md +17 -0
- package/README.md +114 -4
- package/dist/cli/commands/backup.js +50 -22
- package/dist/cli/commands/plugins.js +110 -1
- package/dist/cli/commands/profile.js +31 -9
- package/dist/cli/commands/remove.js +12 -8
- package/dist/cli/commands/restore.js +75 -26
- package/dist/commands/backup.js +28 -22
- package/dist/commands/edit-plugins.js +192 -47
- package/dist/commands/restore.js +85 -34
- package/dist/core/config.js +6 -15
- package/dist/core/plugin-cache.js +3 -2
- package/dist/core/profile.js +32 -8
- package/dist/core/registry.js +44 -30
- package/dist/core/settings.js +8 -15
- package/dist/index.js +48 -19
- package/dist/utils/atomic-write.js +27 -0
- package/dist/utils/claude-cli.js +69 -0
- package/dist/utils/paths.js +8 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# mcpick
|
|
2
2
|
|
|
3
|
+
## 0.0.12
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b4f38d5: fix: discover externally-added servers and use atomic JSON
|
|
8
|
+
writes
|
|
9
|
+
|
|
10
|
+
## 0.0.11
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 7ec3323: feat: add plugin install, uninstall, and update commands
|
|
15
|
+
via Claude CLI
|
|
16
|
+
- 99b2bf3: feat: add plugin install, uninstall, and update to
|
|
17
|
+
interactive menu
|
|
18
|
+
- 9b1c6d7: feat: add plugin support to backup, restore, and profiles
|
|
19
|
+
|
|
3
20
|
## 0.0.10
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# McPick
|
|
2
2
|
|
|
3
|
-
A CLI tool for
|
|
4
|
-
Claude Code.
|
|
5
|
-
context usage and performance.
|
|
3
|
+
A CLI tool for managing MCP servers, plugins, and plugin caches in
|
|
4
|
+
Claude Code. Toggle servers and plugins on/off, manage stale plugin
|
|
5
|
+
caches, and optimise context usage and performance.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -51,10 +51,15 @@ This can lead to:
|
|
|
51
51
|
|
|
52
52
|
## The Solution
|
|
53
53
|
|
|
54
|
-
McPick provides an intuitive CLI menu
|
|
54
|
+
McPick provides an intuitive CLI menu and non-interactive subcommands
|
|
55
|
+
to:
|
|
55
56
|
|
|
56
57
|
- ✅ **Toggle servers on/off** - Enable only the MCP servers you need
|
|
57
58
|
for your current task
|
|
59
|
+
- 🔌 **Toggle plugins on/off** - Enable or disable Claude Code
|
|
60
|
+
marketplace plugins
|
|
61
|
+
- 🗑️ **Manage plugin cache** - Detect stale plugins, clear caches,
|
|
62
|
+
clean orphaned versions, refresh marketplaces
|
|
58
63
|
- 📁 **Manage server registry** - Keep a database of all your
|
|
59
64
|
available MCP servers
|
|
60
65
|
- 🔄 **Safe configuration** - Only modifies the `mcpServers` section,
|
|
@@ -71,6 +76,8 @@ McPick provides an intuitive CLI menu to:
|
|
|
71
76
|
│
|
|
72
77
|
◆ What would you like to do?
|
|
73
78
|
│ ● Enable / Disable MCP servers (Toggle MCP servers on/off)
|
|
79
|
+
│ ○ Enable / Disable plugins (Toggle Claude Code plugins on/off)
|
|
80
|
+
│ ○ Manage plugin cache (View, clear, or refresh plugin caches)
|
|
74
81
|
│ ○ Backup config
|
|
75
82
|
│ ○ Add MCP server
|
|
76
83
|
│ ○ Restore from backup
|
|
@@ -162,6 +169,104 @@ Or use full format with `mcpServers` wrapper:
|
|
|
162
169
|
}
|
|
163
170
|
```
|
|
164
171
|
|
|
172
|
+
### Plugin Cache Management
|
|
173
|
+
|
|
174
|
+
Claude Code caches marketplace plugins at `~/.claude/plugins/cache/`.
|
|
175
|
+
When marketplace authors update plugins, your cached versions can go
|
|
176
|
+
stale. McPick detects this and lets you fix it.
|
|
177
|
+
|
|
178
|
+
#### Interactive
|
|
179
|
+
|
|
180
|
+
Select "Manage plugin cache" from the main menu to:
|
|
181
|
+
|
|
182
|
+
- **View cache status** - See all cached plugins with staleness
|
|
183
|
+
indicators (version mismatch, commits behind, orphaned versions)
|
|
184
|
+
- **Clear plugin caches** - Refreshes the marketplace and clears
|
|
185
|
+
selected caches so they rebuild with the latest version
|
|
186
|
+
- **Clean orphaned versions** - Remove old version directories marked
|
|
187
|
+
as orphaned
|
|
188
|
+
- **Refresh marketplaces** - Git pull all marketplace clones to get
|
|
189
|
+
latest plugin listings
|
|
190
|
+
|
|
191
|
+
#### CLI Subcommands
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Show cache status for all plugins
|
|
195
|
+
npx mcpick cache status
|
|
196
|
+
npx mcpick cache status --json
|
|
197
|
+
|
|
198
|
+
# Clear a specific plugin cache (refreshes marketplace first)
|
|
199
|
+
npx mcpick cache clear plugin-name@marketplace
|
|
200
|
+
npx mcpick cache clear --all
|
|
201
|
+
|
|
202
|
+
# Remove orphaned version directories
|
|
203
|
+
npx mcpick cache clean-orphaned
|
|
204
|
+
|
|
205
|
+
# Refresh all marketplace clones
|
|
206
|
+
npx mcpick cache refresh
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Plugin Management
|
|
210
|
+
|
|
211
|
+
Install, update, and toggle Claude Code marketplace plugins:
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# List all plugins and their status
|
|
215
|
+
npx mcpick plugins list
|
|
216
|
+
npx mcpick plugins list --json
|
|
217
|
+
|
|
218
|
+
# Install/uninstall a plugin (wraps claude plugin CLI)
|
|
219
|
+
npx mcpick plugins install plugin-name@marketplace
|
|
220
|
+
npx mcpick plugins uninstall plugin-name@marketplace
|
|
221
|
+
|
|
222
|
+
# Update a plugin to latest version
|
|
223
|
+
npx mcpick plugins update plugin-name@marketplace
|
|
224
|
+
|
|
225
|
+
# Enable/disable a plugin
|
|
226
|
+
npx mcpick plugins enable plugin-name@marketplace
|
|
227
|
+
npx mcpick plugins disable plugin-name@marketplace
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
All plugin commands support `--scope` (user, project, local) and
|
|
231
|
+
`--json` flags.
|
|
232
|
+
|
|
233
|
+
### CLI Subcommands
|
|
234
|
+
|
|
235
|
+
McPick supports both an interactive menu (default) and non-interactive
|
|
236
|
+
CLI subcommands for scripting and LLM tool use:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# MCP server management
|
|
240
|
+
npx mcpick list # List servers
|
|
241
|
+
npx mcpick enable <server> # Enable a server
|
|
242
|
+
npx mcpick disable <server> # Disable a server
|
|
243
|
+
npx mcpick add --name <n> ... # Add a server
|
|
244
|
+
npx mcpick remove <server> # Remove a server
|
|
245
|
+
|
|
246
|
+
# Backups and profiles
|
|
247
|
+
npx mcpick backup # Create backup
|
|
248
|
+
npx mcpick restore [file] # Restore from backup
|
|
249
|
+
npx mcpick profile list # List profiles
|
|
250
|
+
npx mcpick profile load <name> # Load a profile
|
|
251
|
+
npx mcpick profile save <name> # Save current config
|
|
252
|
+
|
|
253
|
+
# Plugin management
|
|
254
|
+
npx mcpick plugins list # List plugins
|
|
255
|
+
npx mcpick plugins enable <key> # Enable plugin
|
|
256
|
+
npx mcpick plugins disable <key> # Disable plugin
|
|
257
|
+
npx mcpick plugins install <key> # Install from marketplace
|
|
258
|
+
npx mcpick plugins uninstall <key> # Remove plugin
|
|
259
|
+
npx mcpick plugins update <key> # Update to latest version
|
|
260
|
+
|
|
261
|
+
# Cache management
|
|
262
|
+
npx mcpick cache status # Show staleness info
|
|
263
|
+
npx mcpick cache clear [key] # Clear plugin cache
|
|
264
|
+
npx mcpick cache clean-orphaned # Remove orphaned dirs
|
|
265
|
+
npx mcpick cache refresh # Git pull marketplaces
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
All subcommands support `--json` for machine-readable output.
|
|
269
|
+
|
|
165
270
|
### Typical Workflow
|
|
166
271
|
|
|
167
272
|
1. **Before a coding session**: Run `mcpick -p <profile>` or use the
|
|
@@ -209,6 +314,11 @@ MCPick works with the standard Claude Code configuration format:
|
|
|
209
314
|
server database)
|
|
210
315
|
- **Backups**: `~/.claude/mcpick/backups/` (MCP configuration backups)
|
|
211
316
|
- **Profiles**: `~/.claude/mcpick/profiles/` (predefined server sets)
|
|
317
|
+
- **Plugin Cache**: `~/.claude/plugins/cache/` (cached plugin files)
|
|
318
|
+
- **Installed Plugins**: `~/.claude/plugins/installed_plugins.json`
|
|
319
|
+
(plugin install registry)
|
|
320
|
+
- **Marketplaces**: `~/.claude/plugins/marketplaces/` (marketplace git
|
|
321
|
+
clones)
|
|
212
322
|
|
|
213
323
|
#### MCP Server Storage by Scope
|
|
214
324
|
|
|
@@ -2,13 +2,32 @@ import { defineCommand } from 'citty';
|
|
|
2
2
|
import { readdir, unlink, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { read_claude_config } from '../../core/config.js';
|
|
5
|
-
import {
|
|
5
|
+
import { read_claude_settings } from '../../core/settings.js';
|
|
6
|
+
import { ensure_directory_exists, get_backup_filename, get_backups_dir, get_plugin_backup_filename, } from '../../utils/paths.js';
|
|
6
7
|
import { output } from '../output.js';
|
|
7
8
|
const MAX_BACKUPS = 10;
|
|
9
|
+
async function cleanup_old_backups(prefix) {
|
|
10
|
+
try {
|
|
11
|
+
const backups_dir = get_backups_dir();
|
|
12
|
+
const files = await readdir(backups_dir);
|
|
13
|
+
const backup_files = files
|
|
14
|
+
.filter((f) => f.startsWith(prefix) && f.endsWith('.json'))
|
|
15
|
+
.sort()
|
|
16
|
+
.reverse();
|
|
17
|
+
if (backup_files.length > MAX_BACKUPS) {
|
|
18
|
+
for (const file of backup_files.slice(MAX_BACKUPS)) {
|
|
19
|
+
await unlink(join(backups_dir, file));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Cleanup is best-effort
|
|
25
|
+
}
|
|
26
|
+
}
|
|
8
27
|
export default defineCommand({
|
|
9
28
|
meta: {
|
|
10
29
|
name: 'backup',
|
|
11
|
-
description: 'Create a timestamped backup of MCP
|
|
30
|
+
description: 'Create a timestamped backup of MCP servers and plugins',
|
|
12
31
|
},
|
|
13
32
|
args: {
|
|
14
33
|
json: {
|
|
@@ -19,36 +38,45 @@ export default defineCommand({
|
|
|
19
38
|
},
|
|
20
39
|
async run({ args }) {
|
|
21
40
|
const current_config = await read_claude_config();
|
|
41
|
+
const current_settings = await read_claude_settings();
|
|
22
42
|
const backups_dir = get_backups_dir();
|
|
23
43
|
await ensure_directory_exists(backups_dir);
|
|
24
|
-
|
|
25
|
-
const
|
|
44
|
+
// Backup MCP servers
|
|
45
|
+
const mcp_filename = get_backup_filename();
|
|
46
|
+
const mcp_path = join(backups_dir, mcp_filename);
|
|
26
47
|
const mcp_backup = {
|
|
27
48
|
mcpServers: current_config.mcpServers || {},
|
|
28
49
|
};
|
|
29
|
-
await writeFile(
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
await unlink(join(backups_dir, file));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
catch {
|
|
44
|
-
// Cleanup is best-effort
|
|
50
|
+
await writeFile(mcp_path, JSON.stringify(mcp_backup, null, 2), 'utf-8');
|
|
51
|
+
// Backup plugins
|
|
52
|
+
const plugins = current_settings.enabledPlugins || {};
|
|
53
|
+
const plugin_count = Object.keys(plugins).length;
|
|
54
|
+
let plugin_path = null;
|
|
55
|
+
if (plugin_count > 0) {
|
|
56
|
+
const plugin_filename = get_plugin_backup_filename();
|
|
57
|
+
plugin_path = join(backups_dir, plugin_filename);
|
|
58
|
+
const plugin_backup = { enabledPlugins: plugins };
|
|
59
|
+
await writeFile(plugin_path, JSON.stringify(plugin_backup, null, 2), 'utf-8');
|
|
45
60
|
}
|
|
61
|
+
await cleanup_old_backups('mcp-servers-');
|
|
62
|
+
await cleanup_old_backups('plugins-');
|
|
46
63
|
const server_count = Object.keys(current_config.mcpServers || {}).length;
|
|
47
64
|
if (args.json) {
|
|
48
|
-
output({
|
|
65
|
+
output({
|
|
66
|
+
mcp: { path: mcp_path, servers: server_count },
|
|
67
|
+
plugins: plugin_path
|
|
68
|
+
? {
|
|
69
|
+
path: plugin_path,
|
|
70
|
+
plugins: plugin_count,
|
|
71
|
+
}
|
|
72
|
+
: null,
|
|
73
|
+
}, true);
|
|
49
74
|
}
|
|
50
75
|
else {
|
|
51
|
-
console.log(`Backup created: ${
|
|
76
|
+
console.log(`Backup created: ${mcp_path} (${server_count} servers)`);
|
|
77
|
+
if (plugin_path) {
|
|
78
|
+
console.log(`Plugin backup: ${plugin_path} (${plugin_count} plugins)`);
|
|
79
|
+
}
|
|
52
80
|
}
|
|
53
81
|
},
|
|
54
82
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { build_enabled_plugins, get_all_plugins, read_claude_settings, write_claude_settings, } from '../../core/settings.js';
|
|
3
|
+
import { install_plugin_via_cli, uninstall_plugin_via_cli, update_plugin_via_cli, } from '../../utils/claude-cli.js';
|
|
3
4
|
import { error, output } from '../output.js';
|
|
4
5
|
const list = defineCommand({
|
|
5
6
|
meta: {
|
|
@@ -83,11 +84,119 @@ const disable = defineCommand({
|
|
|
83
84
|
console.log(`Disabled plugin '${key}'`);
|
|
84
85
|
},
|
|
85
86
|
});
|
|
87
|
+
const install = defineCommand({
|
|
88
|
+
meta: {
|
|
89
|
+
name: 'install',
|
|
90
|
+
description: 'Install a plugin from a marketplace',
|
|
91
|
+
},
|
|
92
|
+
args: {
|
|
93
|
+
plugin: {
|
|
94
|
+
type: 'positional',
|
|
95
|
+
description: 'Plugin key (name@marketplace)',
|
|
96
|
+
required: true,
|
|
97
|
+
},
|
|
98
|
+
scope: {
|
|
99
|
+
type: 'string',
|
|
100
|
+
description: 'Installation scope: user, project, or local',
|
|
101
|
+
default: 'user',
|
|
102
|
+
},
|
|
103
|
+
json: {
|
|
104
|
+
type: 'boolean',
|
|
105
|
+
description: 'Output as JSON',
|
|
106
|
+
default: false,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
async run({ args }) {
|
|
110
|
+
const scope = args.scope;
|
|
111
|
+
const result = await install_plugin_via_cli(args.plugin, scope);
|
|
112
|
+
if (args.json) {
|
|
113
|
+
output(result, true);
|
|
114
|
+
}
|
|
115
|
+
else if (result.success) {
|
|
116
|
+
console.log(`Installed plugin '${args.plugin}' (scope: ${scope})`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
error(result.error ?? 'Unknown error');
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
const uninstall = defineCommand({
|
|
124
|
+
meta: {
|
|
125
|
+
name: 'uninstall',
|
|
126
|
+
description: 'Uninstall a plugin',
|
|
127
|
+
},
|
|
128
|
+
args: {
|
|
129
|
+
plugin: {
|
|
130
|
+
type: 'positional',
|
|
131
|
+
description: 'Plugin key (name@marketplace)',
|
|
132
|
+
required: true,
|
|
133
|
+
},
|
|
134
|
+
scope: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
description: 'Uninstall from scope: user, project, or local',
|
|
137
|
+
default: 'user',
|
|
138
|
+
},
|
|
139
|
+
json: {
|
|
140
|
+
type: 'boolean',
|
|
141
|
+
description: 'Output as JSON',
|
|
142
|
+
default: false,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
async run({ args }) {
|
|
146
|
+
const scope = args.scope;
|
|
147
|
+
const result = await uninstall_plugin_via_cli(args.plugin, scope);
|
|
148
|
+
if (args.json) {
|
|
149
|
+
output(result, true);
|
|
150
|
+
}
|
|
151
|
+
else if (result.success) {
|
|
152
|
+
console.log(`Uninstalled plugin '${args.plugin}' (scope: ${scope})`);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
error(result.error ?? 'Unknown error');
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
const update = defineCommand({
|
|
160
|
+
meta: {
|
|
161
|
+
name: 'update',
|
|
162
|
+
description: 'Update a plugin to latest version',
|
|
163
|
+
},
|
|
164
|
+
args: {
|
|
165
|
+
plugin: {
|
|
166
|
+
type: 'positional',
|
|
167
|
+
description: 'Plugin key (name@marketplace)',
|
|
168
|
+
required: true,
|
|
169
|
+
},
|
|
170
|
+
scope: {
|
|
171
|
+
type: 'string',
|
|
172
|
+
description: 'Scope to update: user, project, or local',
|
|
173
|
+
default: 'user',
|
|
174
|
+
},
|
|
175
|
+
json: {
|
|
176
|
+
type: 'boolean',
|
|
177
|
+
description: 'Output as JSON',
|
|
178
|
+
default: false,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
async run({ args }) {
|
|
182
|
+
const scope = args.scope;
|
|
183
|
+
const result = await update_plugin_via_cli(args.plugin, scope);
|
|
184
|
+
if (args.json) {
|
|
185
|
+
output(result, true);
|
|
186
|
+
}
|
|
187
|
+
else if (result.success) {
|
|
188
|
+
console.log(`Updated plugin '${args.plugin}' (scope: ${scope})`);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
error(result.error ?? 'Unknown error');
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
});
|
|
86
195
|
export default defineCommand({
|
|
87
196
|
meta: {
|
|
88
197
|
name: 'plugins',
|
|
89
198
|
description: 'Manage Claude Code plugins',
|
|
90
199
|
},
|
|
91
|
-
subCommands: { list, enable, disable },
|
|
200
|
+
subCommands: { list, enable, disable, install, uninstall, update },
|
|
92
201
|
});
|
|
93
202
|
//# sourceMappingURL=plugins.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { write_claude_config } from '../../core/config.js';
|
|
3
3
|
import { list_profiles, load_profile, save_profile, } from '../../core/profile.js';
|
|
4
|
+
import { write_claude_settings } from '../../core/settings.js';
|
|
4
5
|
import { error, output } from '../output.js';
|
|
5
6
|
const list = defineCommand({
|
|
6
7
|
meta: {
|
|
@@ -25,7 +26,11 @@ const list = defineCommand({
|
|
|
25
26
|
return;
|
|
26
27
|
}
|
|
27
28
|
for (const p of profiles) {
|
|
28
|
-
|
|
29
|
+
const parts = [`${p.serverCount} servers`];
|
|
30
|
+
if (p.pluginCount > 0) {
|
|
31
|
+
parts.push(`${p.pluginCount} plugins`);
|
|
32
|
+
}
|
|
33
|
+
console.log(`${p.name} (${parts.join(', ')})`);
|
|
29
34
|
}
|
|
30
35
|
}
|
|
31
36
|
},
|
|
@@ -49,17 +54,29 @@ const load = defineCommand({
|
|
|
49
54
|
},
|
|
50
55
|
async run({ args }) {
|
|
51
56
|
try {
|
|
52
|
-
const
|
|
53
|
-
await write_claude_config(config);
|
|
54
|
-
const server_count = Object.keys(config.mcpServers || {}).length;
|
|
57
|
+
const profile = await load_profile(args.name);
|
|
58
|
+
await write_claude_config(profile.config);
|
|
59
|
+
const server_count = Object.keys(profile.config.mcpServers || {}).length;
|
|
60
|
+
let plugin_count = 0;
|
|
61
|
+
if (profile.enabledPlugins) {
|
|
62
|
+
await write_claude_settings({
|
|
63
|
+
enabledPlugins: profile.enabledPlugins,
|
|
64
|
+
});
|
|
65
|
+
plugin_count = Object.keys(profile.enabledPlugins).length;
|
|
66
|
+
}
|
|
55
67
|
if (args.json) {
|
|
56
68
|
output({
|
|
57
69
|
profile: args.name,
|
|
58
70
|
servers: server_count,
|
|
71
|
+
plugins: plugin_count,
|
|
59
72
|
}, true);
|
|
60
73
|
}
|
|
61
74
|
else {
|
|
62
|
-
|
|
75
|
+
const parts = [`${server_count} servers`];
|
|
76
|
+
if (plugin_count > 0) {
|
|
77
|
+
parts.push(`${plugin_count} plugins`);
|
|
78
|
+
}
|
|
79
|
+
console.log(`Profile '${args.name}' applied (${parts.join(', ')})`);
|
|
63
80
|
}
|
|
64
81
|
}
|
|
65
82
|
catch (err) {
|
|
@@ -86,15 +103,20 @@ const save = defineCommand({
|
|
|
86
103
|
},
|
|
87
104
|
async run({ args }) {
|
|
88
105
|
try {
|
|
89
|
-
const
|
|
106
|
+
const counts = await save_profile(args.name);
|
|
90
107
|
if (args.json) {
|
|
91
108
|
output({
|
|
92
109
|
profile: args.name,
|
|
93
|
-
servers:
|
|
110
|
+
servers: counts.serverCount,
|
|
111
|
+
plugins: counts.pluginCount,
|
|
94
112
|
}, true);
|
|
95
113
|
}
|
|
96
114
|
else {
|
|
97
|
-
|
|
115
|
+
const parts = [`${counts.serverCount} servers`];
|
|
116
|
+
if (counts.pluginCount > 0) {
|
|
117
|
+
parts.push(`${counts.pluginCount} plugins`);
|
|
118
|
+
}
|
|
119
|
+
console.log(`Profile '${args.name}' saved (${parts.join(', ')})`);
|
|
98
120
|
}
|
|
99
121
|
}
|
|
100
122
|
catch (err) {
|
|
@@ -105,7 +127,7 @@ const save = defineCommand({
|
|
|
105
127
|
export default defineCommand({
|
|
106
128
|
meta: {
|
|
107
129
|
name: 'profile',
|
|
108
|
-
description: 'Manage MCP
|
|
130
|
+
description: 'Manage profiles (MCP servers + plugins)',
|
|
109
131
|
},
|
|
110
132
|
subCommands: { list, load, save },
|
|
111
133
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
|
-
import { read_server_registry, write_server_registry, } from '../../core/registry.js';
|
|
2
|
+
import { get_all_available_servers, read_server_registry, write_server_registry, } from '../../core/registry.js';
|
|
3
3
|
import { remove_mcp_via_cli } from '../../utils/claude-cli.js';
|
|
4
4
|
import { error } from '../output.js';
|
|
5
5
|
export default defineCommand({
|
|
@@ -15,17 +15,21 @@ export default defineCommand({
|
|
|
15
15
|
},
|
|
16
16
|
},
|
|
17
17
|
async run({ args }) {
|
|
18
|
+
const all_servers = await get_all_available_servers();
|
|
19
|
+
const found = all_servers.find((s) => s.name === args.server);
|
|
20
|
+
if (!found) {
|
|
21
|
+
error(`Server '${args.server}' not found. Run 'mcpick list' to see available servers.`);
|
|
22
|
+
}
|
|
23
|
+
// Remove from registry if present
|
|
18
24
|
const registry = await read_server_registry();
|
|
19
25
|
const index = registry.servers.findIndex((s) => s.name === args.server);
|
|
20
|
-
if (index
|
|
21
|
-
|
|
26
|
+
if (index >= 0) {
|
|
27
|
+
registry.servers.splice(index, 1);
|
|
28
|
+
await write_server_registry(registry);
|
|
22
29
|
}
|
|
23
|
-
// Remove
|
|
24
|
-
registry.servers.splice(index, 1);
|
|
25
|
-
await write_server_registry(registry);
|
|
26
|
-
// Also disable via CLI (best effort)
|
|
30
|
+
// Remove via CLI
|
|
27
31
|
await remove_mcp_via_cli(args.server);
|
|
28
|
-
console.log(`Removed '${args.server}'
|
|
32
|
+
console.log(`Removed '${args.server}'`);
|
|
29
33
|
},
|
|
30
34
|
});
|
|
31
35
|
//# sourceMappingURL=remove.js.map
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { defineCommand } from 'citty';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { write_claude_config } from '../../core/config.js';
|
|
4
|
-
import { list_backups } from '../../core/registry.js';
|
|
4
|
+
import { list_backups, list_plugin_backups, } from '../../core/registry.js';
|
|
5
|
+
import { write_claude_settings } from '../../core/settings.js';
|
|
5
6
|
import { validate_claude_config } from '../../core/validation.js';
|
|
6
7
|
import { error, output } from '../output.js';
|
|
7
8
|
export default defineCommand({
|
|
8
9
|
meta: {
|
|
9
10
|
name: 'restore',
|
|
10
|
-
description: 'Restore
|
|
11
|
+
description: 'Restore config from a backup (latest if no file specified)',
|
|
11
12
|
},
|
|
12
13
|
args: {
|
|
13
14
|
file: {
|
|
@@ -15,6 +16,11 @@ export default defineCommand({
|
|
|
15
16
|
description: 'Backup filename or path (optional, defaults to latest)',
|
|
16
17
|
required: false,
|
|
17
18
|
},
|
|
19
|
+
type: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'What to restore: mcp (default), plugins, or all',
|
|
22
|
+
default: 'mcp',
|
|
23
|
+
},
|
|
18
24
|
json: {
|
|
19
25
|
type: 'boolean',
|
|
20
26
|
description: 'Output as JSON',
|
|
@@ -22,34 +28,77 @@ export default defineCommand({
|
|
|
22
28
|
},
|
|
23
29
|
},
|
|
24
30
|
async run({ args }) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const restore_type = args.type;
|
|
32
|
+
const results = {};
|
|
33
|
+
if (restore_type === 'mcp' || restore_type === 'all') {
|
|
34
|
+
const backups = await list_backups();
|
|
35
|
+
if (backups.length === 0) {
|
|
36
|
+
if (restore_type === 'mcp') {
|
|
37
|
+
error('No MCP backups found. Run "mcpick backup" first.');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
let backup_path;
|
|
42
|
+
if (args.file && restore_type === 'mcp') {
|
|
43
|
+
const found = backups.find((b) => b.filename === args.file || b.path === args.file);
|
|
44
|
+
if (!found) {
|
|
45
|
+
error(`Backup '${args.file}' not found. Available: ${backups.map((b) => b.filename).join(', ')}`);
|
|
46
|
+
}
|
|
47
|
+
backup_path = found.path;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
backup_path = backups[0].path;
|
|
51
|
+
}
|
|
52
|
+
const content = await readFile(backup_path, 'utf-8');
|
|
53
|
+
const parsed = JSON.parse(content);
|
|
54
|
+
const config = validate_claude_config(parsed);
|
|
55
|
+
await write_claude_config(config);
|
|
56
|
+
const server_count = Object.keys(config.mcpServers || {}).length;
|
|
57
|
+
results.mcp = {
|
|
58
|
+
restored: backup_path,
|
|
59
|
+
servers: server_count,
|
|
60
|
+
};
|
|
61
|
+
if (!args.json) {
|
|
62
|
+
console.log(`Restored MCP: ${backup_path} (${server_count} servers)`);
|
|
63
|
+
}
|
|
34
64
|
}
|
|
35
|
-
backup_path = found.path;
|
|
36
65
|
}
|
|
37
|
-
|
|
38
|
-
|
|
66
|
+
if (restore_type === 'plugins' || restore_type === 'all') {
|
|
67
|
+
const backups = await list_plugin_backups();
|
|
68
|
+
if (backups.length === 0) {
|
|
69
|
+
if (restore_type === 'plugins') {
|
|
70
|
+
error('No plugin backups found. Run "mcpick backup" first.');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
let backup_path;
|
|
75
|
+
if (args.file && restore_type === 'plugins') {
|
|
76
|
+
const found = backups.find((b) => b.filename === args.file || b.path === args.file);
|
|
77
|
+
if (!found) {
|
|
78
|
+
error(`Backup '${args.file}' not found. Available: ${backups.map((b) => b.filename).join(', ')}`);
|
|
79
|
+
}
|
|
80
|
+
backup_path = found.path;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
backup_path = backups[0].path;
|
|
84
|
+
}
|
|
85
|
+
const content = await readFile(backup_path, 'utf-8');
|
|
86
|
+
const parsed = JSON.parse(content);
|
|
87
|
+
await write_claude_settings({
|
|
88
|
+
enabledPlugins: parsed.enabledPlugins || {},
|
|
89
|
+
});
|
|
90
|
+
const plugin_count = Object.keys(parsed.enabledPlugins || {}).length;
|
|
91
|
+
results.plugins = {
|
|
92
|
+
restored: backup_path,
|
|
93
|
+
plugins: plugin_count,
|
|
94
|
+
};
|
|
95
|
+
if (!args.json) {
|
|
96
|
+
console.log(`Restored plugins: ${backup_path} (${plugin_count} plugins)`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
39
99
|
}
|
|
40
|
-
const content = await readFile(backup_path, 'utf-8');
|
|
41
|
-
const parsed = JSON.parse(content);
|
|
42
|
-
const config = validate_claude_config(parsed);
|
|
43
|
-
await write_claude_config(config);
|
|
44
|
-
const server_count = Object.keys(config.mcpServers || {}).length;
|
|
45
100
|
if (args.json) {
|
|
46
|
-
output(
|
|
47
|
-
restored: backup_path,
|
|
48
|
-
servers: server_count,
|
|
49
|
-
}, true);
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
console.log(`Restored from ${backup_path} (${server_count} servers)`);
|
|
101
|
+
output(results, true);
|
|
53
102
|
}
|
|
54
103
|
},
|
|
55
104
|
});
|