dynmcp 0.5.0 → 0.6.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/dist/index.cjs +307 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +307 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import
|
|
4
|
+
import process14 from "process";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "dynmcp",
|
|
10
|
-
version: "0.
|
|
10
|
+
version: "0.6.0",
|
|
11
11
|
description: "Dynamic MCP context management tool for AI MCP-enabled agents and clients.",
|
|
12
12
|
author: "Brandon Burrus <brandon@burrus.io>",
|
|
13
13
|
license: "MIT",
|
|
@@ -465,14 +465,14 @@ var sseTransport = z.object({
|
|
|
465
465
|
headers: z.record(z.string(), z.string()).optional(),
|
|
466
466
|
auth: authConfig
|
|
467
467
|
}).strict();
|
|
468
|
-
var
|
|
468
|
+
var transportConfigSchema = z.discriminatedUnion("transport", [
|
|
469
469
|
stdioTransport,
|
|
470
470
|
streamableHttpTransport,
|
|
471
471
|
sseTransport
|
|
472
472
|
]);
|
|
473
473
|
var mcpConfigSchema = z.object({
|
|
474
474
|
env: envModeSchema.optional(),
|
|
475
|
-
mcp: z.record(mcpName,
|
|
475
|
+
mcp: z.record(mcpName, transportConfigSchema).refine((obj) => Object.keys(obj).length > 0, { message: "At least one MCP must be configured" })
|
|
476
476
|
});
|
|
477
477
|
|
|
478
478
|
// src/config/loader.ts
|
|
@@ -704,7 +704,7 @@ import { spawn } from "child_process";
|
|
|
704
704
|
import process3 from "process";
|
|
705
705
|
async function openUrl(url) {
|
|
706
706
|
const { command, args } = openerForPlatform(url);
|
|
707
|
-
return new Promise((
|
|
707
|
+
return new Promise((resolve4, reject) => {
|
|
708
708
|
const child = spawn(command, args, {
|
|
709
709
|
stdio: "ignore",
|
|
710
710
|
detached: true
|
|
@@ -712,7 +712,7 @@ async function openUrl(url) {
|
|
|
712
712
|
child.once("error", reject);
|
|
713
713
|
child.once("spawn", () => {
|
|
714
714
|
child.unref();
|
|
715
|
-
|
|
715
|
+
resolve4();
|
|
716
716
|
});
|
|
717
717
|
});
|
|
718
718
|
}
|
|
@@ -787,14 +787,14 @@ var CallbackServer = class _CallbackServer {
|
|
|
787
787
|
throw new Error("CallbackServer is already started.");
|
|
788
788
|
}
|
|
789
789
|
const server = createServer((req, res) => this.handleRequest(req, res));
|
|
790
|
-
await new Promise((
|
|
790
|
+
await new Promise((resolve4, reject) => {
|
|
791
791
|
const onError = (err) => {
|
|
792
792
|
server.removeListener("listening", onListening);
|
|
793
793
|
reject(err);
|
|
794
794
|
};
|
|
795
795
|
const onListening = () => {
|
|
796
796
|
server.removeListener("error", onError);
|
|
797
|
-
|
|
797
|
+
resolve4();
|
|
798
798
|
};
|
|
799
799
|
server.once("error", onError);
|
|
800
800
|
server.once("listening", onListening);
|
|
@@ -832,12 +832,12 @@ var CallbackServer = class _CallbackServer {
|
|
|
832
832
|
if (this.pending !== null) {
|
|
833
833
|
return Promise.reject(new Error("awaitCallback already in progress."));
|
|
834
834
|
}
|
|
835
|
-
return new Promise((
|
|
835
|
+
return new Promise((resolve4, reject) => {
|
|
836
836
|
const timer = setTimeout(() => {
|
|
837
837
|
this.pending = null;
|
|
838
838
|
reject(new CallbackTimeoutError(timeoutMs));
|
|
839
839
|
}, timeoutMs);
|
|
840
|
-
this.pending = { resolve:
|
|
840
|
+
this.pending = { resolve: resolve4, reject, timer };
|
|
841
841
|
});
|
|
842
842
|
}
|
|
843
843
|
/** Closes the listening socket. Safe to call multiple times. */
|
|
@@ -847,8 +847,8 @@ var CallbackServer = class _CallbackServer {
|
|
|
847
847
|
this.pending = null;
|
|
848
848
|
}
|
|
849
849
|
if (this.server === null) return;
|
|
850
|
-
await new Promise((
|
|
851
|
-
this.server.close(() =>
|
|
850
|
+
await new Promise((resolve4) => {
|
|
851
|
+
this.server.close(() => resolve4());
|
|
852
852
|
});
|
|
853
853
|
this.server = null;
|
|
854
854
|
this.boundPort = null;
|
|
@@ -892,10 +892,10 @@ var CallbackServer = class _CallbackServer {
|
|
|
892
892
|
}
|
|
893
893
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
894
894
|
res.end(SUCCESS_HTML);
|
|
895
|
-
const { resolve:
|
|
895
|
+
const { resolve: resolve4, timer } = this.pending;
|
|
896
896
|
clearTimeout(timer);
|
|
897
897
|
this.pending = null;
|
|
898
|
-
|
|
898
|
+
resolve4({ code, state });
|
|
899
899
|
}
|
|
900
900
|
respondError(res, errorCode, description2) {
|
|
901
901
|
res.writeHead(400, { "content-type": "text/html; charset=utf-8" });
|
|
@@ -3309,6 +3309,215 @@ async function runProxy(orchestrator) {
|
|
|
3309
3309
|
}
|
|
3310
3310
|
}
|
|
3311
3311
|
|
|
3312
|
+
// src/scaffold/init.ts
|
|
3313
|
+
import { existsSync as existsSync3, writeFileSync } from "fs";
|
|
3314
|
+
import { resolve as resolve3 } from "path";
|
|
3315
|
+
import process12 from "process";
|
|
3316
|
+
|
|
3317
|
+
// src/scaffold/format.ts
|
|
3318
|
+
import { extname } from "path";
|
|
3319
|
+
function detectFormat(filePath) {
|
|
3320
|
+
const ext = extname(filePath).toLowerCase();
|
|
3321
|
+
return ext === ".yml" || ext === ".yaml" ? "yaml" : "json";
|
|
3322
|
+
}
|
|
3323
|
+
var SCHEMA_URL = "https://dynamicmcp.tools/config.json";
|
|
3324
|
+
|
|
3325
|
+
// src/scaffold/init.ts
|
|
3326
|
+
function init(options = {}) {
|
|
3327
|
+
const cwd = options.cwd ?? process12.cwd();
|
|
3328
|
+
const stdout = options.write ?? ((chunk) => void process12.stdout.write(chunk));
|
|
3329
|
+
const fileWriter = options.fileWriter ?? ((p, c) => writeFileSync(p, c, "utf-8"));
|
|
3330
|
+
const fileExists = options.fileExists ?? ((p) => existsSync3(p));
|
|
3331
|
+
const targetPath = resolveInitPath({ cwd, path: options.path, yaml: options.yaml === true });
|
|
3332
|
+
const format = detectFormat(targetPath);
|
|
3333
|
+
if (fileExists(targetPath) && options.force !== true) {
|
|
3334
|
+
throw new Error(`File already exists: ${targetPath}
|
|
3335
|
+
Use --force to overwrite.`);
|
|
3336
|
+
}
|
|
3337
|
+
const contents = format === "yaml" ? renderYamlSkeleton() : renderJsonSkeleton();
|
|
3338
|
+
fileWriter(targetPath, contents);
|
|
3339
|
+
stdout(`Wrote ${targetPath}
|
|
3340
|
+
`);
|
|
3341
|
+
stdout("\nThis config has no MCPs yet. Add one with:\n");
|
|
3342
|
+
stdout(" dynmcp add <name> --command <cmd> (stdio upstream)\n");
|
|
3343
|
+
stdout(" dynmcp add <name> --transport streamable-http --url <url> (remote HTTP upstream)\n");
|
|
3344
|
+
stdout("\nSee https://dynamicmcp.tools for full documentation.\n");
|
|
3345
|
+
}
|
|
3346
|
+
function resolveInitPath(opts) {
|
|
3347
|
+
if (opts.path !== void 0) {
|
|
3348
|
+
return resolve3(opts.cwd, opts.path);
|
|
3349
|
+
}
|
|
3350
|
+
return resolve3(opts.cwd, opts.yaml ? "mcp.yaml" : "mcp.json");
|
|
3351
|
+
}
|
|
3352
|
+
function renderJsonSkeleton() {
|
|
3353
|
+
const body = JSON.stringify({ $schema: SCHEMA_URL, mcp: {} }, null, 2);
|
|
3354
|
+
return `${body}
|
|
3355
|
+
`;
|
|
3356
|
+
}
|
|
3357
|
+
function renderYamlSkeleton() {
|
|
3358
|
+
return `# yaml-language-server: $schema=${SCHEMA_URL}
|
|
3359
|
+
|
|
3360
|
+
mcp: {}
|
|
3361
|
+
`;
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
// src/scaffold/add.ts
|
|
3365
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
3366
|
+
import process13 from "process";
|
|
3367
|
+
import { parseDocument } from "yaml";
|
|
3368
|
+
function add(options) {
|
|
3369
|
+
validateName(options.name);
|
|
3370
|
+
const stdout = options.write ?? ((chunk) => void process13.stdout.write(chunk));
|
|
3371
|
+
const fileReader = options.fileReader ?? ((p) => readFileSync3(p, "utf-8"));
|
|
3372
|
+
const fileWriter = options.fileWriter ?? ((p, c) => writeFileSync2(p, c, "utf-8"));
|
|
3373
|
+
const resolvePath = options.resolvePath ?? resolveConfigPath;
|
|
3374
|
+
let targetPath;
|
|
3375
|
+
try {
|
|
3376
|
+
targetPath = resolvePath(options.configPath);
|
|
3377
|
+
} catch (error) {
|
|
3378
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3379
|
+
throw new Error(`${message}
|
|
3380
|
+
Hint: run 'dynmcp init' to create a starter config file.`);
|
|
3381
|
+
}
|
|
3382
|
+
const raw = fileReader(targetPath);
|
|
3383
|
+
const format = detectFormat(targetPath);
|
|
3384
|
+
const entry = buildEntry(options);
|
|
3385
|
+
const parsed = transportConfigSchema.safeParse(entry);
|
|
3386
|
+
if (!parsed.success) {
|
|
3387
|
+
const issues = parsed.error.issues.map((i) => ` - ${i.path.join(".") || "<root>"}: ${i.message}`).join("\n");
|
|
3388
|
+
throw new Error(`Invalid MCP entry:
|
|
3389
|
+
${issues}`);
|
|
3390
|
+
}
|
|
3391
|
+
const next = format === "yaml" ? writeYaml(raw, options.name, parsed.data, options.force === true) : writeJson(raw, options.name, parsed.data, options.force === true);
|
|
3392
|
+
fileWriter(targetPath, next);
|
|
3393
|
+
stdout(`Added '${options.name}' (${options.transport}) to ${targetPath}
|
|
3394
|
+
`);
|
|
3395
|
+
}
|
|
3396
|
+
function validateName(name) {
|
|
3397
|
+
if (!MCP_NAME_PATTERN.test(name)) {
|
|
3398
|
+
throw new Error(
|
|
3399
|
+
`Invalid MCP name '${name}'. Names must match ${MCP_NAME_PATTERN.source} (lowercase letters, digits, and dashes; starting with a letter or digit).`
|
|
3400
|
+
);
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
3403
|
+
function buildEntry(options) {
|
|
3404
|
+
if (options.transport === "stdio") {
|
|
3405
|
+
if (options.command === void 0 || options.command.length === 0) {
|
|
3406
|
+
throw new Error("--command is required for stdio transport");
|
|
3407
|
+
}
|
|
3408
|
+
const entry2 = {
|
|
3409
|
+
transport: "stdio",
|
|
3410
|
+
command: options.command
|
|
3411
|
+
};
|
|
3412
|
+
if (options.description !== void 0) entry2.description = options.description;
|
|
3413
|
+
if (options.args !== void 0 && options.args.length > 0) {
|
|
3414
|
+
entry2.args = options.args;
|
|
3415
|
+
}
|
|
3416
|
+
if (options.envVars !== void 0 && options.envVars.length > 0) {
|
|
3417
|
+
entry2.env = parseKeyValuePairs(options.envVars, "--env");
|
|
3418
|
+
}
|
|
3419
|
+
return entry2;
|
|
3420
|
+
}
|
|
3421
|
+
if (options.url === void 0 || options.url.length === 0) {
|
|
3422
|
+
throw new Error(`--url is required for ${options.transport} transport`);
|
|
3423
|
+
}
|
|
3424
|
+
const entry = {
|
|
3425
|
+
transport: options.transport,
|
|
3426
|
+
url: options.url
|
|
3427
|
+
};
|
|
3428
|
+
if (options.description !== void 0) entry.description = options.description;
|
|
3429
|
+
if (options.headers !== void 0 && options.headers.length > 0) {
|
|
3430
|
+
entry.headers = parseHeaderPairs(options.headers);
|
|
3431
|
+
}
|
|
3432
|
+
const auth2 = buildAuthBlock(options);
|
|
3433
|
+
if (auth2 !== void 0) entry.auth = auth2;
|
|
3434
|
+
return entry;
|
|
3435
|
+
}
|
|
3436
|
+
function buildAuthBlock(options) {
|
|
3437
|
+
const hasAny = options.clientId !== void 0 || options.clientSecret !== void 0 || options.scope !== void 0;
|
|
3438
|
+
if (!hasAny) return void 0;
|
|
3439
|
+
if (options.clientId === void 0) {
|
|
3440
|
+
throw new Error("--client-id is required when --client-secret or --scope is provided");
|
|
3441
|
+
}
|
|
3442
|
+
const auth2 = { client_id: options.clientId };
|
|
3443
|
+
if (options.clientSecret !== void 0) auth2.client_secret = options.clientSecret;
|
|
3444
|
+
if (options.scope !== void 0) auth2.scope = options.scope;
|
|
3445
|
+
return auth2;
|
|
3446
|
+
}
|
|
3447
|
+
function parseKeyValuePairs(pairs, flag) {
|
|
3448
|
+
const out = {};
|
|
3449
|
+
for (const pair of pairs) {
|
|
3450
|
+
const eq = pair.indexOf("=");
|
|
3451
|
+
if (eq <= 0) {
|
|
3452
|
+
throw new Error(`${flag} expects KEY=VALUE (got: ${JSON.stringify(pair)})`);
|
|
3453
|
+
}
|
|
3454
|
+
const key = pair.slice(0, eq);
|
|
3455
|
+
out[key] = pair.slice(eq + 1);
|
|
3456
|
+
}
|
|
3457
|
+
return out;
|
|
3458
|
+
}
|
|
3459
|
+
function parseHeaderPairs(pairs) {
|
|
3460
|
+
const out = {};
|
|
3461
|
+
for (const pair of pairs) {
|
|
3462
|
+
const colon = pair.indexOf(":");
|
|
3463
|
+
if (colon <= 0) {
|
|
3464
|
+
throw new Error(`--header expects "Name: Value" (got: ${JSON.stringify(pair)})`);
|
|
3465
|
+
}
|
|
3466
|
+
const key = pair.slice(0, colon).trim();
|
|
3467
|
+
const value = pair.slice(colon + 1).trim();
|
|
3468
|
+
if (key.length === 0) {
|
|
3469
|
+
throw new Error(`--header name cannot be empty (got: ${JSON.stringify(pair)})`);
|
|
3470
|
+
}
|
|
3471
|
+
out[key] = value;
|
|
3472
|
+
}
|
|
3473
|
+
return out;
|
|
3474
|
+
}
|
|
3475
|
+
function writeJson(raw, name, entry, force) {
|
|
3476
|
+
let parsed;
|
|
3477
|
+
try {
|
|
3478
|
+
parsed = JSON.parse(raw);
|
|
3479
|
+
} catch (error) {
|
|
3480
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3481
|
+
throw new Error(`Failed to parse config as JSON: ${message}`);
|
|
3482
|
+
}
|
|
3483
|
+
if (!isPlainObject(parsed)) {
|
|
3484
|
+
throw new Error("Top-level config must be a JSON object.");
|
|
3485
|
+
}
|
|
3486
|
+
let mcp = parsed.mcp;
|
|
3487
|
+
if (mcp === void 0) {
|
|
3488
|
+
mcp = {};
|
|
3489
|
+
parsed.mcp = mcp;
|
|
3490
|
+
} else if (!isPlainObject(mcp)) {
|
|
3491
|
+
throw new Error("Config field 'mcp' must be an object.");
|
|
3492
|
+
}
|
|
3493
|
+
const mcpRecord = mcp;
|
|
3494
|
+
if (mcpRecord[name] !== void 0 && !force) {
|
|
3495
|
+
throw new Error(`Entry '${name}' already exists. Use --force to overwrite.`);
|
|
3496
|
+
}
|
|
3497
|
+
mcpRecord[name] = entry;
|
|
3498
|
+
return `${JSON.stringify(parsed, null, 2)}
|
|
3499
|
+
`;
|
|
3500
|
+
}
|
|
3501
|
+
function writeYaml(raw, name, entry, force) {
|
|
3502
|
+
const doc = parseDocument(raw);
|
|
3503
|
+
if (doc.errors.length > 0) {
|
|
3504
|
+
const first = doc.errors[0]?.message ?? "unknown error";
|
|
3505
|
+
throw new Error(`Failed to parse config as YAML: ${first}`);
|
|
3506
|
+
}
|
|
3507
|
+
if (!doc.has("mcp")) {
|
|
3508
|
+
doc.set("mcp", {});
|
|
3509
|
+
}
|
|
3510
|
+
const path = ["mcp", name];
|
|
3511
|
+
if (doc.hasIn(path) && !force) {
|
|
3512
|
+
throw new Error(`Entry '${name}' already exists. Use --force to overwrite.`);
|
|
3513
|
+
}
|
|
3514
|
+
doc.setIn(path, entry);
|
|
3515
|
+
return doc.toString();
|
|
3516
|
+
}
|
|
3517
|
+
function isPlainObject(value) {
|
|
3518
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3312
3521
|
// src/cli.ts
|
|
3313
3522
|
var cliBanner = chalk.bold.magentaBright(
|
|
3314
3523
|
figlet.textSync("DYNAMIC MCP", {
|
|
@@ -3319,36 +3528,100 @@ var cliBanner = chalk.bold.magentaBright(
|
|
|
3319
3528
|
);
|
|
3320
3529
|
var cli = new Command(package_default.name).description(package_default.description).version(package_default.version).addHelpText("beforeAll", cliBanner).addHelpText(
|
|
3321
3530
|
"after",
|
|
3322
|
-
"\nExamples:\n dynmcp -- npx -y chrome-devtools-mcp@latest\n dynmcp --config ./mcp.json\n dynmcp ls\n dynmcp test github\n dynmcp login github\n dynmcp logout github\n"
|
|
3531
|
+
"\nExamples:\n dynmcp -- npx -y chrome-devtools-mcp@latest\n dynmcp --config ./mcp.json\n dynmcp init\n dynmcp add filesystem --command npx --arg -y --arg @modelcontextprotocol/server-filesystem --arg /tmp\n dynmcp add github --transport streamable-http --url https://api.githubcopilot.com/mcp\n dynmcp ls\n dynmcp test github\n dynmcp login github\n dynmcp logout github\n"
|
|
3323
3532
|
).option("-c, --config <path>", "Path to config file (JSON or YAML)").option("-e, --env <path>", "Path to a .env file for environment variable interpolation").allowExcessArguments(true).passThroughOptions(true).action(async (_options, cmd) => {
|
|
3324
|
-
const separatorIndex =
|
|
3533
|
+
const separatorIndex = process14.argv.indexOf("--");
|
|
3325
3534
|
const configPath = cmd.opts().config;
|
|
3326
3535
|
const envFilePath = cmd.opts().env;
|
|
3327
3536
|
if (separatorIndex !== -1) {
|
|
3328
|
-
const [command, ...args] =
|
|
3537
|
+
const [command, ...args] = process14.argv.slice(separatorIndex + 1);
|
|
3329
3538
|
if (command === void 0) {
|
|
3330
|
-
|
|
3539
|
+
process14.stderr.write(
|
|
3331
3540
|
"dynmcp: no upstream command provided after --.\nUsage: dynmcp -- <command> [args...]\n"
|
|
3332
3541
|
);
|
|
3333
|
-
|
|
3542
|
+
process14.exit(1);
|
|
3334
3543
|
}
|
|
3335
3544
|
try {
|
|
3336
3545
|
await startProxy(command, args);
|
|
3337
3546
|
} catch (error) {
|
|
3338
|
-
|
|
3547
|
+
process14.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
|
|
3339
3548
|
`);
|
|
3340
|
-
|
|
3549
|
+
process14.exit(1);
|
|
3341
3550
|
}
|
|
3342
3551
|
return;
|
|
3343
3552
|
}
|
|
3344
3553
|
try {
|
|
3345
3554
|
await startProxyFromConfig({ configPath, envFilePath });
|
|
3346
3555
|
} catch (error) {
|
|
3347
|
-
|
|
3556
|
+
process14.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
|
|
3348
3557
|
`);
|
|
3349
|
-
|
|
3558
|
+
process14.exit(1);
|
|
3350
3559
|
}
|
|
3351
3560
|
});
|
|
3561
|
+
cli.command("init").description("Write a starter config file (mcp.json by default) in the current directory.").option("--path <path>", "Explicit target path (extension determines format).").option("--yaml", "Write mcp.yaml instead of mcp.json (ignored if --path is set).").option("--force", "Overwrite an existing file.").action((options) => {
|
|
3562
|
+
try {
|
|
3563
|
+
init({
|
|
3564
|
+
...options.path !== void 0 ? { path: options.path } : {},
|
|
3565
|
+
...options.yaml === true ? { yaml: true } : {},
|
|
3566
|
+
...options.force === true ? { force: true } : {}
|
|
3567
|
+
});
|
|
3568
|
+
} catch (error) {
|
|
3569
|
+
process14.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
|
|
3570
|
+
`);
|
|
3571
|
+
process14.exit(1);
|
|
3572
|
+
}
|
|
3573
|
+
});
|
|
3574
|
+
var collectRepeatable = (value, previous) => [...previous, value];
|
|
3575
|
+
cli.command("add <name>").description("Insert a new MCP entry into the resolved config file.").option(
|
|
3576
|
+
"-t, --transport <transport>",
|
|
3577
|
+
"Transport: stdio | streamable-http | sse (default: stdio).",
|
|
3578
|
+
"stdio"
|
|
3579
|
+
).option("-c, --config <path>", "Path to config file (otherwise auto-discovered).").option("--description <text>", "Per-entry description; presence makes the entry lazy.").option("--command <cmd>", "(stdio) Command to spawn for the upstream MCP.").option(
|
|
3580
|
+
"--arg <arg>",
|
|
3581
|
+
"(stdio) Repeatable positional argument passed after --command.",
|
|
3582
|
+
collectRepeatable,
|
|
3583
|
+
[]
|
|
3584
|
+
).option(
|
|
3585
|
+
"--env <KEY=VAL>",
|
|
3586
|
+
"(stdio) Repeatable env var for the spawned process.",
|
|
3587
|
+
collectRepeatable,
|
|
3588
|
+
[]
|
|
3589
|
+
).option("--url <url>", "(http/sse) Endpoint URL.").option(
|
|
3590
|
+
"--header <header>",
|
|
3591
|
+
'(http/sse) Repeatable "Name: Value" header.',
|
|
3592
|
+
collectRepeatable,
|
|
3593
|
+
[]
|
|
3594
|
+
).option("--client-id <id>", "(http/sse) Pre-registered OAuth client_id (skips DCR).").option("--client-secret <secret>", "(http/sse) Pre-registered OAuth client_secret.").option("--scope <scope>", "(http/sse) OAuth scope to request.").option("--force", "Overwrite an existing entry with the same name.").action(
|
|
3595
|
+
(name, options) => {
|
|
3596
|
+
try {
|
|
3597
|
+
const transport = options.transport;
|
|
3598
|
+
if (transport !== "stdio" && transport !== "streamable-http" && transport !== "sse") {
|
|
3599
|
+
throw new Error(
|
|
3600
|
+
`Invalid --transport '${options.transport}'. Must be one of: stdio, streamable-http, sse.`
|
|
3601
|
+
);
|
|
3602
|
+
}
|
|
3603
|
+
add({
|
|
3604
|
+
name,
|
|
3605
|
+
transport,
|
|
3606
|
+
...options.config !== void 0 ? { configPath: options.config } : {},
|
|
3607
|
+
...options.force === true ? { force: true } : {},
|
|
3608
|
+
...options.description !== void 0 ? { description: options.description } : {},
|
|
3609
|
+
...options.command !== void 0 ? { command: options.command } : {},
|
|
3610
|
+
...options.arg.length > 0 ? { args: options.arg } : {},
|
|
3611
|
+
...options.env.length > 0 ? { envVars: options.env } : {},
|
|
3612
|
+
...options.url !== void 0 ? { url: options.url } : {},
|
|
3613
|
+
...options.header.length > 0 ? { headers: options.header } : {},
|
|
3614
|
+
...options.clientId !== void 0 ? { clientId: options.clientId } : {},
|
|
3615
|
+
...options.clientSecret !== void 0 ? { clientSecret: options.clientSecret } : {},
|
|
3616
|
+
...options.scope !== void 0 ? { scope: options.scope } : {}
|
|
3617
|
+
});
|
|
3618
|
+
} catch (error) {
|
|
3619
|
+
process14.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
|
|
3620
|
+
`);
|
|
3621
|
+
process14.exit(1);
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
);
|
|
3352
3625
|
cli.command("login <name>").description("Run the OAuth authorization-code flow for an upstream MCP and store tokens.").option("-c, --config <path>", "Path to config file (JSON or YAML)").option("-e, --env <path>", "Path to a .env file for environment variable interpolation").action(async (name, options) => {
|
|
3353
3626
|
try {
|
|
3354
3627
|
await login({
|
|
@@ -3357,9 +3630,9 @@ cli.command("login <name>").description("Run the OAuth authorization-code flow f
|
|
|
3357
3630
|
...options.env !== void 0 ? { envFilePath: options.env } : {}
|
|
3358
3631
|
});
|
|
3359
3632
|
} catch (error) {
|
|
3360
|
-
|
|
3633
|
+
process14.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
|
|
3361
3634
|
`);
|
|
3362
|
-
|
|
3635
|
+
process14.exit(1);
|
|
3363
3636
|
}
|
|
3364
3637
|
});
|
|
3365
3638
|
cli.command("logout <name>").description("Delete the OAuth keychain entry for an upstream MCP.").option("-c, --config <path>", "Path to config file (JSON or YAML)").option("-e, --env <path>", "Path to a .env file for environment variable interpolation").action(async (name, options) => {
|
|
@@ -3370,9 +3643,9 @@ cli.command("logout <name>").description("Delete the OAuth keychain entry for an
|
|
|
3370
3643
|
...options.env !== void 0 ? { envFilePath: options.env } : {}
|
|
3371
3644
|
});
|
|
3372
3645
|
} catch (error) {
|
|
3373
|
-
|
|
3646
|
+
process14.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
|
|
3374
3647
|
`);
|
|
3375
|
-
|
|
3648
|
+
process14.exit(1);
|
|
3376
3649
|
}
|
|
3377
3650
|
});
|
|
3378
3651
|
cli.command("ls").description("List configured upstream MCPs with transport, mode, endpoint, and auth status.").option("-c, --config <path>", "Path to config file (JSON or YAML)").option("-e, --env <path>", "Path to a .env file for environment variable interpolation").option("--json", "Emit JSON instead of the aligned text table").action(async (options) => {
|
|
@@ -3383,9 +3656,9 @@ cli.command("ls").description("List configured upstream MCPs with transport, mod
|
|
|
3383
3656
|
...options.json === true ? { json: true } : {}
|
|
3384
3657
|
});
|
|
3385
3658
|
} catch (error) {
|
|
3386
|
-
|
|
3659
|
+
process14.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
|
|
3387
3660
|
`);
|
|
3388
|
-
|
|
3661
|
+
process14.exit(1);
|
|
3389
3662
|
}
|
|
3390
3663
|
});
|
|
3391
3664
|
cli.command("test [name]").description("Probe one or all configured upstream MCPs and print their discovered catalogs.").option("-c, --config <path>", "Path to config file (JSON or YAML)").option("-e, --env <path>", "Path to a .env file for environment variable interpolation").option("--json", "Emit JSON instead of the formatted text output").option("--timeout <ms>", "Per-MCP timeout in milliseconds (default: 15000)", (v) => Number(v)).action(
|
|
@@ -3398,19 +3671,19 @@ cli.command("test [name]").description("Probe one or all configured upstream MCP
|
|
|
3398
3671
|
...options.json === true ? { json: true } : {},
|
|
3399
3672
|
...options.timeout !== void 0 && !Number.isNaN(options.timeout) ? { timeoutMs: options.timeout } : {}
|
|
3400
3673
|
});
|
|
3401
|
-
if (exitCode !== 0)
|
|
3674
|
+
if (exitCode !== 0) process14.exit(exitCode);
|
|
3402
3675
|
} catch (error) {
|
|
3403
|
-
|
|
3676
|
+
process14.stderr.write(`dynmcp: ${error instanceof Error ? error.message : String(error)}
|
|
3404
3677
|
`);
|
|
3405
|
-
|
|
3678
|
+
process14.exit(1);
|
|
3406
3679
|
}
|
|
3407
3680
|
}
|
|
3408
3681
|
);
|
|
3409
3682
|
|
|
3410
3683
|
// src/index.ts
|
|
3411
|
-
import
|
|
3684
|
+
import process15 from "process";
|
|
3412
3685
|
async function main() {
|
|
3413
|
-
cli.parse(
|
|
3686
|
+
cli.parse(process15.argv);
|
|
3414
3687
|
}
|
|
3415
3688
|
main();
|
|
3416
3689
|
//# sourceMappingURL=index.js.map
|