chainlesschain 0.47.8 → 0.49.0
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/bin/chainlesschain.js +0 -0
- package/package.json +10 -8
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
- package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
- package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/activitypub.js +533 -0
- package/src/commands/codegen.js +303 -0
- package/src/commands/collab.js +482 -0
- package/src/commands/compliance.js +597 -6
- package/src/commands/crosschain.js +382 -0
- package/src/commands/dbevo.js +388 -0
- package/src/commands/dev.js +411 -0
- package/src/commands/federation.js +427 -0
- package/src/commands/fusion.js +332 -0
- package/src/commands/governance.js +505 -0
- package/src/commands/hardening.js +110 -0
- package/src/commands/incentive.js +373 -0
- package/src/commands/inference.js +304 -0
- package/src/commands/infra.js +361 -0
- package/src/commands/kg.js +371 -0
- package/src/commands/marketplace.js +326 -0
- package/src/commands/matrix.js +283 -0
- package/src/commands/mcp.js +441 -18
- package/src/commands/nlprog.js +329 -0
- package/src/commands/nostr.js +196 -7
- package/src/commands/ops.js +408 -0
- package/src/commands/perception.js +385 -0
- package/src/commands/pqc.js +34 -0
- package/src/commands/privacy.js +345 -0
- package/src/commands/quantization.js +280 -0
- package/src/commands/recommend.js +336 -0
- package/src/commands/reputation.js +349 -0
- package/src/commands/runtime.js +500 -0
- package/src/commands/sla.js +352 -0
- package/src/commands/social.js +265 -0
- package/src/commands/stress.js +252 -0
- package/src/commands/tech.js +268 -0
- package/src/commands/tenant.js +576 -0
- package/src/commands/trust.js +366 -0
- package/src/harness/mcp-client.js +330 -54
- package/src/index.js +114 -0
- package/src/lib/activitypub-bridge.js +623 -0
- package/src/lib/aiops.js +523 -0
- package/src/lib/autonomous-developer.js +524 -0
- package/src/lib/code-agent.js +442 -0
- package/src/lib/collaboration-governance.js +556 -0
- package/src/lib/community-governance.js +649 -0
- package/src/lib/compliance-framework-reporter.js +600 -0
- package/src/lib/content-recommendation.js +600 -0
- package/src/lib/cross-chain.js +669 -0
- package/src/lib/dbevo.js +669 -0
- package/src/lib/decentral-infra.js +445 -0
- package/src/lib/federation-hardening.js +587 -0
- package/src/lib/hardening-manager.js +409 -0
- package/src/lib/inference-network.js +407 -0
- package/src/lib/knowledge-graph.js +530 -0
- package/src/lib/matrix-bridge.js +252 -0
- package/src/lib/mcp-client.js +3 -0
- package/src/lib/mcp-registry.js +347 -0
- package/src/lib/mcp-scaffold.js +385 -0
- package/src/lib/multimodal.js +698 -0
- package/src/lib/nl-programming.js +595 -0
- package/src/lib/nostr-bridge.js +214 -38
- package/src/lib/perception.js +500 -0
- package/src/lib/pqc-manager.js +141 -9
- package/src/lib/privacy-computing.js +575 -0
- package/src/lib/protocol-fusion.js +535 -0
- package/src/lib/quantization.js +362 -0
- package/src/lib/reputation-optimizer.js +509 -0
- package/src/lib/skill-marketplace.js +397 -0
- package/src/lib/sla-manager.js +484 -0
- package/src/lib/social-graph.js +408 -0
- package/src/lib/stix-parser.js +167 -0
- package/src/lib/stress-tester.js +383 -0
- package/src/lib/tech-learning-engine.js +651 -0
- package/src/lib/tenant-saas.js +831 -0
- package/src/lib/threat-intel.js +268 -0
- package/src/lib/token-incentive.js +513 -0
- package/src/lib/topic-classifier.js +400 -0
- package/src/lib/trust-security.js +473 -0
- package/src/lib/ueba.js +403 -0
- package/src/lib/universal-runtime.js +771 -0
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
package/src/commands/mcp.js
CHANGED
|
@@ -3,15 +3,32 @@
|
|
|
3
3
|
* chainlesschain mcp servers|connect|disconnect|tools|call
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import fs from "fs";
|
|
7
|
+
import path from "path";
|
|
6
8
|
import chalk from "chalk";
|
|
7
9
|
import ora from "ora";
|
|
8
10
|
import { logger } from "../lib/logger.js";
|
|
9
11
|
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
10
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
MCPClient,
|
|
14
|
+
MCPServerConfig,
|
|
15
|
+
inferTransport,
|
|
16
|
+
} from "../harness/mcp-client.js";
|
|
11
17
|
import {
|
|
12
18
|
validateMcpServer,
|
|
13
19
|
annotateMcpCompatibility,
|
|
14
20
|
} from "@chainlesschain/session-core";
|
|
21
|
+
import {
|
|
22
|
+
generateMcpServerScaffold,
|
|
23
|
+
SUPPORTED_TRANSPORTS,
|
|
24
|
+
} from "../lib/mcp-scaffold.js";
|
|
25
|
+
import {
|
|
26
|
+
CATALOG as REGISTRY_CATALOG,
|
|
27
|
+
CATEGORIES as REGISTRY_CATEGORIES,
|
|
28
|
+
listServers as registryListServers,
|
|
29
|
+
searchServers as registrySearchServers,
|
|
30
|
+
getServer as registryGetServer,
|
|
31
|
+
} from "../lib/mcp-registry.js";
|
|
15
32
|
|
|
16
33
|
// Singleton MCP client for session reuse
|
|
17
34
|
let mcpClient = null;
|
|
@@ -98,9 +115,15 @@ export function registerMcpCommand(program) {
|
|
|
98
115
|
? chalk.green(" [ok]")
|
|
99
116
|
: chalk.yellow(` [blocked: ${s._reason}]`);
|
|
100
117
|
logger.log(` ${chalk.cyan(s.name)}${auto}${flag}`);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
118
|
+
if (s.url) {
|
|
119
|
+
logger.log(
|
|
120
|
+
` ${chalk.gray("URL:")} ${s.url} ${chalk.gray(`[${s.transport || s._transport || "http"}]`)}`,
|
|
121
|
+
);
|
|
122
|
+
} else {
|
|
123
|
+
logger.log(
|
|
124
|
+
` ${chalk.gray("Command:")} ${s.command} ${s.args.join(" ")}`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
104
127
|
logger.log(
|
|
105
128
|
` ${chalk.gray("Compatible:")} ${s._modeCompatibility.join(", ") || "(none)"}`,
|
|
106
129
|
);
|
|
@@ -119,8 +142,20 @@ export function registerMcpCommand(program) {
|
|
|
119
142
|
.command("add")
|
|
120
143
|
.description("Add or update an MCP server configuration")
|
|
121
144
|
.argument("<name>", "Server name")
|
|
122
|
-
.
|
|
145
|
+
.option("-c, --command <cmd>", "Server command to run (stdio transport)")
|
|
123
146
|
.option("-a, --args <args>", "Command arguments (comma-separated)")
|
|
147
|
+
.option(
|
|
148
|
+
"-u, --url <url>",
|
|
149
|
+
"Server URL (http / https / ws / wss transports)",
|
|
150
|
+
)
|
|
151
|
+
.option(
|
|
152
|
+
"-t, --transport <kind>",
|
|
153
|
+
"Transport kind: stdio | http | https | sse | ws | wss",
|
|
154
|
+
)
|
|
155
|
+
.option(
|
|
156
|
+
"-H, --header <header...>",
|
|
157
|
+
"HTTP header to include on requests (KEY=VALUE, repeatable)",
|
|
158
|
+
)
|
|
124
159
|
.option("--auto-connect", "Auto-connect on startup")
|
|
125
160
|
.option(
|
|
126
161
|
"--mode <mode>",
|
|
@@ -129,6 +164,17 @@ export function registerMcpCommand(program) {
|
|
|
129
164
|
.option("--json", "Output as JSON")
|
|
130
165
|
.action(async (name, options) => {
|
|
131
166
|
try {
|
|
167
|
+
if (!options.command && !options.url) {
|
|
168
|
+
logger.error(
|
|
169
|
+
"Provide either -c <command> (stdio) or -u <url> (http/ws).",
|
|
170
|
+
);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
if (options.command && options.url) {
|
|
174
|
+
logger.error("Use either -c <command> or -u <url>, not both.");
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
|
|
132
178
|
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
133
179
|
if (!ctx.db) {
|
|
134
180
|
logger.error("Database not available");
|
|
@@ -141,9 +187,43 @@ export function registerMcpCommand(program) {
|
|
|
141
187
|
? options.args.split(",").map((a) => a.trim())
|
|
142
188
|
: [];
|
|
143
189
|
|
|
190
|
+
// Parse -H KEY=VALUE into { headers }
|
|
191
|
+
const headers = {};
|
|
192
|
+
if (Array.isArray(options.header)) {
|
|
193
|
+
for (const raw of options.header) {
|
|
194
|
+
const eq = raw.indexOf("=");
|
|
195
|
+
if (eq <= 0) {
|
|
196
|
+
logger.warn(
|
|
197
|
+
`Ignored malformed --header "${raw}" (expected KEY=VALUE)`,
|
|
198
|
+
);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
headers[raw.slice(0, eq).trim()] = raw.slice(eq + 1);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const transport =
|
|
206
|
+
options.transport ||
|
|
207
|
+
(options.url ? inferTransport({ url: options.url }) : "stdio");
|
|
208
|
+
|
|
209
|
+
if (options.url) {
|
|
210
|
+
try {
|
|
211
|
+
new URL(options.url);
|
|
212
|
+
} catch (_e) {
|
|
213
|
+
logger.error(`Invalid URL: ${options.url}`);
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
144
218
|
const mode = resolveMode(options);
|
|
145
219
|
const check = validateMcpServer(
|
|
146
|
-
{
|
|
220
|
+
{
|
|
221
|
+
name,
|
|
222
|
+
command: options.command,
|
|
223
|
+
args,
|
|
224
|
+
url: options.url,
|
|
225
|
+
transport,
|
|
226
|
+
},
|
|
147
227
|
mode,
|
|
148
228
|
);
|
|
149
229
|
if (!check.allowed) {
|
|
@@ -153,25 +233,37 @@ export function registerMcpCommand(program) {
|
|
|
153
233
|
}
|
|
154
234
|
|
|
155
235
|
config.add(name, {
|
|
156
|
-
command: options.command,
|
|
236
|
+
command: options.command || null,
|
|
157
237
|
args,
|
|
238
|
+
url: options.url || null,
|
|
239
|
+
transport,
|
|
240
|
+
env: {},
|
|
158
241
|
autoConnect: !!options.autoConnect,
|
|
242
|
+
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
159
243
|
});
|
|
160
244
|
|
|
245
|
+
const payload = {
|
|
246
|
+
name,
|
|
247
|
+
command: options.command || null,
|
|
248
|
+
args,
|
|
249
|
+
url: options.url || null,
|
|
250
|
+
transport,
|
|
251
|
+
autoConnect: !!options.autoConnect,
|
|
252
|
+
};
|
|
253
|
+
|
|
161
254
|
if (options.json) {
|
|
162
|
-
console.log(
|
|
163
|
-
JSON.stringify({
|
|
164
|
-
name,
|
|
165
|
-
command: options.command,
|
|
166
|
-
args,
|
|
167
|
-
autoConnect: !!options.autoConnect,
|
|
168
|
-
}),
|
|
169
|
-
);
|
|
255
|
+
console.log(JSON.stringify(payload));
|
|
170
256
|
} else {
|
|
171
257
|
logger.success(`MCP server "${chalk.cyan(name)}" configured`);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
258
|
+
if (options.url) {
|
|
259
|
+
logger.log(
|
|
260
|
+
` ${chalk.gray("URL:")} ${options.url} ${chalk.gray(`[${transport}]`)}`,
|
|
261
|
+
);
|
|
262
|
+
} else {
|
|
263
|
+
logger.log(
|
|
264
|
+
` ${chalk.gray("Command:")} ${options.command} ${args.join(" ")}`,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
175
267
|
}
|
|
176
268
|
|
|
177
269
|
await shutdown();
|
|
@@ -381,4 +473,335 @@ export function registerMcpCommand(program) {
|
|
|
381
473
|
process.exit(1);
|
|
382
474
|
}
|
|
383
475
|
});
|
|
476
|
+
|
|
477
|
+
// mcp scaffold — generate a boilerplate MCP server project
|
|
478
|
+
mcp
|
|
479
|
+
.command("scaffold <name>")
|
|
480
|
+
.description("Scaffold a new MCP server project (stdio or http+sse)")
|
|
481
|
+
.option("-d, --description <text>", "Short description of the server")
|
|
482
|
+
.option(
|
|
483
|
+
"-t, --transport <kind>",
|
|
484
|
+
`Transport: ${SUPPORTED_TRANSPORTS.join("|")}`,
|
|
485
|
+
"stdio",
|
|
486
|
+
)
|
|
487
|
+
.option("-o, --output <dir>", "Target directory (defaults to ./<name>)")
|
|
488
|
+
.option("-a, --author <name>", "package.json author field")
|
|
489
|
+
.option("-p, --port <n>", "HTTP port (http transport only)", (v) =>
|
|
490
|
+
parseInt(v, 10),
|
|
491
|
+
)
|
|
492
|
+
.option("--force", "Overwrite existing files")
|
|
493
|
+
.option("--dry-run", "Print files that would be written, don't touch disk")
|
|
494
|
+
.option("--json", "Output as JSON")
|
|
495
|
+
.action(async (name, options) => {
|
|
496
|
+
try {
|
|
497
|
+
const { files, summary } = generateMcpServerScaffold({
|
|
498
|
+
name,
|
|
499
|
+
description: options.description,
|
|
500
|
+
transport: options.transport,
|
|
501
|
+
author: options.author,
|
|
502
|
+
port: options.port,
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
const targetDir = path.resolve(options.output || `./${summary.name}`);
|
|
506
|
+
|
|
507
|
+
if (options.dryRun) {
|
|
508
|
+
if (options.json) {
|
|
509
|
+
console.log(JSON.stringify({ targetDir, summary, files }, null, 2));
|
|
510
|
+
} else {
|
|
511
|
+
logger.log(
|
|
512
|
+
`${chalk.bold("Would write")} ${files.length} files to ${chalk.cyan(targetDir)}:`,
|
|
513
|
+
);
|
|
514
|
+
for (const f of files) {
|
|
515
|
+
logger.log(
|
|
516
|
+
` ${chalk.cyan(f.path)} ` +
|
|
517
|
+
chalk.dim(`(${f.content.length} bytes)`),
|
|
518
|
+
);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Collision check — refuse to clobber unless --force.
|
|
525
|
+
if (!options.force && fs.existsSync(targetDir)) {
|
|
526
|
+
const clashing = files.filter((f) =>
|
|
527
|
+
fs.existsSync(path.join(targetDir, f.path)),
|
|
528
|
+
);
|
|
529
|
+
if (clashing.length > 0) {
|
|
530
|
+
logger.error(
|
|
531
|
+
`Refusing to overwrite existing files in ${targetDir}: ` +
|
|
532
|
+
clashing.map((f) => f.path).join(", ") +
|
|
533
|
+
`. Re-run with --force to overwrite.`,
|
|
534
|
+
);
|
|
535
|
+
process.exit(1);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
540
|
+
for (const f of files) {
|
|
541
|
+
const full = path.join(targetDir, f.path);
|
|
542
|
+
fs.mkdirSync(path.dirname(full), { recursive: true });
|
|
543
|
+
fs.writeFileSync(full, f.content, "utf-8");
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (options.json) {
|
|
547
|
+
console.log(
|
|
548
|
+
JSON.stringify(
|
|
549
|
+
{
|
|
550
|
+
targetDir,
|
|
551
|
+
summary,
|
|
552
|
+
files: files.map((f) => f.path),
|
|
553
|
+
},
|
|
554
|
+
null,
|
|
555
|
+
2,
|
|
556
|
+
),
|
|
557
|
+
);
|
|
558
|
+
} else {
|
|
559
|
+
logger.success(
|
|
560
|
+
`Scaffolded ${chalk.cyan(summary.name)} ` +
|
|
561
|
+
chalk.dim(
|
|
562
|
+
`(${summary.transport}${summary.port ? `, port ${summary.port}` : ""})`,
|
|
563
|
+
),
|
|
564
|
+
);
|
|
565
|
+
logger.log(` ${chalk.bold("Path:")} ${targetDir}`);
|
|
566
|
+
for (const f of files) {
|
|
567
|
+
logger.log(` ${chalk.dim("+")} ${f.path}`);
|
|
568
|
+
}
|
|
569
|
+
logger.log("");
|
|
570
|
+
logger.log(chalk.bold("Next steps:"));
|
|
571
|
+
logger.log(
|
|
572
|
+
` ${chalk.dim("$")} cd ${path.relative(process.cwd(), targetDir) || "."}`,
|
|
573
|
+
);
|
|
574
|
+
logger.log(` ${chalk.dim("$")} npm install`);
|
|
575
|
+
if (summary.transport === "stdio") {
|
|
576
|
+
logger.log(
|
|
577
|
+
` ${chalk.dim("$")} cc mcp add ${summary.name} -c node -a "./index.js"`,
|
|
578
|
+
);
|
|
579
|
+
} else {
|
|
580
|
+
logger.log(` ${chalk.dim("$")} npm start`);
|
|
581
|
+
logger.log(
|
|
582
|
+
` ${chalk.dim("$")} cc mcp add ${summary.name} -u http://localhost:${summary.port}/mcp`,
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
} catch (err) {
|
|
587
|
+
logger.error(`Scaffold failed: ${err.message}`);
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// mcp registry — browse + search + one-shot install from the bundled catalog
|
|
593
|
+
const registry = mcp
|
|
594
|
+
.command("registry")
|
|
595
|
+
.description("Browse the curated catalog of community MCP servers");
|
|
596
|
+
|
|
597
|
+
registry
|
|
598
|
+
.command("list")
|
|
599
|
+
.description("List catalog entries (filter by category/tag/author)")
|
|
600
|
+
.option("-c, --category <name>", "Filter by category")
|
|
601
|
+
.option("-t, --tags <list>", "Filter by comma-separated tags (any match)")
|
|
602
|
+
.option("--author <name>", "Filter by author (substring, case-insensitive)")
|
|
603
|
+
.option("--sort <field>", "Sort by 'name' | 'rating' | 'category'", "name")
|
|
604
|
+
.option("--order <dir>", "'asc' | 'desc'", "asc")
|
|
605
|
+
.option("--limit <n>", "Max results", (v) => parseInt(v, 10))
|
|
606
|
+
.option("--offset <n>", "Pagination offset", (v) => parseInt(v, 10))
|
|
607
|
+
.option("--json", "Output as JSON")
|
|
608
|
+
.action((options) => {
|
|
609
|
+
try {
|
|
610
|
+
const tags = options.tags
|
|
611
|
+
? options.tags
|
|
612
|
+
.split(",")
|
|
613
|
+
.map((t) => t.trim())
|
|
614
|
+
.filter(Boolean)
|
|
615
|
+
: undefined;
|
|
616
|
+
const { servers, total } = registryListServers({
|
|
617
|
+
category: options.category,
|
|
618
|
+
tags,
|
|
619
|
+
author: options.author,
|
|
620
|
+
sortBy: options.sort,
|
|
621
|
+
sortOrder: options.order,
|
|
622
|
+
limit: options.limit,
|
|
623
|
+
offset: options.offset,
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
if (options.json) {
|
|
627
|
+
console.log(JSON.stringify({ servers, total }, null, 2));
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
if (servers.length === 0) {
|
|
632
|
+
logger.info("No catalog entries match your filters.");
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
logger.log(
|
|
637
|
+
chalk.bold(`MCP Registry — ${servers.length}/${total} servers\n`),
|
|
638
|
+
);
|
|
639
|
+
for (const s of servers) {
|
|
640
|
+
logger.log(
|
|
641
|
+
` ${chalk.cyan(s.name)} ${chalk.gray(`(${s.id})`)} ` +
|
|
642
|
+
chalk.dim(`★${s.rating ?? "-"} ${s.category}`),
|
|
643
|
+
);
|
|
644
|
+
logger.log(` ${chalk.gray(s.description)}`);
|
|
645
|
+
if (s.tags?.length) {
|
|
646
|
+
logger.log(
|
|
647
|
+
` ${chalk.gray("tags:")} ${s.tags.slice(0, 5).join(", ")}`,
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
} catch (err) {
|
|
652
|
+
logger.error(`Registry list failed: ${err.message}`);
|
|
653
|
+
process.exit(1);
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
registry
|
|
658
|
+
.command("search <keyword>")
|
|
659
|
+
.description("Keyword search across name/description/tags")
|
|
660
|
+
.option("--json", "Output as JSON")
|
|
661
|
+
.action((keyword, options) => {
|
|
662
|
+
try {
|
|
663
|
+
const hits = registrySearchServers(keyword);
|
|
664
|
+
if (options.json) {
|
|
665
|
+
console.log(JSON.stringify({ hits, total: hits.length }, null, 2));
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
if (hits.length === 0) {
|
|
669
|
+
logger.info(`No matches for "${keyword}".`);
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
logger.log(chalk.bold(`${hits.length} matches for "${keyword}":\n`));
|
|
673
|
+
for (const s of hits) {
|
|
674
|
+
logger.log(
|
|
675
|
+
` ${chalk.cyan(s.name)} ${chalk.gray(`(${s.id})`)} ` +
|
|
676
|
+
chalk.dim(s.category),
|
|
677
|
+
);
|
|
678
|
+
logger.log(` ${chalk.gray(s.description)}`);
|
|
679
|
+
}
|
|
680
|
+
} catch (err) {
|
|
681
|
+
logger.error(`Registry search failed: ${err.message}`);
|
|
682
|
+
process.exit(1);
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
registry
|
|
687
|
+
.command("show <idOrName>")
|
|
688
|
+
.description("Show full catalog entry (id or short name)")
|
|
689
|
+
.option("--json", "Output as JSON")
|
|
690
|
+
.action((idOrName, options) => {
|
|
691
|
+
try {
|
|
692
|
+
const entry = registryGetServer(idOrName);
|
|
693
|
+
if (!entry) {
|
|
694
|
+
logger.error(`Not found: "${idOrName}".`);
|
|
695
|
+
process.exit(1);
|
|
696
|
+
}
|
|
697
|
+
if (options.json) {
|
|
698
|
+
console.log(JSON.stringify(entry, null, 2));
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
logger.log(
|
|
702
|
+
`${chalk.bold(entry.displayName)} ${chalk.gray(`(${entry.id})`)}`,
|
|
703
|
+
);
|
|
704
|
+
logger.log(` ${chalk.gray("Author:")} ${entry.author}`);
|
|
705
|
+
logger.log(` ${chalk.gray("Category:")} ${entry.category}`);
|
|
706
|
+
logger.log(` ${chalk.gray("Version:")} ${entry.version}`);
|
|
707
|
+
logger.log(` ${chalk.gray("Rating:")} ★${entry.rating ?? "-"}`);
|
|
708
|
+
logger.log(` ${chalk.gray("Package:")} ${entry.npmPackage}`);
|
|
709
|
+
logger.log(
|
|
710
|
+
` ${chalk.gray("Command:")} ${entry.command} ${entry.args.join(" ")}`,
|
|
711
|
+
);
|
|
712
|
+
logger.log(` ${chalk.gray("Transport:")}${entry.transport}`);
|
|
713
|
+
if (entry.homepage) {
|
|
714
|
+
logger.log(` ${chalk.gray("Homepage:")} ${entry.homepage}`);
|
|
715
|
+
}
|
|
716
|
+
logger.log(`\n ${entry.description}`);
|
|
717
|
+
if (entry.tools?.length) {
|
|
718
|
+
logger.log(`\n ${chalk.bold("Tools:")} ${entry.tools.join(", ")}`);
|
|
719
|
+
}
|
|
720
|
+
logger.log(
|
|
721
|
+
`\n ${chalk.dim("Install:")} cc mcp registry install ${entry.name}`,
|
|
722
|
+
);
|
|
723
|
+
} catch (err) {
|
|
724
|
+
logger.error(`Registry show failed: ${err.message}`);
|
|
725
|
+
process.exit(1);
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
registry
|
|
730
|
+
.command("install <idOrName>")
|
|
731
|
+
.description("Install a catalog entry by registering it as an MCP server")
|
|
732
|
+
.option("--as <name>", "Override the stored server name")
|
|
733
|
+
.option("--auto-connect", "Mark the server to auto-connect on startup")
|
|
734
|
+
.option("--json", "Output as JSON")
|
|
735
|
+
.action(async (idOrName, options) => {
|
|
736
|
+
try {
|
|
737
|
+
const entry = registryGetServer(idOrName);
|
|
738
|
+
if (!entry) {
|
|
739
|
+
logger.error(
|
|
740
|
+
`Not found: "${idOrName}". Try 'cc mcp registry list' or 'search'.`,
|
|
741
|
+
);
|
|
742
|
+
process.exit(1);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
746
|
+
if (!ctx.db) {
|
|
747
|
+
logger.error("Database not available");
|
|
748
|
+
process.exit(1);
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
const db = ctx.db.getDatabase();
|
|
752
|
+
const config = new MCPServerConfig(db);
|
|
753
|
+
const storedName = (options.as || entry.name).trim();
|
|
754
|
+
|
|
755
|
+
config.add(storedName, {
|
|
756
|
+
command: entry.command,
|
|
757
|
+
args: entry.args,
|
|
758
|
+
autoConnect: !!options.autoConnect,
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
if (options.json) {
|
|
762
|
+
console.log(
|
|
763
|
+
JSON.stringify(
|
|
764
|
+
{
|
|
765
|
+
name: storedName,
|
|
766
|
+
id: entry.id,
|
|
767
|
+
command: entry.command,
|
|
768
|
+
args: entry.args,
|
|
769
|
+
autoConnect: !!options.autoConnect,
|
|
770
|
+
},
|
|
771
|
+
null,
|
|
772
|
+
2,
|
|
773
|
+
),
|
|
774
|
+
);
|
|
775
|
+
} else {
|
|
776
|
+
logger.success(
|
|
777
|
+
`Installed ${chalk.cyan(storedName)} ` + chalk.dim(`(${entry.id})`),
|
|
778
|
+
);
|
|
779
|
+
logger.log(
|
|
780
|
+
` ${chalk.gray("Command:")} ${entry.command} ${entry.args.join(" ")}`,
|
|
781
|
+
);
|
|
782
|
+
logger.log(` ${chalk.gray("Next:")} cc mcp connect ${storedName}`);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
await shutdown();
|
|
786
|
+
} catch (err) {
|
|
787
|
+
logger.error(`Registry install failed: ${err.message}`);
|
|
788
|
+
process.exit(1);
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
|
|
792
|
+
registry
|
|
793
|
+
.command("categories")
|
|
794
|
+
.description("List available registry categories")
|
|
795
|
+
.option("--json", "Output as JSON")
|
|
796
|
+
.action((options) => {
|
|
797
|
+
if (options.json) {
|
|
798
|
+
console.log(JSON.stringify(REGISTRY_CATEGORIES, null, 2));
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
logger.log(chalk.bold("Categories:"));
|
|
802
|
+
for (const c of REGISTRY_CATEGORIES) {
|
|
803
|
+
const count = REGISTRY_CATALOG.filter((s) => s.category === c).length;
|
|
804
|
+
logger.log(` ${chalk.cyan(c)} ${chalk.dim(`(${count})`)}`);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
384
807
|
}
|