@hileeon/mcc 0.1.4 → 0.1.6
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/dist/accounts/store.d.ts +1 -0
- package/dist/accounts/store.d.ts.map +1 -1
- package/dist/accounts/store.js.map +1 -1
- package/dist/dashboard-server.js +5 -4
- package/dist/dashboard-server.js.map +1 -1
- package/dist/mcc.js +8 -2
- package/dist/mcc.js.map +1 -1
- package/dist/proxy/proxy-daemon.d.ts +1 -1
- package/dist/proxy/proxy-daemon.d.ts.map +1 -1
- package/dist/proxy/proxy-daemon.js +2 -1
- package/dist/proxy/proxy-daemon.js.map +1 -1
- package/dist/proxy/proxy-entry.js +6 -1
- package/dist/proxy/proxy-entry.js.map +1 -1
- package/dist/proxy/proxy-server.d.ts +1 -0
- package/dist/proxy/proxy-server.d.ts.map +1 -1
- package/dist/proxy/proxy-server.js +1 -1
- package/dist/proxy/proxy-server.js.map +1 -1
- package/dist/proxy/upstream-url.d.ts +1 -1
- package/dist/proxy/upstream-url.d.ts.map +1 -1
- package/dist/proxy/upstream-url.js +20 -2
- package/dist/proxy/upstream-url.js.map +1 -1
- package/dist/shared/provider-preset-catalog.d.ts +3 -1
- package/dist/shared/provider-preset-catalog.d.ts.map +1 -1
- package/dist/shared/provider-preset-catalog.js +16 -0
- package/dist/shared/provider-preset-catalog.js.map +1 -1
- package/{ui/dist → dist/ui}/index.html +2 -2
- package/package.json +6 -2
- package/.claude/CLAUDE.md +0 -204
- package/.claude/agents/.gitkeep +0 -0
- package/.claude/settings.json +0 -9
- package/.claude/skills/.gitkeep +0 -0
- package/docs/decisions.md +0 -33
- package/docs/lessons.md +0 -8
- package/docs/product.md +0 -37
- package/src/accounts/instance-manager.ts +0 -58
- package/src/accounts/shared-manager.ts +0 -154
- package/src/accounts/store.ts +0 -111
- package/src/core/model-router.ts +0 -82
- package/src/dashboard-server.ts +0 -427
- package/src/mcc.ts +0 -482
- package/src/mcp/external-registry.ts +0 -73
- package/src/mcp/installer.ts +0 -258
- package/src/mcp/mcp-config.ts +0 -168
- package/src/mcp/registry.ts +0 -89
- package/src/proxy/proxy-daemon.ts +0 -184
- package/src/proxy/proxy-entry.ts +0 -63
- package/src/proxy/proxy-paths.ts +0 -97
- package/src/proxy/proxy-server.ts +0 -278
- package/src/proxy/upstream-url.ts +0 -38
- package/src/shared/logger.ts +0 -140
- package/src/shared/provider-preset-catalog.ts +0 -340
- package/tsconfig.json +0 -33
- package/ui/.prettierrc +0 -9
- package/ui/index.html +0 -12
- package/ui/package.json +0 -33
- package/ui/postcss.config.js +0 -6
- package/ui/src/App.tsx +0 -753
- package/ui/src/components/ui/button.tsx +0 -48
- package/ui/src/components/ui/card.tsx +0 -50
- package/ui/src/components/ui/input.tsx +0 -21
- package/ui/src/components/ui/label.tsx +0 -20
- package/ui/src/components/ui/select.tsx +0 -80
- package/ui/src/components/ui/switch.tsx +0 -26
- package/ui/src/components/ui/tabs.tsx +0 -52
- package/ui/src/index.css +0 -33
- package/ui/src/lib/api.ts +0 -185
- package/ui/src/lib/utils.ts +0 -6
- package/ui/src/main.tsx +0 -10
- package/ui/src/vite-env.d.ts +0 -1
- package/ui/tailwind.config.js +0 -49
- package/ui/tsconfig.json +0 -25
- package/ui/vite.config.ts +0 -20
- /package/{ui/dist → dist/ui}/assets/index-B16lhKZ6.js +0 -0
- /package/{ui/dist → dist/ui}/assets/index-jEfiB6-h.css +0 -0
package/src/mcc.ts
DELETED
|
@@ -1,482 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCC - My Cloud Code
|
|
3
|
-
*
|
|
4
|
-
* Simplified CLI for multi-provider profile switching.
|
|
5
|
-
* No OAuth, no local proxy - uses provider's direct API.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { spawn } from 'child_process';
|
|
9
|
-
import * as path from 'path';
|
|
10
|
-
import pc from 'picocolors';
|
|
11
|
-
import {
|
|
12
|
-
listProfiles,
|
|
13
|
-
getProfile,
|
|
14
|
-
getProfileApiKey,
|
|
15
|
-
saveProfile,
|
|
16
|
-
deleteProfile,
|
|
17
|
-
setDefaultProfile,
|
|
18
|
-
getDefaultProfile,
|
|
19
|
-
hasProfile,
|
|
20
|
-
type Profile,
|
|
21
|
-
} from './accounts/store';
|
|
22
|
-
import { MCCInstanceManager } from './accounts/instance-manager';
|
|
23
|
-
import { buildProfileEnv } from './core/model-router';
|
|
24
|
-
import {
|
|
25
|
-
installBuiltinServers,
|
|
26
|
-
syncInstanceMcpServers,
|
|
27
|
-
enableInstanceExternalMcp,
|
|
28
|
-
disableInstanceExternalMcp,
|
|
29
|
-
} from './mcp/installer';
|
|
30
|
-
import { BUILTIN_MCP_SERVERS, getAllServers } from './mcp/registry';
|
|
31
|
-
import {
|
|
32
|
-
readExternalMcpRegistry,
|
|
33
|
-
addExternalMcpServer,
|
|
34
|
-
removeExternalMcpServer,
|
|
35
|
-
type ExternalMcpServer,
|
|
36
|
-
} from './mcp/external-registry';
|
|
37
|
-
import { startProxy } from './proxy/proxy-daemon';
|
|
38
|
-
import { log, init, makeSessionId, isDebugEnabled } from './shared/logger';
|
|
39
|
-
import { readMcpConfig, getEnabledWebSearchProviders, getActiveImageAnalysisProvider } from './mcp/mcp-config';
|
|
40
|
-
|
|
41
|
-
const PKG_VERSION = '0.1.4';
|
|
42
|
-
|
|
43
|
-
const instanceMgr = new MCCInstanceManager();
|
|
44
|
-
|
|
45
|
-
function showVersion(): void {
|
|
46
|
-
console.log(`@hileeon/mcc ${PKG_VERSION}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function showHelp(): void {
|
|
50
|
-
console.log(`
|
|
51
|
-
MCC - My Cloud Code
|
|
52
|
-
|
|
53
|
-
Usage: mcc [mcc-options] <profile> [claude-options...]
|
|
54
|
-
|
|
55
|
-
<profile> Profile name to launch (required)
|
|
56
|
-
[claude-options...] All options after profile are passed directly to Claude Code
|
|
57
|
-
|
|
58
|
-
MCC Options:
|
|
59
|
-
-h, --help Show this help
|
|
60
|
-
-v, --version Show version number
|
|
61
|
-
-- Explicit separator (everything after is for Claude Code)
|
|
62
|
-
|
|
63
|
-
Note: All flags after <profile> are passed directly to Claude Code.
|
|
64
|
-
|
|
65
|
-
Debug logging: MCC_LOG_LEVEL=debug node dist/mcc.js deepseek
|
|
66
|
-
|
|
67
|
-
Examples:
|
|
68
|
-
mcc deepseek Interactive session
|
|
69
|
-
mcc deepseek --print "hello" Non-interactive
|
|
70
|
-
mcc deepseek --print "hello" --verbose Claude Code verbose mode
|
|
71
|
-
|
|
72
|
-
mcc profile add prod --base-url https://api.deepseek.com/anthropic --api-key sk-xxxx --model deepseek-chat
|
|
73
|
-
mcc profile list
|
|
74
|
-
mcc dashboard
|
|
75
|
-
`.trim());
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async function cmdLaunch(args: string[]): Promise<void> {
|
|
79
|
-
const profileName = args[0];
|
|
80
|
-
if (!profileName) {
|
|
81
|
-
console.error('[!] Usage: mcc <profile> [args...]');
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const profile = getProfile(profileName);
|
|
86
|
-
if (!profile) {
|
|
87
|
-
console.error(`[!] Profile not found: ${profileName}`);
|
|
88
|
-
console.error('[i] Available profiles:');
|
|
89
|
-
for (const p of listProfiles()) {
|
|
90
|
-
console.error(` - ${p.name} (baseUrl: ${p.baseUrl})`);
|
|
91
|
-
}
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const proto = profile.protocol || 'anthropic';
|
|
96
|
-
console.log(` ${pc.cyan(pc.bold('●'))} ${pc.bold(profileName)} ${pc.dim('·')} ${pc.cyan(profile.model)} ${pc.dim('·')} ${pc.dim(proto)}`);
|
|
97
|
-
|
|
98
|
-
const apiKey = getProfileApiKey(profileName);
|
|
99
|
-
if (!apiKey) {
|
|
100
|
-
console.error(`[!] No API key found for profile: ${profileName}`);
|
|
101
|
-
process.exit(1);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Read MCP config early so we can display provider info
|
|
105
|
-
const mcpConfig = readMcpConfig();
|
|
106
|
-
const wsProviders = getEnabledWebSearchProviders(mcpConfig);
|
|
107
|
-
const iaProvider = getActiveImageAnalysisProvider(mcpConfig);
|
|
108
|
-
|
|
109
|
-
const instancePath = await instanceMgr.ensureInstance(profileName);
|
|
110
|
-
console.log(` ${pc.dim('instance')} ${pc.dim(instancePath)}`);
|
|
111
|
-
|
|
112
|
-
syncInstanceMcpServers(instancePath, BUILTIN_MCP_SERVERS.map((s) => s.name), profileName);
|
|
113
|
-
|
|
114
|
-
// Initialize logging session
|
|
115
|
-
const sessionId = makeSessionId();
|
|
116
|
-
const mccHome = process.env.MCC_HOME ?? path.join(process.env.HOME ?? process.env.USERPROFILE ?? '~', '.mcc');
|
|
117
|
-
const logDir = path.join(mccHome, 'logs', profileName, sessionId);
|
|
118
|
-
init(sessionId, logDir);
|
|
119
|
-
log.info('MCC', `Session starting: ${profileName} | log: ${logDir}`);
|
|
120
|
-
console.log(` ${pc.dim('session')} ${pc.dim(sessionId)}`);
|
|
121
|
-
|
|
122
|
-
// MCP provider summary
|
|
123
|
-
if (wsProviders.length > 0) {
|
|
124
|
-
console.log(` ${pc.dim('websearch')} ${pc.dim(wsProviders.join(', '))}`);
|
|
125
|
-
} else if (mcpConfig.websearch.enabled) {
|
|
126
|
-
console.log(` ${pc.dim('websearch')} ${pc.dim('none enabled')}`);
|
|
127
|
-
}
|
|
128
|
-
if (mcpConfig.imageAnalysis.enabled) {
|
|
129
|
-
if (iaProvider) {
|
|
130
|
-
console.log(` ${pc.dim('image')} ${pc.dim(iaProvider.id)} ${pc.dim('·')} ${pc.cyan(iaProvider.model)}`);
|
|
131
|
-
} else {
|
|
132
|
-
console.log(` ${pc.dim('image')} ${pc.dim('no active provider')}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const env = buildProfileEnv(profile, apiKey, instancePath);
|
|
137
|
-
env.MCC_CURRENT_PROFILE = profileName;
|
|
138
|
-
env.MCC_LOG_SESSION_ID = sessionId;
|
|
139
|
-
env.MCC_LOG_DIR = logDir;
|
|
140
|
-
|
|
141
|
-
// Debug: key env vars (no secrets)
|
|
142
|
-
if (isDebugEnabled()) {
|
|
143
|
-
const debugEnvs: [string, string][] = [
|
|
144
|
-
['ANTHROPIC_BASE_URL', env.ANTHROPIC_BASE_URL],
|
|
145
|
-
['ANTHROPIC_MODEL', env.ANTHROPIC_MODEL],
|
|
146
|
-
['CLAUDE_CONFIG_DIR', env.CLAUDE_CONFIG_DIR],
|
|
147
|
-
['MCC_WEBSEARCH_ENABLED', env.MCC_WEBSEARCH_ENABLED || '0'],
|
|
148
|
-
['MCC_IMAGE_ANALYSIS_ENABLED', env.MCC_IMAGE_ANALYSIS_ENABLED || '0'],
|
|
149
|
-
];
|
|
150
|
-
if (env.ANTHROPIC_DEFAULT_OPUS_MODEL !== env.ANTHROPIC_MODEL)
|
|
151
|
-
debugEnvs.push(['ANTHROPIC_DEFAULT_OPUS_MODEL', env.ANTHROPIC_DEFAULT_OPUS_MODEL]);
|
|
152
|
-
if (env.ANTHROPIC_DEFAULT_SONNET_MODEL !== env.ANTHROPIC_MODEL)
|
|
153
|
-
debugEnvs.push(['ANTHROPIC_DEFAULT_SONNET_MODEL', env.ANTHROPIC_DEFAULT_SONNET_MODEL]);
|
|
154
|
-
if (env.ANTHROPIC_DEFAULT_HAIKU_MODEL !== env.ANTHROPIC_MODEL)
|
|
155
|
-
debugEnvs.push(['ANTHROPIC_DEFAULT_HAIKU_MODEL', env.ANTHROPIC_DEFAULT_HAIKU_MODEL]);
|
|
156
|
-
|
|
157
|
-
console.log(` ${pc.dim('---')}`);
|
|
158
|
-
for (const [k, v] of debugEnvs) {
|
|
159
|
-
console.log(` ${pc.dim('env')} ${pc.dim(k)}=${pc.dim(v)}`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Start translation proxy for OpenAI-compatible profiles
|
|
164
|
-
if (profile.protocol === 'openai') {
|
|
165
|
-
try {
|
|
166
|
-
const proxyInfo = await startProxy(profileName, profile.baseUrl, apiKey, profile.model);
|
|
167
|
-
env.ANTHROPIC_BASE_URL = `http://127.0.0.1:${proxyInfo.port}`;
|
|
168
|
-
env.ANTHROPIC_AUTH_TOKEN = proxyInfo.authToken;
|
|
169
|
-
// MCP image analysis also needs to go through the proxy
|
|
170
|
-
env.MCC_IMAGE_ANALYSIS_RUNTIME_BASE_URL = `http://127.0.0.1:${proxyInfo.port}`;
|
|
171
|
-
env.MCC_IMAGE_ANALYSIS_RUNTIME_API_KEY = proxyInfo.authToken;
|
|
172
|
-
console.log(` ${pc.dim('proxy')} ${pc.cyan(`:${proxyInfo.port}`)}`);
|
|
173
|
-
} catch (e) {
|
|
174
|
-
console.error(`[!] Failed to start translation proxy: ${(e as Error).message}`);
|
|
175
|
-
process.exit(1);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
console.log(`\n ${pc.green('✓')} ${pc.bold('launching Claude Code')}`);
|
|
180
|
-
const remainingArgs = args.slice(1);
|
|
181
|
-
const child = spawn('claude', remainingArgs, {
|
|
182
|
-
env: { ...process.env, ...env },
|
|
183
|
-
stdio: 'inherit',
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
child.on('exit', (code) => process.exit(code ?? 0));
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async function cmdProfileAdd(args: string[]): Promise<void> {
|
|
190
|
-
const name = args[0];
|
|
191
|
-
if (!name) {
|
|
192
|
-
console.error('[!] Usage: mcc profile add <name> --base-url <url> --api-key <key> --model <model> [--protocol anthropic|openai] [--opus-model <m>] [--sonnet-model <m>] [--haiku-model <m>]');
|
|
193
|
-
process.exit(1);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const getArg = (flag: string) => {
|
|
197
|
-
const idx = args.indexOf(flag);
|
|
198
|
-
return idx !== -1 ? args[idx + 1] : undefined;
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
const baseUrl = getArg('--base-url');
|
|
202
|
-
const apiKey = getArg('--api-key');
|
|
203
|
-
const model = getArg('--model') ?? 'claude-sonnet-4-6';
|
|
204
|
-
const protocol = (getArg('--protocol') as 'anthropic' | 'openai') ?? 'anthropic';
|
|
205
|
-
|
|
206
|
-
if (!baseUrl || !apiKey) {
|
|
207
|
-
console.error('[!] --base-url and --api-key are required');
|
|
208
|
-
process.exit(1);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (protocol !== 'anthropic' && protocol !== 'openai') {
|
|
212
|
-
console.error('[!] --protocol must be "anthropic" or "openai"');
|
|
213
|
-
process.exit(1);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const profile: Profile = {
|
|
217
|
-
name,
|
|
218
|
-
baseUrl,
|
|
219
|
-
model,
|
|
220
|
-
opusModel: getArg('--opus-model'),
|
|
221
|
-
sonnetModel: getArg('--sonnet-model'),
|
|
222
|
-
haikuModel: getArg('--haiku-model'),
|
|
223
|
-
protocol,
|
|
224
|
-
createdAt: new Date().toISOString(),
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
saveProfile(profile, apiKey);
|
|
228
|
-
await instanceMgr.ensureInstance(name);
|
|
229
|
-
const instancePath = instanceMgr.getInstancePath(name);
|
|
230
|
-
syncInstanceMcpServers(instancePath, BUILTIN_MCP_SERVERS.map((s) => s.name), name);
|
|
231
|
-
|
|
232
|
-
console.log(`[OK] Profile created: ${name}`);
|
|
233
|
-
console.log(` Base URL: ${baseUrl}`);
|
|
234
|
-
console.log(` Model: ${model}`);
|
|
235
|
-
console.log(` Protocol: ${protocol}`);
|
|
236
|
-
if (profile.opusModel) console.log(` Opus: ${profile.opusModel}`);
|
|
237
|
-
if (profile.sonnetModel) console.log(` Sonnet: ${profile.sonnetModel}`);
|
|
238
|
-
if (profile.haikuModel) console.log(` Haiku: ${profile.haikuModel}`);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async function cmdProfileList(): Promise<void> {
|
|
242
|
-
const profiles = listProfiles();
|
|
243
|
-
const defaultProfile = getDefaultProfile();
|
|
244
|
-
|
|
245
|
-
if (profiles.length === 0) {
|
|
246
|
-
console.log('[i] No profiles. Run: mcc profile add <name> --base-url <url> --api-key <key> --model <model>');
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
console.log('Profiles:');
|
|
251
|
-
for (const p of profiles) {
|
|
252
|
-
const marker = p.name === defaultProfile ? ' (default)' : '';
|
|
253
|
-
console.log(` ${p.name}${marker}`);
|
|
254
|
-
console.log(` Base URL: ${p.baseUrl}`);
|
|
255
|
-
console.log(` Model: ${p.model}`);
|
|
256
|
-
console.log(` Protocol: ${p.protocol || 'anthropic'}`);
|
|
257
|
-
if (p.opusModel) console.log(` Opus: ${p.opusModel}`);
|
|
258
|
-
if (p.sonnetModel) console.log(` Sonnet: ${p.sonnetModel}`);
|
|
259
|
-
if (p.haikuModel) console.log(` Haiku: ${p.haikuModel}`);
|
|
260
|
-
console.log();
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
async function cmdProfileRemove(args: string[]): Promise<void> {
|
|
265
|
-
const name = args[0];
|
|
266
|
-
if (!name) {
|
|
267
|
-
console.error('[!] Usage: mcc profile remove <name>');
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
270
|
-
if (!hasProfile(name)) {
|
|
271
|
-
console.error(`[!] Profile not found: ${name}`);
|
|
272
|
-
process.exit(1);
|
|
273
|
-
}
|
|
274
|
-
deleteProfile(name);
|
|
275
|
-
await instanceMgr.deleteInstance(name);
|
|
276
|
-
console.log(`[OK] Profile removed: ${name}`);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
async function cmdProfileDefault(args: string[]): Promise<void> {
|
|
280
|
-
const name = args[0];
|
|
281
|
-
if (name) {
|
|
282
|
-
if (!hasProfile(name)) {
|
|
283
|
-
console.error(`[!] Profile not found: ${name}`);
|
|
284
|
-
process.exit(1);
|
|
285
|
-
}
|
|
286
|
-
setDefaultProfile(name);
|
|
287
|
-
console.log(`[OK] Default profile set: ${name}`);
|
|
288
|
-
} else {
|
|
289
|
-
const defaultProf = getDefaultProfile();
|
|
290
|
-
console.log(defaultProf ? `Default profile: ${defaultProf}` : '[i] No default profile set');
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
async function cmdMcpList(): Promise<void> {
|
|
295
|
-
const servers = getAllServers();
|
|
296
|
-
console.log('MCP Servers:');
|
|
297
|
-
for (const s of servers) {
|
|
298
|
-
const isBuiltin = 'config' in s;
|
|
299
|
-
const marker = isBuiltin && s.enabledByDefault ? ' (builtin, default)' : isBuiltin ? ' (builtin)' : '';
|
|
300
|
-
console.log(` ${s.name}${marker}`);
|
|
301
|
-
console.log(` ${s.displayName}: ${s.description}`);
|
|
302
|
-
if (!isBuiltin) {
|
|
303
|
-
const ext = s as ExternalMcpServer;
|
|
304
|
-
console.log(` Command: ${ext.command} ${ext.args.join(' ')}`);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
async function cmdMcpAdd(args: string[]): Promise<void> {
|
|
310
|
-
const getArg = (flag: string) => {
|
|
311
|
-
const idx = args.indexOf(flag);
|
|
312
|
-
return idx !== -1 ? args[idx + 1] : undefined;
|
|
313
|
-
};
|
|
314
|
-
const getBoolArg = (flag: string) => args.includes(flag);
|
|
315
|
-
|
|
316
|
-
const name = getArg('--name');
|
|
317
|
-
const displayName = getArg('--display-name') ?? name ?? '';
|
|
318
|
-
const description = getArg('--description') ?? '';
|
|
319
|
-
const command = getArg('--command') ?? 'uvx';
|
|
320
|
-
const argsStr = getArg('--args') ?? '';
|
|
321
|
-
const argsList = argsStr ? argsStr.split(',').map((s) => s.trim()) : [];
|
|
322
|
-
const providerRef = getArg('--provider-ref');
|
|
323
|
-
const enabledByDefault = getBoolArg('--enabled-by-default');
|
|
324
|
-
|
|
325
|
-
if (!name || !command) {
|
|
326
|
-
console.error('[!] --name and --command are required');
|
|
327
|
-
console.error(' Usage: mcc mcp add --name <id> --command <cmd> --args "<arg1>,<arg2>" [--display-name <name>] [--description <desc>] [--provider-ref <provider>] [--enabled-by-default]');
|
|
328
|
-
process.exit(1);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const envVars: Record<string, string> = {};
|
|
332
|
-
if (providerRef) {
|
|
333
|
-
envVars.MINIMAX_API_KEY = `\${MCC_PROVIDER_KEY:${providerRef}}`;
|
|
334
|
-
envVars.MINIMAX_API_HOST = 'https://api.minimaxi.com';
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const server: ExternalMcpServer = {
|
|
338
|
-
name,
|
|
339
|
-
displayName,
|
|
340
|
-
description,
|
|
341
|
-
command,
|
|
342
|
-
args: argsList,
|
|
343
|
-
envVars,
|
|
344
|
-
enabledByDefault,
|
|
345
|
-
};
|
|
346
|
-
addExternalMcpServer(server);
|
|
347
|
-
console.log(`[OK] External MCP added: ${name}`);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
async function cmdMcpRemove(args: string[]): Promise<void> {
|
|
351
|
-
const name = args[0];
|
|
352
|
-
if (!name) {
|
|
353
|
-
console.error('[!] Usage: mcc mcp remove <name>');
|
|
354
|
-
process.exit(1);
|
|
355
|
-
}
|
|
356
|
-
const existing = readExternalMcpRegistry().find((s) => s.name === name);
|
|
357
|
-
if (!existing) {
|
|
358
|
-
console.error(`[!] External MCP not found: ${name}`);
|
|
359
|
-
process.exit(1);
|
|
360
|
-
}
|
|
361
|
-
removeExternalMcpServer(name);
|
|
362
|
-
console.log(`[OK] External MCP removed: ${name}`);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
async function cmdMcpEnable(args: string[]): Promise<void> {
|
|
366
|
-
const name = args[0];
|
|
367
|
-
const profileName = args[1];
|
|
368
|
-
if (!name || !profileName) {
|
|
369
|
-
console.error('[!] Usage: mcc mcp enable <name> <profile>');
|
|
370
|
-
process.exit(1);
|
|
371
|
-
}
|
|
372
|
-
if (!hasProfile(profileName)) {
|
|
373
|
-
console.error(`[!] Profile not found: ${profileName}`);
|
|
374
|
-
process.exit(1);
|
|
375
|
-
}
|
|
376
|
-
const instancePath = instanceMgr.getInstancePath(profileName);
|
|
377
|
-
enableInstanceExternalMcp(instancePath, name);
|
|
378
|
-
syncInstanceMcpServers(instancePath, BUILTIN_MCP_SERVERS.map((s) => s.name), profileName);
|
|
379
|
-
console.log(`[OK] External MCP '${name}' enabled for profile '${profileName}'`);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
async function cmdMcpDisable(args: string[]): Promise<void> {
|
|
383
|
-
const name = args[0];
|
|
384
|
-
const profileName = args[1];
|
|
385
|
-
if (!name || !profileName) {
|
|
386
|
-
console.error('[!] Usage: mcc mcp disable <name> <profile>');
|
|
387
|
-
process.exit(1);
|
|
388
|
-
}
|
|
389
|
-
if (!hasProfile(profileName)) {
|
|
390
|
-
console.error(`[!] Profile not found: ${profileName}`);
|
|
391
|
-
process.exit(1);
|
|
392
|
-
}
|
|
393
|
-
const instancePath = instanceMgr.getInstancePath(profileName);
|
|
394
|
-
disableInstanceExternalMcp(instancePath, name);
|
|
395
|
-
syncInstanceMcpServers(instancePath, BUILTIN_MCP_SERVERS.map((s) => s.name), profileName);
|
|
396
|
-
console.log(`[OK] External MCP '${name}' disabled for profile '${profileName}'`);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
async function main(): Promise<void> {
|
|
400
|
-
const rawArgs = process.argv.slice(2);
|
|
401
|
-
|
|
402
|
-
// Global flags — always intercepted regardless of position
|
|
403
|
-
// NOTE: -v/-h are intentionally NOT subcommand-aware for simplicity
|
|
404
|
-
if (rawArgs[0] === '-h' || rawArgs[0] === '--help') {
|
|
405
|
-
showHelp();
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
if (rawArgs[0] === '-v' || rawArgs[0] === '--version') {
|
|
409
|
-
showVersion();
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// No args: show help
|
|
414
|
-
if (rawArgs.length === 0) {
|
|
415
|
-
showHelp();
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Parse: mcc <profile> [claude-args...]
|
|
420
|
-
// Explicit separator: mcc <profile> -- <claude-args...>
|
|
421
|
-
// Everything after first arg (the profile) is passed to Claude as-is.
|
|
422
|
-
const dashIdx = rawArgs.indexOf('--');
|
|
423
|
-
const profileArgs = dashIdx === -1 ? rawArgs : rawArgs.slice(0, dashIdx);
|
|
424
|
-
const claudeArgs = dashIdx === -1 ? [] : rawArgs.slice(dashIdx); // includes '--'
|
|
425
|
-
|
|
426
|
-
if (profileArgs.length === 0) {
|
|
427
|
-
showHelp();
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
installBuiltinServers();
|
|
432
|
-
|
|
433
|
-
const command = profileArgs[0];
|
|
434
|
-
const args = profileArgs.slice(1);
|
|
435
|
-
|
|
436
|
-
switch (command) {
|
|
437
|
-
// mcc <profile> - launch with profile
|
|
438
|
-
case 'profile':
|
|
439
|
-
switch (args[0]) {
|
|
440
|
-
case 'add': await cmdProfileAdd(args.slice(1)); break;
|
|
441
|
-
case 'list': await cmdProfileList(); break;
|
|
442
|
-
case 'remove': await cmdProfileRemove(args.slice(1)); break;
|
|
443
|
-
case 'default': await cmdProfileDefault(args.slice(1)); break;
|
|
444
|
-
default:
|
|
445
|
-
console.error(`[!] Unknown profile command: ${args[0]}`);
|
|
446
|
-
showHelp();
|
|
447
|
-
process.exit(1);
|
|
448
|
-
}
|
|
449
|
-
break;
|
|
450
|
-
case 'dashboard': {
|
|
451
|
-
spawn('node', [path.join(__dirname, 'dashboard-server.js')], {
|
|
452
|
-
env: process.env,
|
|
453
|
-
stdio: 'inherit',
|
|
454
|
-
});
|
|
455
|
-
break;
|
|
456
|
-
}
|
|
457
|
-
case 'mcp': {
|
|
458
|
-
const sub = args[0];
|
|
459
|
-
const subArgs = args.slice(1);
|
|
460
|
-
switch (sub) {
|
|
461
|
-
case 'list': await cmdMcpList(); break;
|
|
462
|
-
case 'add': await cmdMcpAdd(subArgs); break;
|
|
463
|
-
case 'remove': await cmdMcpRemove(subArgs); break;
|
|
464
|
-
case 'enable': await cmdMcpEnable(subArgs); break;
|
|
465
|
-
case 'disable': await cmdMcpDisable(subArgs); break;
|
|
466
|
-
default:
|
|
467
|
-
console.error(`[!] Unknown mcp command: ${sub}`);
|
|
468
|
-
showHelp();
|
|
469
|
-
process.exit(1);
|
|
470
|
-
}
|
|
471
|
-
break;
|
|
472
|
-
}
|
|
473
|
-
default:
|
|
474
|
-
// Treat as profile launch; pass profile name + claude args
|
|
475
|
-
await cmdLaunch([command, ...args, ...claudeArgs]);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
main().catch((err) => {
|
|
480
|
-
console.error(`[!] Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
481
|
-
process.exit(1);
|
|
482
|
-
});
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External MCP Server Registry
|
|
3
|
-
*
|
|
4
|
-
* Stores user-added MCP server definitions in ~/.mcc/external-mcp-servers.json.
|
|
5
|
-
* These are MCP servers not bundled with MCC (e.g. MiniMax Token Plan MCP).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from 'fs';
|
|
9
|
-
import * as path from 'path';
|
|
10
|
-
import type { McpRegistryEntry } from './registry';
|
|
11
|
-
|
|
12
|
-
export interface ExternalMcpServer {
|
|
13
|
-
name: string; // unique identifier, e.g. "minimax-plan"
|
|
14
|
-
displayName: string;
|
|
15
|
-
description: string;
|
|
16
|
-
command: string; // e.g. "uvx"
|
|
17
|
-
args: string[]; // e.g. ["minimax-coding-plan-mcp", "-y"]
|
|
18
|
-
envVars: Record<string, string>; // e.g. { MINIMAX_API_KEY: "${MCC_PROVIDER_KEY:minimax}" }
|
|
19
|
-
enabledByDefault: boolean;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getMccHome(): string {
|
|
23
|
-
return process.env.MCC_HOME ?? path.join(process.env.HOME ?? process.env.USERPROFILE ?? '~', '.mcc');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function getExternalMcpRegistryPath(): string {
|
|
27
|
-
return path.join(getMccHome(), 'external-mcp-servers.json');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function readExternalMcpRegistry(): ExternalMcpServer[] {
|
|
31
|
-
const filePath = getExternalMcpRegistryPath();
|
|
32
|
-
if (!fs.existsSync(filePath)) return [];
|
|
33
|
-
try {
|
|
34
|
-
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
35
|
-
} catch {
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function writeExternalMcpRegistry(servers: ExternalMcpServer[]): void {
|
|
41
|
-
const filePath = getExternalMcpRegistryPath();
|
|
42
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
|
|
43
|
-
fs.writeFileSync(filePath, JSON.stringify(servers, null, 2) + '\n', {
|
|
44
|
-
encoding: 'utf8',
|
|
45
|
-
mode: 0o600,
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function getExternalMcpServer(name: string): ExternalMcpServer | undefined {
|
|
50
|
-
return readExternalMcpRegistry().find((s) => s.name === name);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function addExternalMcpServer(server: ExternalMcpServer): void {
|
|
54
|
-
const servers = readExternalMcpRegistry();
|
|
55
|
-
const idx = servers.findIndex((s) => s.name === server.name);
|
|
56
|
-
if (idx >= 0) {
|
|
57
|
-
servers[idx] = server;
|
|
58
|
-
} else {
|
|
59
|
-
servers.push(server);
|
|
60
|
-
}
|
|
61
|
-
writeExternalMcpRegistry(servers);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function removeExternalMcpServer(name: string): void {
|
|
65
|
-
const servers = readExternalMcpRegistry().filter((s) => s.name !== name);
|
|
66
|
-
writeExternalMcpRegistry(servers);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function getAllServers(): Array<McpRegistryEntry | ExternalMcpServer> {
|
|
70
|
-
// Imported here to avoid circular dependency
|
|
71
|
-
const { BUILTIN_MCP_SERVERS } = require('./registry');
|
|
72
|
-
return [...BUILTIN_MCP_SERVERS, ...readExternalMcpRegistry()];
|
|
73
|
-
}
|