@emcy/cli 0.1.0-pr.1.44e275868746.1.1

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.
@@ -0,0 +1,827 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { createInterface } from "node:readline/promises";
3
+ import { stdin as input, stdout as output } from "node:process";
4
+ import { createParser } from "eventsource-parser";
5
+ import { McpstackClient } from "./client.js";
6
+ import { login, logout, serviceAccountLogin, serviceAccountLogout, status, whoami } from "./auth.js";
7
+ import { printData, printInfo, printSuccess } from "./output.js";
8
+ const orgColumns = [
9
+ { header: "ID", value: (item) => item.id },
10
+ { header: "Name", value: (item) => item.name },
11
+ { header: "Slug", value: (item) => item.slug },
12
+ { header: "Role", value: (item) => item.roleKey ?? item.role },
13
+ ];
14
+ const serverColumns = [
15
+ { header: "ID", value: (item) => item.id },
16
+ { header: "Name", value: (item) => item.name },
17
+ { header: "Slug", value: (item) => item.slug },
18
+ { header: "Runtime", value: (item) => item.runtimeType },
19
+ { header: "Status", value: (item) => item.status },
20
+ ];
21
+ const memberColumns = [
22
+ { header: "Principal", value: (item) => item.principalId ?? item.id },
23
+ { header: "Name", value: (item) => item.displayName ?? item.name },
24
+ { header: "Email", value: (item) => item.email },
25
+ { header: "Role", value: (item) => item.roleKey ?? item.role },
26
+ ];
27
+ const invitationColumns = [
28
+ { header: "ID", value: (item) => item.id },
29
+ { header: "Email", value: (item) => item.email ?? item.invitedEmail },
30
+ { header: "Role", value: (item) => item.role },
31
+ { header: "Status", value: (item) => item.status },
32
+ { header: "Expires", value: (item) => item.expiresAt },
33
+ ];
34
+ const gatewayColumns = [
35
+ { header: "ID", value: (item) => item.id },
36
+ { header: "Name", value: (item) => item.name },
37
+ { header: "Provider", value: (item) => item.provider },
38
+ { header: "Status", value: (item) => item.status },
39
+ ];
40
+ const agentColumns = [
41
+ { header: "ID", value: (item) => item.id },
42
+ { header: "Name", value: (item) => item.name },
43
+ { header: "Model", value: (item) => item.model ?? item.modelId },
44
+ { header: "Status", value: (item) => item.status },
45
+ ];
46
+ export function registerCommands(program) {
47
+ registerAuthCommands(program);
48
+ registerOrgCommands(program);
49
+ registerMemberCommands(program);
50
+ registerApiKeyCommands(program);
51
+ registerDashboardCommands(program);
52
+ registerHostingCommands(program);
53
+ registerHostCommands(program);
54
+ registerServerCommands(program);
55
+ registerToolCommands(program);
56
+ registerDeploymentCommands(program);
57
+ registerRuntimeCommands(program);
58
+ registerGatewayCommands(program);
59
+ registerGatewayPublicCommands(program);
60
+ registerAgentCommands(program);
61
+ registerCompletionCommand(program);
62
+ }
63
+ function registerAuthCommands(program) {
64
+ const auth = program.command("auth").description("Authenticate the MCP Stack CLI");
65
+ auth.command("login")
66
+ .description("Sign in with OAuth device flow")
67
+ .action(run(async (options) => login(options)));
68
+ auth.command("logout")
69
+ .description("Delete the active local login")
70
+ .action(run(async (options) => logout(options)));
71
+ auth.command("status")
72
+ .description("Show the active login")
73
+ .action(run(async (options) => status(options)));
74
+ auth.command("whoami")
75
+ .description("Show the authenticated user or service account")
76
+ .action(run(async (options) => printData(await whoami(options), options)));
77
+ const serviceAccount = auth.command("service-account").description("Manage service-account auth");
78
+ serviceAccount.command("login")
79
+ .description("Store a service-account API key")
80
+ .requiredOption("--key <key>", "Service-account API key")
81
+ .action(run(async (options) => serviceAccountLogin(options)));
82
+ serviceAccount.command("logout")
83
+ .description("Delete the active service-account login")
84
+ .action(run(async (options) => serviceAccountLogout(options)));
85
+ }
86
+ function registerOrgCommands(program) {
87
+ const org = program.command("org").description("Manage organizations");
88
+ org.command("list")
89
+ .description("List organizations")
90
+ .action(runClient(async (client, options) => {
91
+ printData(await client.request("/api/v1/organizations"), options, orgColumns);
92
+ }));
93
+ org.command("use")
94
+ .argument("<organizationId>", "Organization id")
95
+ .description("Set active organization")
96
+ .action(runClient(async (client, _options, organizationId) => {
97
+ await client.setActiveOrg(organizationId);
98
+ printSuccess(`Using organization '${organizationId}'.`);
99
+ }));
100
+ org.command("get")
101
+ .argument("[organizationId]", "Organization id")
102
+ .description("Show an organization")
103
+ .action(runClient(async (client, options, organizationId) => {
104
+ const orgId = await client.resolveOrgId(organizationId);
105
+ printData(await client.request(`/api/v1/organizations/${orgId}`), options);
106
+ }));
107
+ org.command("create")
108
+ .requiredOption("--name <name>", "Organization name")
109
+ .option("--slug <slug>", "Organization slug")
110
+ .option("--no-switch", "Do not set the new organization as active")
111
+ .description("Create an organization")
112
+ .action(runClient(async (client, options) => {
113
+ const created = await client.request("/api/v1/organizations", {
114
+ method: "POST",
115
+ body: omitUndefined({ name: options.name, slug: options.slug }),
116
+ });
117
+ if (options.switch !== false && created.id) {
118
+ await client.setActiveOrg(created.id);
119
+ }
120
+ printData(created, options);
121
+ }));
122
+ org.command("update")
123
+ .argument("[organizationId]", "Organization id")
124
+ .requiredOption("--name <name>", "Organization name")
125
+ .description("Update an organization")
126
+ .action(runClient(async (client, options, organizationId) => {
127
+ const orgId = await client.resolveOrgId(organizationId);
128
+ printData(await client.request(`/api/v1/organizations/${orgId}`, {
129
+ method: "PATCH",
130
+ body: { name: options.name },
131
+ }), options);
132
+ }));
133
+ org.command("delete")
134
+ .argument("[organizationId]", "Organization id")
135
+ .option("--yes", "Confirm deletion")
136
+ .description("Delete an organization")
137
+ .action(runClient(async (client, options, organizationId) => {
138
+ const orgId = await client.resolveOrgId(organizationId);
139
+ await requireConfirmation(options, `Delete organization '${orgId}'?`);
140
+ await client.request(`/api/v1/organizations/${orgId}`, { method: "DELETE" });
141
+ printSuccess(`Deleted organization '${orgId}'.`);
142
+ }));
143
+ }
144
+ function registerMemberCommands(program) {
145
+ const members = program.command("members").description("Manage organization members and invitations");
146
+ members.command("list")
147
+ .description("List active members")
148
+ .action(runClientWithOrg(async (client, options, orgId) => {
149
+ printData(await client.request(`/api/v1/organizations/${orgId}/members`), options, memberColumns);
150
+ }));
151
+ members.command("invite")
152
+ .argument("<email>", "Email address")
153
+ .requiredOption("--role <role>", "Role: admin, developer, or viewer")
154
+ .description("Send an invitation")
155
+ .action(runClientWithOrg(async (client, options, orgId, email) => {
156
+ printData(await client.request(`/api/v1/organizations/${orgId}/invitations`, {
157
+ method: "POST",
158
+ body: { email, role: options.role },
159
+ }), options);
160
+ }));
161
+ members.command("update-role")
162
+ .argument("<principalId>", "Principal id")
163
+ .requiredOption("--role <role>", "Role: admin, developer, or viewer")
164
+ .description("Update a member role")
165
+ .action(runClientWithOrg(async (client, options, orgId, principalId) => {
166
+ printData(await client.request(`/api/v1/organizations/${orgId}/members/${principalId}`, {
167
+ method: "PATCH",
168
+ body: { role: options.role },
169
+ }), options);
170
+ }));
171
+ members.command("remove")
172
+ .argument("<principalId>", "Principal id")
173
+ .option("--yes", "Confirm removal")
174
+ .description("Remove a member")
175
+ .action(runClientWithOrg(async (client, options, orgId, principalId) => {
176
+ await requireConfirmation(options, `Remove member '${principalId}'?`);
177
+ await client.request(`/api/v1/organizations/${orgId}/members/${principalId}`, { method: "DELETE" });
178
+ printSuccess(`Removed member '${principalId}'.`);
179
+ }));
180
+ const invitations = members.command("invitations").description("Manage pending invitations");
181
+ invitations.command("list")
182
+ .description("List invitations")
183
+ .action(runClientWithOrg(async (client, options, orgId) => {
184
+ printData(await client.request(`/api/v1/organizations/${orgId}/invitations`), options, invitationColumns);
185
+ }));
186
+ invitations.command("resend")
187
+ .argument("<invitationId>", "Invitation id")
188
+ .description("Resend an invitation")
189
+ .action(runClientWithOrg(async (client, options, orgId, invitationId) => {
190
+ printData(await client.request(`/api/v1/organizations/${orgId}/invitations/${invitationId}/resend`, { method: "POST" }), options);
191
+ }));
192
+ invitations.command("revoke")
193
+ .argument("<invitationId>", "Invitation id")
194
+ .option("--reason <reason>", "Revocation reason")
195
+ .description("Revoke an invitation")
196
+ .action(runClientWithOrg(async (client, options, orgId, invitationId) => {
197
+ printData(await client.request(`/api/v1/organizations/${orgId}/invitations/${invitationId}/revoke`, {
198
+ method: "POST",
199
+ body: { reason: options.reason },
200
+ }), options);
201
+ }));
202
+ }
203
+ function registerApiKeyCommands(program) {
204
+ const keys = program.command("api-keys").description("Manage service-account API keys");
205
+ keys.command("list").action(runClientWithOrg(async (client, options, orgId) => {
206
+ printData(await client.request(`/api/v1/organizations/${orgId}/api-keys`), options);
207
+ }));
208
+ keys.command("roles").action(runClientWithOrg(async (client, options, orgId) => {
209
+ printData(await client.request(`/api/v1/organizations/${orgId}/api-keys/roles`), options);
210
+ }));
211
+ keys.command("create")
212
+ .requiredOption("--name <name>", "Key name")
213
+ .option("--description <description>", "Description")
214
+ .option("--role <role>", "Role key", "viewer")
215
+ .option("--expires-at <iso>", "Expiration timestamp")
216
+ .action(runClientWithOrg(async (client, options, orgId) => {
217
+ printData(await client.request(`/api/v1/organizations/${orgId}/api-keys`, {
218
+ method: "POST",
219
+ body: omitUndefined({
220
+ name: options.name,
221
+ description: options.description,
222
+ roleKey: options.role,
223
+ expiresAt: options.expiresAt,
224
+ }),
225
+ }), options);
226
+ }));
227
+ keys.command("revoke")
228
+ .argument("<keyId>", "API key id")
229
+ .option("--yes", "Confirm revocation")
230
+ .action(runClientWithOrg(async (client, options, orgId, keyId) => {
231
+ await requireConfirmation(options, `Revoke API key '${keyId}'?`);
232
+ await client.request(`/api/v1/organizations/${orgId}/api-keys/${keyId}`, { method: "DELETE" });
233
+ printSuccess(`Revoked API key '${keyId}'.`);
234
+ }));
235
+ }
236
+ function registerDashboardCommands(program) {
237
+ const dashboard = program.command("dashboard").description("Dashboard summary and analytics");
238
+ dashboard.command("stats").action(runClient(async (client, options) => {
239
+ printData(await client.request("/api/v1/dashboard/stats"), options);
240
+ }));
241
+ dashboard.command("analytics")
242
+ .option("--days <days>", "Days to include", "30")
243
+ .action(runClient(async (client, options) => {
244
+ printData(await client.request("/api/v1/dashboard/analytics", { query: { days: options.days } }), options);
245
+ }));
246
+ }
247
+ function registerHostingCommands(program) {
248
+ const hosting = program.command("hosting").description("Hosting usage");
249
+ hosting.command("usage").action(runClientWithOrg(async (client, options, orgId) => {
250
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-hosting/usage`), options);
251
+ }));
252
+ const billing = program.command("billing").description("Billing operations");
253
+ billing.command("checkout").action(runClientWithOrg(async (client, options, orgId) => {
254
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-hosting/billing/checkout-session`, { method: "POST" }), options);
255
+ }));
256
+ billing.command("sync").argument("<sessionId>").action(runClientWithOrg(async (client, options, orgId, sessionId) => {
257
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-hosting/billing/checkout-session/${sessionId}/sync`, { method: "POST" }), options);
258
+ }));
259
+ }
260
+ function registerHostCommands(program) {
261
+ const host = program.command("host").description("Host environment metadata");
262
+ host.command("environment").action(runClientWithOrg(async (client, options, orgId) => {
263
+ printData(await client.request(`/api/v1/organizations/${orgId}/host/environment`), options);
264
+ }));
265
+ host.command("regions")
266
+ .requiredOption("--environment <environment>", "Environment")
267
+ .action(runClientWithOrg(async (client, options, orgId) => {
268
+ printData(await client.request(`/api/v1/organizations/${orgId}/host/environments/${options.environment}/regions`), options);
269
+ }));
270
+ host.command("routing")
271
+ .requiredOption("--environment <environment>", "Environment")
272
+ .action(runClientWithOrg(async (client, options, orgId) => {
273
+ printData(await client.request(`/api/v1/organizations/${orgId}/host/environments/${options.environment}/routing`), options);
274
+ }));
275
+ }
276
+ function registerServerCommands(program) {
277
+ const servers = program.command("servers").description("Manage MCP servers");
278
+ servers.command("list").action(runClientWithOrg(async (client, options, orgId) => {
279
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers`), options, serverColumns);
280
+ }));
281
+ servers.command("get").argument("<serverId>").action(runClientWithOrg(async (client, options, orgId, serverId) => {
282
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}`), options);
283
+ }));
284
+ servers.command("create")
285
+ .requiredOption("--name <name>", "Server name")
286
+ .option("--slug <slug>", "Server slug")
287
+ .option("--openapi-url <url>", "OpenAPI URL")
288
+ .option("--openapi-file <file>", "OpenAPI JSON/YAML file")
289
+ .option("--runtime-type <runtimeType>", "Runtime type")
290
+ .option("--kind <kind>", "Server kind")
291
+ .option("--description <description>", "Description")
292
+ .action(runClientWithOrg(async (client, options, orgId) => {
293
+ const spec = options.openapiFile ? await readFile(options.openapiFile, "utf8") : undefined;
294
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers`, {
295
+ method: "POST",
296
+ body: omitUndefined({
297
+ name: options.name,
298
+ slug: options.slug,
299
+ kind: options.kind,
300
+ description: options.description,
301
+ runtimeType: options.runtimeType,
302
+ openApiSpecUrl: options.openapiUrl,
303
+ openApiSpecJson: spec,
304
+ }),
305
+ }), options);
306
+ }));
307
+ servers.command("update")
308
+ .argument("<serverId>")
309
+ .option("--name <name>")
310
+ .option("--description <description>")
311
+ .option("--status <status>")
312
+ .option("--server-url <url>")
313
+ .option("--runtime-type <runtimeType>")
314
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
315
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}`, {
316
+ method: "PATCH",
317
+ body: omitUndefined({
318
+ name: options.name,
319
+ description: options.description,
320
+ status: options.status,
321
+ serverUrl: options.serverUrl,
322
+ runtimeType: options.runtimeType,
323
+ }),
324
+ }), options);
325
+ }));
326
+ servers.command("delete")
327
+ .argument("<serverId>")
328
+ .option("--delete-runtime", "Delete hosted runtime")
329
+ .option("--environment <environment>")
330
+ .option("--yes")
331
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
332
+ await requireConfirmation(options, `Delete MCP server '${serverId}'?`);
333
+ await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}`, {
334
+ method: "DELETE",
335
+ query: { deleteHostedRuntime: options.deleteRuntime, environment: options.environment },
336
+ });
337
+ printSuccess(`Deleted MCP server '${serverId}'.`);
338
+ }));
339
+ servers.command("analytics").argument("<serverId>").option("--days <days>", "Days", "7")
340
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
341
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/analytics`, { query: { days: options.days } }), options);
342
+ }));
343
+ servers.command("logs").argument("<serverId>").option("--page <page>", "Page", "1").option("--page-size <pageSize>", "Page size", "50")
344
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
345
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/logs`, { query: { page: options.page, pageSize: options.pageSize } }), options);
346
+ }));
347
+ servers.command("config").argument("<serverId>").action(runClientWithOrg(async (client, options, orgId, serverId) => {
348
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/config`), options);
349
+ }));
350
+ const openapi = servers.command("openapi").description("Manage server OpenAPI config");
351
+ openapi.command("set").argument("<serverId>").option("--url <url>").option("--file <file>")
352
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
353
+ const spec = options.file ? await readFile(options.file, "utf8") : undefined;
354
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/openapi`, {
355
+ method: "POST",
356
+ body: omitUndefined({ openApiSpecUrl: options.url, openApiSpecJson: spec }),
357
+ }), options);
358
+ }));
359
+ openapi.command("refresh").argument("<serverId>")
360
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
361
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/openapi/refresh`, { method: "POST" }), options);
362
+ }));
363
+ servers.command("discover-tools").argument("<serverId>")
364
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
365
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/discover-tools`, { method: "POST" }), options);
366
+ }));
367
+ addJsonGetSet(servers, "auth-config", "auth-config");
368
+ addJsonGetSet(servers, "endpoints", "endpoints");
369
+ servers.command("auth-discovery").argument("<serverId>")
370
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
371
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/auth-discovery`), options);
372
+ }));
373
+ const gateway = servers.command("gateway").description("Manage server gateway attachment");
374
+ gateway.command("get").argument("<serverId>").action(runClientWithOrg(async (client, options, orgId, serverId) => {
375
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/gateway`), options);
376
+ }));
377
+ gateway.command("attach").argument("<serverId>").requiredOption("--gateway <gatewayId>").option("--public-id <publicId>")
378
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
379
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/gateway`, {
380
+ method: "PUT",
381
+ body: omitUndefined({ gatewayId: options.gateway, publicId: options.publicId }),
382
+ }), options);
383
+ }));
384
+ gateway.command("detach").argument("<serverId>").option("--yes")
385
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
386
+ await requireConfirmation(options, `Detach gateway from server '${serverId}'?`);
387
+ await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/gateway`, { method: "DELETE" });
388
+ printSuccess("Gateway detached.");
389
+ }));
390
+ }
391
+ function registerToolCommands(program) {
392
+ const tools = program.command("tools").description("Manage MCP tools");
393
+ tools.command("list").argument("<serverId>").action(runClientWithOrg(async (client, options, orgId, serverId) => {
394
+ const server = await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}`);
395
+ printData(server.tools ?? [], options);
396
+ }));
397
+ tools.command("update").argument("<serverId>").argument("<toolId>")
398
+ .option("--enabled <enabled>")
399
+ .option("--display-name <name>")
400
+ .option("--description <description>")
401
+ .option("--instructions <instructions>")
402
+ .action(runClientWithOrg(async (client, options, orgId, serverId, toolId) => {
403
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/tools/${toolId}`, {
404
+ method: "PATCH",
405
+ body: omitUndefined({
406
+ isEnabled: options.enabled === undefined ? undefined : options.enabled === "true",
407
+ displayName: options.displayName,
408
+ descriptionOverride: options.description,
409
+ instructions: options.instructions,
410
+ }),
411
+ }), options);
412
+ }));
413
+ tools.command("bulk-update").argument("<serverId>").requiredOption("--file <file>")
414
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
415
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/tools`, {
416
+ method: "PUT",
417
+ body: await readJsonFile(options.file),
418
+ }), options);
419
+ }));
420
+ }
421
+ function registerDeploymentCommands(program) {
422
+ program.command("deploy")
423
+ .argument("<serverId>")
424
+ .option("--environment <environment>")
425
+ .option("--region <region>")
426
+ .option("--cpu <cpu>")
427
+ .option("--memory <memory>")
428
+ .option("--min <minReplicas>")
429
+ .option("--max <maxReplicas>")
430
+ .option("--attach-gateway")
431
+ .option("--wait")
432
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
433
+ const result = await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployments`, {
434
+ method: "POST",
435
+ body: omitUndefined({
436
+ environment: options.environment,
437
+ region: options.region,
438
+ cpu: options.cpu,
439
+ memory: options.memory,
440
+ minReplicas: options.min ? Number(options.min) : undefined,
441
+ maxReplicas: options.max ? Number(options.max) : undefined,
442
+ attachGateway: options.attachGateway,
443
+ idempotencyKey: `cli_${Date.now()}_${Math.random().toString(16).slice(2)}`,
444
+ }),
445
+ });
446
+ printData(result, options);
447
+ if (options.wait && result.operationId) {
448
+ await watchOperation(client, options, orgId, serverId, result.operationId);
449
+ }
450
+ }));
451
+ program.command("undeploy").argument("<serverId>").option("--environment <environment>").option("--yes")
452
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
453
+ await requireConfirmation(options, `Undeploy MCP server '${serverId}'?`);
454
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/undeploy`, {
455
+ method: "POST",
456
+ body: omitUndefined({ environment: options.environment }),
457
+ }), options);
458
+ }));
459
+ const deployments = program.command("deployments").description("Inspect deployments");
460
+ deployments.command("list").argument("<serverId>").option("--environment <environment>")
461
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
462
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployments`, { query: { environment: options.environment } }), options);
463
+ }));
464
+ deployments.command("get").argument("<serverId>").argument("<deploymentId>")
465
+ .action(runClientWithOrg(async (client, options, orgId, serverId, deploymentId) => {
466
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployments/${deploymentId}`), options);
467
+ }));
468
+ deployments.command("logs").argument("<serverId>").option("--environment <environment>").option("--tail <tail>", "Tail", "100")
469
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
470
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment/logs`, { query: { environment: options.environment, tail: options.tail } }), options);
471
+ }));
472
+ deployments.command("target-logs").argument("<serverId>").argument("<deploymentId>").argument("<targetId>").option("--tail <tail>", "Tail", "100")
473
+ .action(runClientWithOrg(async (client, options, orgId, serverId, deploymentId, targetId) => {
474
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployments/${deploymentId}/targets/${targetId}/logs`, { query: { tail: options.tail } }), options);
475
+ }));
476
+ const operations = program.command("operations").description("Inspect deployment operations");
477
+ operations.command("list").argument("<serverId>").option("--environment <environment>")
478
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
479
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment-operations`, { query: { environment: options.environment } }), options);
480
+ }));
481
+ operations.command("get").argument("<serverId>").argument("<operationId>")
482
+ .action(runClientWithOrg(async (client, options, orgId, serverId, operationId) => {
483
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment-operations/${operationId}`), options);
484
+ }));
485
+ operations.command("watch").argument("<serverId>").argument("<operationId>")
486
+ .action(runClientWithOrg(async (client, options, orgId, serverId, operationId) => {
487
+ await watchOperation(client, options, orgId, serverId, operationId);
488
+ }));
489
+ const config = program.command("deployment-config").description("Manage deployment config");
490
+ config.command("get").argument("<serverId>").option("--environment <environment>")
491
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
492
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment-config`, { query: { environment: options.environment } }), options);
493
+ }));
494
+ config.command("set").argument("<serverId>").requiredOption("--file <file>")
495
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
496
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment-config`, {
497
+ method: "PUT",
498
+ body: await readJsonFile(options.file),
499
+ }), options);
500
+ }));
501
+ const regions = config.command("regions");
502
+ regions.command("add").argument("<serverId>").argument("<region>").option("--environment <environment>")
503
+ .action(runClientWithOrg(async (client, options, orgId, serverId, region) => {
504
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment-config/regions`, {
505
+ method: "POST",
506
+ body: { region, environment: options.environment },
507
+ }), options);
508
+ }));
509
+ regions.command("update").argument("<serverId>").argument("<region>").option("--environment <environment>").option("--cpu <cpu>").option("--memory <memory>").option("--min <min>").option("--max <max>").option("--enabled <enabled>")
510
+ .action(runClientWithOrg(async (client, options, orgId, serverId, region) => {
511
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment-config/regions/${region}`, {
512
+ method: "PATCH",
513
+ query: { environment: options.environment },
514
+ body: omitUndefined({
515
+ cpu: options.cpu,
516
+ memory: options.memory,
517
+ minReplicas: options.min ? Number(options.min) : undefined,
518
+ maxReplicas: options.max ? Number(options.max) : undefined,
519
+ enabled: options.enabled === undefined ? undefined : options.enabled === "true",
520
+ }),
521
+ }), options);
522
+ }));
523
+ regions.command("remove").argument("<serverId>").argument("<region>").option("--environment <environment>")
524
+ .action(runClientWithOrg(async (client, options, orgId, serverId, region) => {
525
+ await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment-config/regions/${region}`, {
526
+ method: "DELETE",
527
+ query: { environment: options.environment },
528
+ });
529
+ printSuccess(`Removed region '${region}'.`);
530
+ }));
531
+ }
532
+ function registerRuntimeCommands(program) {
533
+ const logs = program.command("logs").description("Stream runtime logs");
534
+ logs.command("stream").argument("<serverId>").requiredOption("--region <region>").option("--environment <environment>")
535
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
536
+ const parser = createParser({
537
+ onEvent(event) {
538
+ if (event.data) {
539
+ console.log(event.data);
540
+ }
541
+ },
542
+ });
543
+ await client.stream(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/runtime-targets/${options.region}/logs/stream`, (chunk) => {
544
+ parser.feed(chunk);
545
+ }, { query: { environment: options.environment } });
546
+ }));
547
+ const runtime = program.command("runtime").description("Runtime target operations");
548
+ for (const action of ["restart", "drain", "enable-routing", "rollback", "refresh-status"]) {
549
+ runtime.command(action).argument("<serverId>").requiredOption("--region <region>").option("--environment <environment>")
550
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
551
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/runtime-targets/${options.region}/${action}`, {
552
+ method: "POST",
553
+ query: { environment: options.environment },
554
+ }), options);
555
+ }));
556
+ }
557
+ runtime.command("scale").argument("<serverId>").requiredOption("--region <region>").option("--environment <environment>").option("--cpu <cpu>").option("--memory <memory>").option("--min <min>").option("--max <max>").option("--enabled <enabled>")
558
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
559
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/runtime-targets/${options.region}/scale`, {
560
+ method: "POST",
561
+ query: { environment: options.environment },
562
+ body: omitUndefined({
563
+ cpu: options.cpu,
564
+ memory: options.memory,
565
+ minReplicas: options.min ? Number(options.min) : undefined,
566
+ maxReplicas: options.max ? Number(options.max) : undefined,
567
+ enabled: options.enabled === undefined ? undefined : options.enabled === "true",
568
+ }),
569
+ }), options);
570
+ }));
571
+ runtime.command("reconcile").argument("<serverId>").option("--environment <environment>")
572
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
573
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/runtime/reconcile`, {
574
+ method: "POST",
575
+ query: { environment: options.environment },
576
+ }), options);
577
+ }));
578
+ const routing = program.command("routing").description("Routing operations");
579
+ for (const action of ["refresh", "reconcile"]) {
580
+ routing.command(action).argument("<serverId>").option("--environment <environment>")
581
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
582
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/routing/${action}`, {
583
+ method: "POST",
584
+ query: { environment: options.environment },
585
+ }), options);
586
+ }));
587
+ }
588
+ program.command("doctor").argument("<serverId>").option("--environment <environment>")
589
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
590
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment-doctor`, {
591
+ method: "POST",
592
+ query: { environment: options.environment },
593
+ }), options);
594
+ }));
595
+ const smoke = program.command("smoke").description("Run MCP smoke checks");
596
+ smoke.command("tools-list").argument("<serverId>")
597
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
598
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/mcp-smoke/tools-list`, { method: "POST" }), options);
599
+ }));
600
+ smoke.command("call").argument("<serverId>").argument("<toolName>").option("--args <json>").option("--file <file>")
601
+ .action(runClientWithOrg(async (client, options, orgId, serverId, toolName) => {
602
+ const args = options.file ? await readJsonFile(options.file) : options.args ? JSON.parse(options.args) : {};
603
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/mcp-smoke/tools/${encodeURIComponent(toolName)}/call`, {
604
+ method: "POST",
605
+ body: { arguments: args },
606
+ }), options);
607
+ }));
608
+ }
609
+ function registerGatewayCommands(program) {
610
+ const gateways = program.command("gateways").description("Manage gateways");
611
+ gateways.command("list").action(runClientWithOrg(async (client, options, orgId) => {
612
+ printData(await client.request(`/api/v1/organizations/${orgId}/gateways`), options, gatewayColumns);
613
+ }));
614
+ gateways.command("get").argument("<gatewayId>").action(runClientWithOrg(async (client, options, orgId, gatewayId) => {
615
+ printData(await client.request(`/api/v1/organizations/${orgId}/gateways/${gatewayId}`), options);
616
+ }));
617
+ gateways.command("create")
618
+ .requiredOption("--name <name>")
619
+ .requiredOption("--provider <provider>")
620
+ .requiredOption("--auth-server-url <url>")
621
+ .requiredOption("--client-id <clientId>")
622
+ .requiredOption("--resource <resource>")
623
+ .requiredOption("--scopes <scopes>")
624
+ .option("--description <description>")
625
+ .action(runClientWithOrg(async (client, options, orgId) => {
626
+ printData(await client.request(`/api/v1/organizations/${orgId}/gateways`, {
627
+ method: "POST",
628
+ body: omitUndefined({
629
+ name: options.name,
630
+ description: options.description,
631
+ provider: options.provider,
632
+ authorizationServerUrl: options.authServerUrl,
633
+ clientId: options.clientId,
634
+ resource: options.resource,
635
+ scopes: splitList(options.scopes),
636
+ }),
637
+ }), options);
638
+ }));
639
+ gateways.command("update").argument("<gatewayId>").requiredOption("--file <file>")
640
+ .action(runClientWithOrg(async (client, options, orgId, gatewayId) => {
641
+ printData(await client.request(`/api/v1/organizations/${orgId}/gateways/${gatewayId}`, {
642
+ method: "PUT",
643
+ body: await readJsonFile(options.file),
644
+ }), options);
645
+ }));
646
+ gateways.command("delete").argument("<gatewayId>").option("--yes")
647
+ .action(runClientWithOrg(async (client, options, orgId, gatewayId) => {
648
+ await requireConfirmation(options, `Delete gateway '${gatewayId}'?`);
649
+ await client.request(`/api/v1/organizations/${orgId}/gateways/${gatewayId}`, { method: "DELETE" });
650
+ printSuccess(`Deleted gateway '${gatewayId}'.`);
651
+ }));
652
+ for (const child of ["servers", "grants", "logs"]) {
653
+ gateways.command(child).argument("<gatewayId>")
654
+ .action(runClientWithOrg(async (client, options, orgId, gatewayId) => {
655
+ printData(await client.request(`/api/v1/organizations/${orgId}/gateways/${gatewayId}/${child}`), options);
656
+ }));
657
+ }
658
+ }
659
+ function registerGatewayPublicCommands(program) {
660
+ const gatewayPublic = program.command("gateway-public").description("Inspect hosted MCP gateway public endpoints");
661
+ gatewayPublic.command("metadata").argument("<publicId>").action(runClient(async (client, options, publicId) => {
662
+ const base = `/api/v1/gateway/${publicId}`;
663
+ const metadata = {
664
+ protectedResource: await client.request(`${base}/.well-known/oauth-protected-resource`, { noAuth: true }),
665
+ authorizationServer: await client.request(`${base}/.well-known/oauth-authorization-server`, { noAuth: true }),
666
+ openidConfiguration: await client.request(`${base}/.well-known/openid-configuration`, { noAuth: true }),
667
+ jwks: await client.request(`${base}/.well-known/jwks.json`, { noAuth: true }),
668
+ };
669
+ printData(metadata, options);
670
+ }));
671
+ gatewayPublic.command("mcp-tools").argument("<publicId>").action(runClient(async (client, options, publicId) => {
672
+ printData(await client.request(`/api/v1/gateway/${publicId}/mcp`, {
673
+ method: "POST",
674
+ body: { jsonrpc: "2.0", id: "cli-tools-list", method: "tools/list" },
675
+ noAuth: true,
676
+ }), options);
677
+ }));
678
+ gatewayPublic.command("mcp-call").argument("<publicId>").argument("<toolName>").option("--args <json>").option("--file <file>")
679
+ .action(runClient(async (client, options, publicId, toolName) => {
680
+ const args = options.file ? await readJsonFile(options.file) : options.args ? JSON.parse(options.args) : {};
681
+ printData(await client.request(`/api/v1/gateway/${publicId}/mcp`, {
682
+ method: "POST",
683
+ body: { jsonrpc: "2.0", id: "cli-tool-call", method: "tools/call", params: { name: toolName, arguments: args } },
684
+ noAuth: true,
685
+ }), options);
686
+ }));
687
+ }
688
+ function registerAgentCommands(program) {
689
+ const agents = program.command("agents").description("Manage agents");
690
+ agents.command("model-options").action(runClientWithOrg(async (client, options, orgId) => {
691
+ printData(await client.request(`/api/v1/organizations/${orgId}/agent-model-options`), options);
692
+ }));
693
+ agents.command("list").action(runClientWithOrg(async (client, options, orgId) => {
694
+ printData(await client.request(`/api/v1/organizations/${orgId}/agents`), options, agentColumns);
695
+ }));
696
+ agents.command("get").argument("<agentId>").action(runClientWithOrg(async (client, options, orgId, agentId) => {
697
+ printData(await client.request(`/api/v1/organizations/${orgId}/agents/${agentId}`), options);
698
+ }));
699
+ agents.command("create").requiredOption("--file <file>")
700
+ .action(runClientWithOrg(async (client, options, orgId) => {
701
+ printData(await client.request(`/api/v1/organizations/${orgId}/agents`, {
702
+ method: "POST",
703
+ body: await readJsonFile(options.file),
704
+ }), options);
705
+ }));
706
+ agents.command("update").argument("<agentId>").requiredOption("--file <file>")
707
+ .action(runClientWithOrg(async (client, options, orgId, agentId) => {
708
+ printData(await client.request(`/api/v1/organizations/${orgId}/agents/${agentId}`, {
709
+ method: "PATCH",
710
+ body: await readJsonFile(options.file),
711
+ }), options);
712
+ }));
713
+ agents.command("delete").argument("<agentId>").option("--yes")
714
+ .action(runClientWithOrg(async (client, options, orgId, agentId) => {
715
+ await requireConfirmation(options, `Delete agent '${agentId}'?`);
716
+ await client.request(`/api/v1/organizations/${orgId}/agents/${agentId}`, { method: "DELETE" });
717
+ printSuccess(`Deleted agent '${agentId}'.`);
718
+ }));
719
+ agents.command("usage").argument("<agentId>").action(runClientWithOrg(async (client, options, orgId, agentId) => {
720
+ printData(await client.request(`/api/v1/organizations/${orgId}/agents/${agentId}/usage`), options);
721
+ }));
722
+ agents.command("embed-usage").action(runClientWithOrg(async (client, options, orgId) => {
723
+ printData(await client.request(`/api/v1/organizations/${orgId}/embed-usage`), options);
724
+ }));
725
+ agents.command("chat").argument("<agentId>").requiredOption("--message <message>")
726
+ .action(runClient(async (client, options, agentId) => {
727
+ printData(await client.request("/api/v1/chat", {
728
+ method: "POST",
729
+ body: { agentId, message: options.message },
730
+ }), options);
731
+ }));
732
+ const conversations = agents.command("conversations").description("Inspect agent conversations");
733
+ conversations.command("list").argument("<agentId>").option("--cursor <cursor>").option("--page-size <pageSize>", "Page size")
734
+ .action(runClientWithOrg(async (client, options, orgId, agentId) => {
735
+ printData(await client.request(`/api/v1/organizations/${orgId}/agents/${agentId}/conversations`, {
736
+ query: { cursor: options.cursor, pageSize: options.pageSize },
737
+ }), options);
738
+ }));
739
+ conversations.command("get").argument("<agentId>").argument("<conversationId>")
740
+ .action(runClientWithOrg(async (client, options, orgId, agentId, conversationId) => {
741
+ printData(await client.request(`/api/v1/organizations/${orgId}/agents/${agentId}/conversations/${conversationId}`), options);
742
+ }));
743
+ conversations.command("messages").argument("<agentId>").argument("<conversationId>")
744
+ .action(runClientWithOrg(async (client, options, orgId, agentId, conversationId) => {
745
+ printData(await client.request(`/api/v1/organizations/${orgId}/agents/${agentId}/conversations/${conversationId}/messages`), options);
746
+ }));
747
+ }
748
+ function registerCompletionCommand(program) {
749
+ program.command("completion")
750
+ .argument("<shell>", "zsh, bash, or fish")
751
+ .description("Print shell completion setup guidance")
752
+ .action(run(async (_options, shell) => {
753
+ printInfo(`Shell completion for ${shell} will be generated by the npm package release script.`);
754
+ }));
755
+ }
756
+ function addJsonGetSet(servers, commandName, endpoint) {
757
+ const command = servers.command(commandName);
758
+ command.command("get").argument("<serverId>")
759
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
760
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/${endpoint}`), options);
761
+ }));
762
+ command.command("set").argument("<serverId>").requiredOption("--file <file>")
763
+ .action(runClientWithOrg(async (client, options, orgId, serverId) => {
764
+ printData(await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/${endpoint}`, {
765
+ method: "PUT",
766
+ body: await readJsonFile(options.file),
767
+ }), options);
768
+ }));
769
+ }
770
+ function run(action) {
771
+ return async (...args) => {
772
+ const command = args.at(-1);
773
+ const options = command.optsWithGlobals();
774
+ await action(options, ...args.slice(0, -1));
775
+ };
776
+ }
777
+ function runClient(action) {
778
+ return run(async (options, ...args) => {
779
+ const client = await McpstackClient.create(options);
780
+ await action(client, options, ...args);
781
+ });
782
+ }
783
+ function runClientWithOrg(action) {
784
+ return runClient(async (client, options, ...args) => {
785
+ const orgId = await client.resolveOrgId(options.org);
786
+ await action(client, options, orgId, ...args);
787
+ });
788
+ }
789
+ async function requireConfirmation(options, message) {
790
+ if (options.yes) {
791
+ return;
792
+ }
793
+ const rl = createInterface({ input, output });
794
+ try {
795
+ const answer = await rl.question(`${message} Type 'yes' to continue: `);
796
+ if (answer.trim().toLowerCase() !== "yes") {
797
+ throw new Error("Cancelled.");
798
+ }
799
+ }
800
+ finally {
801
+ rl.close();
802
+ }
803
+ }
804
+ async function readJsonFile(path) {
805
+ return JSON.parse(await readFile(path, "utf8"));
806
+ }
807
+ function omitUndefined(value) {
808
+ return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
809
+ }
810
+ function splitList(value) {
811
+ return value.split(/[,\s]+/).map((item) => item.trim()).filter(Boolean);
812
+ }
813
+ async function watchOperation(client, options, orgId, serverId, operationId) {
814
+ const timeoutMs = Number(options.timeout ?? 600) * 1000;
815
+ const started = Date.now();
816
+ while (Date.now() - started < timeoutMs) {
817
+ const operation = await client.request(`/api/v1/organizations/${orgId}/mcp-servers/${serverId}/deployment-operations/${operationId}`);
818
+ printData(operation, { ...options, output: "json" });
819
+ const status = String(operation.status ?? "").toLowerCase();
820
+ if (["succeeded", "failed", "cancelled", "canceled", "completed"].includes(status)) {
821
+ return;
822
+ }
823
+ await new Promise((resolve) => setTimeout(resolve, 5_000));
824
+ }
825
+ throw new Error(`Timed out waiting for operation '${operationId}'.`);
826
+ }
827
+ //# sourceMappingURL=commands.js.map