mcpick 0.0.18 → 0.0.20
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/workflows/ci.yml +26 -0
- package/.vscode/settings.json +5 -0
- package/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/dist/add-BDyaBew0.js +113 -0
- package/dist/add-json-BjgzdeG-.js +58 -0
- package/dist/atomic-write-BqEykHp9.js +26 -0
- package/dist/backup-DSDhHI5f.js +64 -0
- package/dist/cache-D6kd7qE8.js +226 -0
- package/dist/claude-cli-DnmBJrjg.js +445 -0
- package/dist/cli-CsFfnWBo.js +84 -0
- package/dist/clone-DYKPEsar.js +88 -0
- package/dist/config-DijVdEFn.js +176 -0
- package/dist/dev-DRJRNp7y.js +265 -0
- package/dist/disable-xJXZfUR_.js +39 -0
- package/dist/enable-RrpcN6la.js +40 -0
- package/dist/get-Bb1eOOIZ.js +41 -0
- package/dist/hook-state-Di8lUsPr.js +171 -0
- package/dist/hooks-Bmn7pUZa.js +280 -0
- package/dist/index.js +1232 -305
- package/dist/list-B8YeDWt6.js +64 -0
- package/dist/marketplace-DcKk5dc1.js +168 -0
- package/dist/output-BchYq0mR.js +15 -0
- package/dist/paths-BPISiJi4.js +124 -0
- package/dist/plugin-cache-Bby9Dxm9.js +405 -0
- package/dist/plugins-Dc7DN6R_.js +212 -0
- package/dist/profile-CX97sMGp.js +120 -0
- package/dist/profile-DkY_lBEm.js +70 -0
- package/dist/redact-O35tjnRD.js +26 -0
- package/dist/registry-CfUKT7_C.js +92 -0
- package/dist/reload-CYDhkCVZ.js +31 -0
- package/dist/remove-D1owHLhG.js +31 -0
- package/dist/reset-project-choices-BfRSNN3m.js +28 -0
- package/dist/restore-DdMfUljI.js +84 -0
- package/dist/rolldown-runtime-CiIaOW0V.js +13 -0
- package/dist/settings-DEcWtzLE.js +201 -0
- package/dist/validation-xMlbgGCF.js +44 -0
- package/package.json +23 -22
- package/dist/cli/commands/add-json.js +0 -60
- package/dist/cli/commands/add.js +0 -135
- package/dist/cli/commands/backup.js +0 -83
- package/dist/cli/commands/cache.js +0 -296
- package/dist/cli/commands/clone.js +0 -108
- package/dist/cli/commands/dev.js +0 -167
- package/dist/cli/commands/disable.js +0 -36
- package/dist/cli/commands/enable.js +0 -39
- package/dist/cli/commands/get.js +0 -45
- package/dist/cli/commands/hooks.js +0 -314
- package/dist/cli/commands/list.js +0 -64
- package/dist/cli/commands/marketplace.js +0 -211
- package/dist/cli/commands/plugins.js +0 -265
- package/dist/cli/commands/profile.js +0 -134
- package/dist/cli/commands/reload.js +0 -36
- package/dist/cli/commands/remove.js +0 -35
- package/dist/cli/commands/reset-project-choices.js +0 -32
- package/dist/cli/commands/restore.js +0 -105
- package/dist/cli/index.js +0 -29
- package/dist/cli/output.js +0 -21
- package/dist/commands/add-server.js +0 -310
- package/dist/commands/backup.js +0 -60
- package/dist/commands/edit-config.js +0 -109
- package/dist/commands/edit-plugins.js +0 -201
- package/dist/commands/manage-cache.js +0 -155
- package/dist/commands/manage-hooks.js +0 -99
- package/dist/commands/manage-marketplace.js +0 -293
- package/dist/commands/restore.js +0 -118
- package/dist/core/config.js +0 -200
- package/dist/core/dev-override.js +0 -155
- package/dist/core/hook-state.js +0 -220
- package/dist/core/plugin-cache.js +0 -506
- package/dist/core/profile.js +0 -94
- package/dist/core/registry.js +0 -121
- package/dist/core/settings.js +0 -243
- package/dist/core/validation.js +0 -49
- package/dist/types.js +0 -2
- package/dist/utils/atomic-write.js +0 -27
- package/dist/utils/claude-cli.js +0 -485
- package/dist/utils/paths.js +0 -114
- package/dist/utils/redact.js +0 -28
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { a as get_claude_settings_path } from "./paths-BPISiJi4.js";
|
|
2
|
+
import { t as atomic_json_write } from "./atomic-write-BqEykHp9.js";
|
|
3
|
+
import { access, readFile } from "node:fs/promises";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
//#region src/core/settings.ts
|
|
6
|
+
async function read_claude_settings() {
|
|
7
|
+
const settings_path = get_claude_settings_path();
|
|
8
|
+
try {
|
|
9
|
+
await access(settings_path);
|
|
10
|
+
const content = await readFile(settings_path, "utf-8");
|
|
11
|
+
return JSON.parse(content);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") return {};
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function write_claude_settings(updates) {
|
|
18
|
+
await atomic_json_write(get_claude_settings_path(), (existing) => {
|
|
19
|
+
for (const [key, value] of Object.entries(updates)) existing[key] = value;
|
|
20
|
+
return existing;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Parse enabledPlugins into structured list.
|
|
25
|
+
* Keys are in format "plugin-name@marketplace-name"
|
|
26
|
+
*/
|
|
27
|
+
function get_all_plugins(settings) {
|
|
28
|
+
const enabled_plugins = settings.enabledPlugins || {};
|
|
29
|
+
return Object.entries(enabled_plugins).map(([key, enabled]) => {
|
|
30
|
+
const at_index = key.lastIndexOf("@");
|
|
31
|
+
return {
|
|
32
|
+
name: at_index > 0 ? key.substring(0, at_index) : key,
|
|
33
|
+
marketplace: at_index > 0 ? key.substring(at_index + 1) : "unknown",
|
|
34
|
+
enabled
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build the enabledPlugins record from a list of PluginInfo
|
|
40
|
+
*/
|
|
41
|
+
function build_enabled_plugins(plugins) {
|
|
42
|
+
const result = {};
|
|
43
|
+
for (const plugin of plugins) {
|
|
44
|
+
const key = `${plugin.name}@${plugin.marketplace}`;
|
|
45
|
+
result[key] = plugin.enabled;
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
async function read_settings_file(path) {
|
|
50
|
+
try {
|
|
51
|
+
await access(path);
|
|
52
|
+
const content = await readFile(path, "utf-8");
|
|
53
|
+
return JSON.parse(content);
|
|
54
|
+
} catch {
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function get_settings_paths() {
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
scope: "user",
|
|
62
|
+
path: resolve(process.env.HOME || process.env.USERPROFILE || "", ".claude", "settings.json")
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
scope: "project",
|
|
66
|
+
path: resolve(process.cwd(), ".claude", "settings.json")
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
scope: "project-local",
|
|
70
|
+
path: resolve(process.cwd(), ".claude", "settings.local.json")
|
|
71
|
+
}
|
|
72
|
+
];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Read all hooks across all scopes (settings + plugins), flattened for display.
|
|
76
|
+
*/
|
|
77
|
+
async function get_all_hooks() {
|
|
78
|
+
const entries = [];
|
|
79
|
+
for (const { scope, path } of get_settings_paths()) {
|
|
80
|
+
const hooks = (await read_settings_file(path)).hooks;
|
|
81
|
+
if (!hooks) continue;
|
|
82
|
+
for (const [event, matchers] of Object.entries(hooks)) {
|
|
83
|
+
if (!Array.isArray(matchers)) continue;
|
|
84
|
+
for (let mi = 0; mi < matchers.length; mi++) {
|
|
85
|
+
const m = matchers[mi];
|
|
86
|
+
if (!m.hooks?.length) continue;
|
|
87
|
+
for (let hi = 0; hi < m.hooks.length; hi++) entries.push({
|
|
88
|
+
event,
|
|
89
|
+
matcher: m.matcher,
|
|
90
|
+
handler: m.hooks[hi],
|
|
91
|
+
scope,
|
|
92
|
+
source: scope,
|
|
93
|
+
matcher_index: mi,
|
|
94
|
+
hook_index: hi
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const plugin_hooks = await get_all_plugin_hooks();
|
|
100
|
+
entries.push(...plugin_hooks);
|
|
101
|
+
return entries;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Scan all installed plugins for hooks.json and return flattened hook entries.
|
|
105
|
+
* Checks both cache and marketplace source paths since Claude Code reads from both.
|
|
106
|
+
*/
|
|
107
|
+
async function get_all_plugin_hooks() {
|
|
108
|
+
const { read_installed_plugins } = await import("./plugin-cache-Bby9Dxm9.js").then((n) => n.s);
|
|
109
|
+
const { get_marketplaces_dir } = await import("./paths-BPISiJi4.js").then((n) => n.b);
|
|
110
|
+
const installed = await read_installed_plugins();
|
|
111
|
+
const entries = [];
|
|
112
|
+
const seen_hooks = /* @__PURE__ */ new Set();
|
|
113
|
+
for (const [plugin_key, installs] of Object.entries(installed.plugins)) {
|
|
114
|
+
if (!installs?.length) continue;
|
|
115
|
+
const install = installs[0];
|
|
116
|
+
const at_index = plugin_key.lastIndexOf("@");
|
|
117
|
+
const plugin_name = at_index > 0 ? plugin_key.substring(0, at_index) : plugin_key;
|
|
118
|
+
const marketplace_name = at_index > 0 ? plugin_key.substring(at_index + 1) : "";
|
|
119
|
+
const hooks_paths = [join(install.installPath, "hooks", "hooks.json")];
|
|
120
|
+
if (marketplace_name) hooks_paths.push(join(get_marketplaces_dir(), marketplace_name, "plugins", plugin_name, "hooks", "hooks.json"));
|
|
121
|
+
for (const hooks_path of hooks_paths) {
|
|
122
|
+
let hooks_data;
|
|
123
|
+
try {
|
|
124
|
+
const content = await readFile(hooks_path, "utf-8");
|
|
125
|
+
hooks_data = JSON.parse(content);
|
|
126
|
+
} catch {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const hooks = hooks_data.hooks || hooks_data;
|
|
130
|
+
for (const [event, matchers] of Object.entries(hooks)) {
|
|
131
|
+
if (!Array.isArray(matchers)) continue;
|
|
132
|
+
for (let mi = 0; mi < matchers.length; mi++) {
|
|
133
|
+
const m = matchers[mi];
|
|
134
|
+
if (!m.hooks?.length) continue;
|
|
135
|
+
for (let hi = 0; hi < m.hooks.length; hi++) {
|
|
136
|
+
const h = m.hooks[hi];
|
|
137
|
+
const dedup_key = `${plugin_key}:${event}:${h.type}:${h.command || h.url || h.prompt}`;
|
|
138
|
+
if (seen_hooks.has(dedup_key)) continue;
|
|
139
|
+
seen_hooks.add(dedup_key);
|
|
140
|
+
entries.push({
|
|
141
|
+
event,
|
|
142
|
+
matcher: m.matcher,
|
|
143
|
+
handler: h,
|
|
144
|
+
scope: "user",
|
|
145
|
+
source: "plugin",
|
|
146
|
+
matcher_index: mi,
|
|
147
|
+
hook_index: hi,
|
|
148
|
+
plugin_key,
|
|
149
|
+
hooks_json_path: hooks_path
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return entries;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Remove a specific hook entry by scope/event/indices.
|
|
160
|
+
*/
|
|
161
|
+
async function remove_hook(entry) {
|
|
162
|
+
const scope_path = get_settings_paths().find((s) => s.scope === entry.scope);
|
|
163
|
+
if (!scope_path) throw new Error(`Unknown scope: ${entry.scope}`);
|
|
164
|
+
await atomic_json_write(scope_path.path, (existing) => {
|
|
165
|
+
const hooks = existing.hooks;
|
|
166
|
+
if (!hooks) return existing;
|
|
167
|
+
const matchers = hooks[entry.event];
|
|
168
|
+
if (!matchers?.[entry.matcher_index]) return existing;
|
|
169
|
+
const matcher = matchers[entry.matcher_index];
|
|
170
|
+
matcher.hooks.splice(entry.hook_index, 1);
|
|
171
|
+
if (matcher.hooks.length === 0) matchers.splice(entry.matcher_index, 1);
|
|
172
|
+
if (matchers.length === 0) delete hooks[entry.event];
|
|
173
|
+
if (Object.keys(hooks).length === 0) delete existing.hooks;
|
|
174
|
+
return existing;
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Add a hook to a specific scope.
|
|
179
|
+
*/
|
|
180
|
+
async function add_hook(scope, event, matcher, handler) {
|
|
181
|
+
const scope_path = get_settings_paths().find((s) => s.scope === scope);
|
|
182
|
+
if (!scope_path) throw new Error(`Unknown scope: ${scope}`);
|
|
183
|
+
await atomic_json_write(scope_path.path, (existing) => {
|
|
184
|
+
if (!existing.hooks) existing.hooks = {};
|
|
185
|
+
const hooks = existing.hooks;
|
|
186
|
+
if (!hooks[event]) hooks[event] = [];
|
|
187
|
+
const matchers = hooks[event];
|
|
188
|
+
const existing_matcher = matchers.find((m) => (m.matcher || void 0) === matcher);
|
|
189
|
+
if (existing_matcher) existing_matcher.hooks.push(handler);
|
|
190
|
+
else {
|
|
191
|
+
const new_matcher = { hooks: [handler] };
|
|
192
|
+
if (matcher) new_matcher.matcher = matcher;
|
|
193
|
+
matchers.push(new_matcher);
|
|
194
|
+
}
|
|
195
|
+
return existing;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
//#endregion
|
|
199
|
+
export { read_claude_settings as a, get_all_plugins as i, build_enabled_plugins as n, remove_hook as o, get_all_hooks as r, write_claude_settings as s, add_hook as t };
|
|
200
|
+
|
|
201
|
+
//# sourceMappingURL=settings-DEcWtzLE.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
//#region src/core/validation.ts
|
|
3
|
+
const mcp_server_schema_stdio = v.object({
|
|
4
|
+
type: v.optional(v.literal("stdio")),
|
|
5
|
+
command: v.pipe(v.string(), v.minLength(1)),
|
|
6
|
+
args: v.optional(v.array(v.string())),
|
|
7
|
+
env: v.optional(v.record(v.string(), v.string())),
|
|
8
|
+
description: v.optional(v.string())
|
|
9
|
+
});
|
|
10
|
+
const mcp_server_schema_sse = v.object({
|
|
11
|
+
type: v.literal("sse"),
|
|
12
|
+
env: v.optional(v.record(v.string(), v.string())),
|
|
13
|
+
url: v.pipe(v.string(), v.minLength(1)),
|
|
14
|
+
headers: v.optional(v.record(v.string(), v.string())),
|
|
15
|
+
description: v.optional(v.string())
|
|
16
|
+
});
|
|
17
|
+
const mcp_server_schema_http = v.object({
|
|
18
|
+
type: v.literal("http"),
|
|
19
|
+
env: v.optional(v.record(v.string(), v.string())),
|
|
20
|
+
url: v.pipe(v.string(), v.minLength(1)),
|
|
21
|
+
headers: v.optional(v.record(v.string(), v.string())),
|
|
22
|
+
description: v.optional(v.string())
|
|
23
|
+
});
|
|
24
|
+
const mcp_server_schema_base = v.union([
|
|
25
|
+
mcp_server_schema_stdio,
|
|
26
|
+
mcp_server_schema_sse,
|
|
27
|
+
mcp_server_schema_http
|
|
28
|
+
]);
|
|
29
|
+
const mcp_server_schema = v.intersect([v.object({ name: v.pipe(v.string(), v.minLength(1)) }), mcp_server_schema_base]);
|
|
30
|
+
const claude_config_schema = v.object({ mcpServers: v.optional(v.record(v.string(), mcp_server_schema_base)) });
|
|
31
|
+
const server_registry_schema = v.object({ servers: v.array(mcp_server_schema) });
|
|
32
|
+
function validate_mcp_server(data) {
|
|
33
|
+
return v.parse(mcp_server_schema, data);
|
|
34
|
+
}
|
|
35
|
+
function validate_claude_config(data) {
|
|
36
|
+
return v.parse(claude_config_schema, data);
|
|
37
|
+
}
|
|
38
|
+
function validate_server_registry(data) {
|
|
39
|
+
return v.parse(server_registry_schema, data);
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { validate_mcp_server as n, validate_server_registry as r, validate_claude_config as t };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=validation-xMlbgGCF.js.map
|
package/package.json
CHANGED
|
@@ -1,41 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcpick",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"mcpick": "./dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"engines": {
|
|
11
|
-
"node": ">=22.0.0"
|
|
12
|
-
},
|
|
3
|
+
"version": "0.0.20",
|
|
4
|
+
"description": "Claude Code extension manager — MCP servers, plugins (skills, hooks, agents), and marketplaces",
|
|
13
5
|
"keywords": [
|
|
14
6
|
"claude",
|
|
15
|
-
"
|
|
7
|
+
"claude-code",
|
|
16
8
|
"cli",
|
|
17
9
|
"configuration",
|
|
18
|
-
"
|
|
10
|
+
"mcp"
|
|
19
11
|
],
|
|
20
|
-
"author": "",
|
|
21
12
|
"license": "MIT",
|
|
13
|
+
"author": "",
|
|
14
|
+
"bin": {
|
|
15
|
+
"mcpick": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"main": "./dist/index.js",
|
|
22
19
|
"dependencies": {
|
|
23
|
-
"@clack/prompts": "^1.
|
|
24
|
-
"citty": "^0.2.
|
|
20
|
+
"@clack/prompts": "^1.2.0",
|
|
21
|
+
"citty": "^0.2.2",
|
|
25
22
|
"valibot": "^1.3.1"
|
|
26
23
|
},
|
|
27
24
|
"devDependencies": {
|
|
28
25
|
"@changesets/cli": "^2.30.0",
|
|
29
|
-
"@types/node": "^25.
|
|
30
|
-
"
|
|
31
|
-
"
|
|
26
|
+
"@types/node": "^25.6.0",
|
|
27
|
+
"vite-plus": "^0.1.16",
|
|
28
|
+
"vitest": "^4.1.4"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=22.0.0"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
"build": "
|
|
35
|
-
"dev": "
|
|
34
|
+
"build": "vp pack",
|
|
35
|
+
"dev": "vp pack --watch",
|
|
36
36
|
"start": "node ./dist/index.js",
|
|
37
|
-
"
|
|
38
|
-
"format
|
|
37
|
+
"check": "vp check",
|
|
38
|
+
"format": "vp check --fix",
|
|
39
|
+
"test": "vp test",
|
|
39
40
|
"changeset": "changeset",
|
|
40
41
|
"version": "changeset version",
|
|
41
42
|
"release": "pnpm run build && changeset publish"
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
2
|
-
import { mcp_add_json_via_cli } from '../../utils/claude-cli.js';
|
|
3
|
-
import { error, output } from '../output.js';
|
|
4
|
-
export default defineCommand({
|
|
5
|
-
meta: {
|
|
6
|
-
name: 'add-json',
|
|
7
|
-
description: 'Add an MCP server from a JSON configuration string',
|
|
8
|
-
},
|
|
9
|
-
args: {
|
|
10
|
-
name: {
|
|
11
|
-
type: 'positional',
|
|
12
|
-
description: 'Server name',
|
|
13
|
-
required: true,
|
|
14
|
-
},
|
|
15
|
-
config: {
|
|
16
|
-
type: 'positional',
|
|
17
|
-
description: 'JSON configuration string',
|
|
18
|
-
required: true,
|
|
19
|
-
},
|
|
20
|
-
scope: {
|
|
21
|
-
type: 'string',
|
|
22
|
-
description: 'Scope: local, project, or user (default: local)',
|
|
23
|
-
default: 'local',
|
|
24
|
-
},
|
|
25
|
-
json: {
|
|
26
|
-
type: 'boolean',
|
|
27
|
-
description: 'Output as JSON',
|
|
28
|
-
default: false,
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
async run({ args }) {
|
|
32
|
-
const scope = args.scope;
|
|
33
|
-
if (!['local', 'project', 'user'].includes(scope)) {
|
|
34
|
-
error(`Invalid scope: ${scope}. Use local, project, or user.`);
|
|
35
|
-
}
|
|
36
|
-
// Validate the JSON is parseable
|
|
37
|
-
try {
|
|
38
|
-
JSON.parse(args.config);
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
error('Invalid JSON configuration. Provide a valid JSON string.');
|
|
42
|
-
}
|
|
43
|
-
const result = await mcp_add_json_via_cli(args.name, args.config, scope);
|
|
44
|
-
if (args.json) {
|
|
45
|
-
output({
|
|
46
|
-
added: args.name,
|
|
47
|
-
scope,
|
|
48
|
-
success: result.success,
|
|
49
|
-
error: result.error,
|
|
50
|
-
}, true);
|
|
51
|
-
}
|
|
52
|
-
else if (result.success) {
|
|
53
|
-
console.log(`Added '${args.name}' from JSON (scope: ${scope})`);
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
error(result.error || 'Unknown error');
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
//# sourceMappingURL=add-json.js.map
|
package/dist/cli/commands/add.js
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
2
|
-
import { add_server_to_registry } from '../../core/registry.js';
|
|
3
|
-
import { validate_mcp_server } from '../../core/validation.js';
|
|
4
|
-
import { add_mcp_via_cli } from '../../utils/claude-cli.js';
|
|
5
|
-
import { error, output } from '../output.js';
|
|
6
|
-
export default defineCommand({
|
|
7
|
-
meta: {
|
|
8
|
-
name: 'add',
|
|
9
|
-
description: 'Add a new MCP server to the registry and enable it',
|
|
10
|
-
},
|
|
11
|
-
args: {
|
|
12
|
-
name: {
|
|
13
|
-
type: 'string',
|
|
14
|
-
description: 'Server name',
|
|
15
|
-
required: true,
|
|
16
|
-
},
|
|
17
|
-
command: {
|
|
18
|
-
type: 'string',
|
|
19
|
-
description: 'Command to run (for stdio transport)',
|
|
20
|
-
},
|
|
21
|
-
args: {
|
|
22
|
-
type: 'string',
|
|
23
|
-
description: 'Comma-separated arguments (e.g. "npx,-y,mcp-sqlite")',
|
|
24
|
-
},
|
|
25
|
-
url: {
|
|
26
|
-
type: 'string',
|
|
27
|
-
description: 'URL (for sse or http transport)',
|
|
28
|
-
},
|
|
29
|
-
type: {
|
|
30
|
-
type: 'string',
|
|
31
|
-
description: 'Transport type: stdio, sse, or http (default: stdio)',
|
|
32
|
-
default: 'stdio',
|
|
33
|
-
},
|
|
34
|
-
env: {
|
|
35
|
-
type: 'string',
|
|
36
|
-
description: 'Environment variables as KEY=val,KEY=val',
|
|
37
|
-
},
|
|
38
|
-
headers: {
|
|
39
|
-
type: 'string',
|
|
40
|
-
description: 'HTTP headers as KEY=val,KEY=val',
|
|
41
|
-
},
|
|
42
|
-
description: {
|
|
43
|
-
type: 'string',
|
|
44
|
-
description: 'Server description',
|
|
45
|
-
},
|
|
46
|
-
scope: {
|
|
47
|
-
type: 'string',
|
|
48
|
-
description: 'Scope: local, project, or user (default: local)',
|
|
49
|
-
default: 'local',
|
|
50
|
-
},
|
|
51
|
-
json: {
|
|
52
|
-
type: 'boolean',
|
|
53
|
-
description: 'Output as JSON',
|
|
54
|
-
default: false,
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
async run({ args }) {
|
|
58
|
-
const scope = args.scope;
|
|
59
|
-
if (!['local', 'project', 'user'].includes(scope)) {
|
|
60
|
-
error(`Invalid scope: ${scope}. Use local, project, or user.`);
|
|
61
|
-
}
|
|
62
|
-
const transport = args.type;
|
|
63
|
-
if (!['stdio', 'sse', 'http'].includes(transport)) {
|
|
64
|
-
error(`Invalid type: ${transport}. Use stdio, sse, or http.`);
|
|
65
|
-
}
|
|
66
|
-
// Build server object
|
|
67
|
-
const server_data = {
|
|
68
|
-
name: args.name,
|
|
69
|
-
};
|
|
70
|
-
if (transport === 'stdio') {
|
|
71
|
-
if (!args.command) {
|
|
72
|
-
error('--command is required for stdio transport');
|
|
73
|
-
}
|
|
74
|
-
server_data.command = args.command;
|
|
75
|
-
if (args.args) {
|
|
76
|
-
server_data.args = args.args.split(',');
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
if (!args.url) {
|
|
81
|
-
error(`--url is required for ${transport} transport`);
|
|
82
|
-
}
|
|
83
|
-
server_data.type = transport;
|
|
84
|
-
server_data.url = args.url;
|
|
85
|
-
if (args.headers) {
|
|
86
|
-
server_data.headers = parse_key_value_pairs(args.headers);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
if (args.env) {
|
|
90
|
-
server_data.env = parse_key_value_pairs(args.env);
|
|
91
|
-
}
|
|
92
|
-
if (args.description) {
|
|
93
|
-
server_data.description = args.description;
|
|
94
|
-
}
|
|
95
|
-
// Validate
|
|
96
|
-
let server;
|
|
97
|
-
try {
|
|
98
|
-
server = validate_mcp_server(server_data);
|
|
99
|
-
}
|
|
100
|
-
catch (err) {
|
|
101
|
-
error(`Invalid server config: ${err instanceof Error ? err.message : 'validation failed'}`);
|
|
102
|
-
}
|
|
103
|
-
// Add to registry
|
|
104
|
-
await add_server_to_registry(server);
|
|
105
|
-
// Enable via CLI
|
|
106
|
-
const result = await add_mcp_via_cli(server, scope);
|
|
107
|
-
if (args.json) {
|
|
108
|
-
output({
|
|
109
|
-
added: server.name,
|
|
110
|
-
scope,
|
|
111
|
-
cli: result.success,
|
|
112
|
-
error: result.error,
|
|
113
|
-
}, true);
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
if (result.success) {
|
|
117
|
-
console.log(`Added '${server.name}' and enabled (scope: ${scope})`);
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
console.log(`Added '${server.name}' to registry but CLI failed: ${result.error}`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
function parse_key_value_pairs(input) {
|
|
126
|
-
const result = {};
|
|
127
|
-
for (const pair of input.split(',')) {
|
|
128
|
-
const eq = pair.indexOf('=');
|
|
129
|
-
if (eq > 0) {
|
|
130
|
-
result[pair.substring(0, eq)] = pair.substring(eq + 1);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return result;
|
|
134
|
-
}
|
|
135
|
-
//# sourceMappingURL=add.js.map
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { defineCommand } from 'citty';
|
|
2
|
-
import { readdir, unlink, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { read_claude_config } from '../../core/config.js';
|
|
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';
|
|
7
|
-
import { output } from '../output.js';
|
|
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
|
-
}
|
|
27
|
-
export default defineCommand({
|
|
28
|
-
meta: {
|
|
29
|
-
name: 'backup',
|
|
30
|
-
description: 'Create a timestamped backup of MCP servers and plugins',
|
|
31
|
-
},
|
|
32
|
-
args: {
|
|
33
|
-
json: {
|
|
34
|
-
type: 'boolean',
|
|
35
|
-
description: 'Output as JSON',
|
|
36
|
-
default: false,
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
async run({ args }) {
|
|
40
|
-
const current_config = await read_claude_config();
|
|
41
|
-
const current_settings = await read_claude_settings();
|
|
42
|
-
const backups_dir = get_backups_dir();
|
|
43
|
-
await ensure_directory_exists(backups_dir);
|
|
44
|
-
// Backup MCP servers
|
|
45
|
-
const mcp_filename = get_backup_filename();
|
|
46
|
-
const mcp_path = join(backups_dir, mcp_filename);
|
|
47
|
-
const mcp_backup = {
|
|
48
|
-
mcpServers: current_config.mcpServers || {},
|
|
49
|
-
};
|
|
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');
|
|
60
|
-
}
|
|
61
|
-
await cleanup_old_backups('mcp-servers-');
|
|
62
|
-
await cleanup_old_backups('plugins-');
|
|
63
|
-
const server_count = Object.keys(current_config.mcpServers || {}).length;
|
|
64
|
-
if (args.json) {
|
|
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);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
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
|
-
}
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
});
|
|
83
|
-
//# sourceMappingURL=backup.js.map
|