@maka/maka-cli 5.142.0 → 5.143.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.142.0",
3
+ "version": "5.143.1",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 3.x applications using React.",
@@ -49,6 +49,7 @@
49
49
  "@inquirer/prompts": "^8.5.2",
50
50
  "@maka/meteor-sdk": "^0.2.13",
51
51
  "@maka/tar": "^6.2.2",
52
+ "@modelcontextprotocol/sdk": "^1.30.0",
52
53
  "blessed": "^0.1.81",
53
54
  "chalk": "^4.1.2",
54
55
  "change-case": "^5.4.4",
@@ -70,7 +71,8 @@
70
71
  "tar": "^7.4.3",
71
72
  "uuid": "^14.0.1",
72
73
  "ws": "^8.21.3",
73
- "xtend": "^4.0.2"
74
+ "xtend": "^4.0.2",
75
+ "zod": "^4.5.4"
74
76
  },
75
77
  "devDependencies": {
76
78
  "@aws-sdk/types": "^3.974.4",
@@ -115,8 +115,15 @@ export async function fetchReviewQueue() {
115
115
  }
116
116
  /** Answer, ask, or annotate one item. `kind` defaults to a plain note;
117
117
  * a play-tester replying to a question posts an 'answer'. */
118
- export async function postBacklogComment(itemId, text, opts = {}) {
119
- const token = authToken();
118
+ export async function postBacklogComment(itemId, text, opts = {},
119
+ // ADMIN OVERRIDE (2026-09-08). The MCP admin server (mcp.sub.cmd.ts)
120
+ // reuses this for notes/questions posted as the service account,
121
+ // which holds no entry in ~/.maka/global-config.json for authToken()
122
+ // to find. Optional and trailing so every existing human-CLI call
123
+ // site is untouched -- omitted, this is exactly the function it always
124
+ // was.
125
+ tokenOverride) {
126
+ const token = tokenOverride ?? authToken();
120
127
  if (!token)
121
128
  return 'no-login';
122
129
  try {
@@ -375,8 +382,12 @@ export async function setRole(action, email, role) {
375
382
  * in-review first, then open. `all` asks for everything anyway; a
376
383
  * specific `status` is always honoured exactly.
377
384
  */
378
- export async function fetchBacklog(status, opts = {}) {
379
- const token = authToken();
385
+ export async function fetchBacklog(status, opts = {},
386
+ // ADMIN OVERRIDE, same reasoning as postBacklogComment's: the MCP
387
+ // admin server has no human login for authToken() to find. Optional
388
+ // and trailing so every existing call site is untouched.
389
+ tokenOverride) {
390
+ const token = tokenOverride ?? authToken();
380
391
  if (!token)
381
392
  return { outcome: 'no-login' };
382
393
  try {
@@ -395,4 +406,162 @@ export async function fetchBacklog(status, opts = {}) {
395
406
  return { outcome: 'offline' };
396
407
  }
397
408
  }
409
+ // ---------------------------------------------------------------------
410
+ // ADMIN CALLS (2026-09-08), added for the MCP admin server
411
+ // (sideQuest-backlog-mcp.sub.cmd.ts). Every function below takes its
412
+ // token EXPLICITLY rather than falling back to authToken() -- these are
413
+ // only ever called with the M2M service account's session, never a
414
+ // human's, and a silent fallback to whichever human happens to be
415
+ // logged in on this machine would be exactly the wrong default for an
416
+ // admin action. All of them are the same house shape as everything
417
+ // above: swallow errors, return an outcome, let the caller turn it into
418
+ // prose (or, here, into an MCP tool error).
419
+ // ---------------------------------------------------------------------
420
+ /** One item, in full -- including fields /list withholds from a caller
421
+ * who is not its reporter or an admin. `GET item/:id` is gated the same
422
+ * as /list (mayUseBacklog), so this never needs a special admin path
423
+ * server-side; the token being an admin's is what gets it the fuller
424
+ * read. */
425
+ export async function fetchBacklogItemById(itemId, token) {
426
+ try {
427
+ const res = await request('GET', `/${API_BASE}/item/${encodeURIComponent(itemId)}`, token);
428
+ if (res.statusCode === 401)
429
+ return { outcome: 'expired' };
430
+ if (res.statusCode === 403)
431
+ return { outcome: 'forbidden' };
432
+ if (res.statusCode === 404)
433
+ return { outcome: 'rejected' };
434
+ if (res.statusCode !== 200)
435
+ return { outcome: 'offline' };
436
+ const parsed = parseData(res.data);
437
+ return { outcome: 'ok', item: parsed?.item };
438
+ }
439
+ catch {
440
+ return { outcome: 'offline' };
441
+ }
442
+ }
443
+ /** THE ACTUAL RESOLUTION MOVE: attach evidence and/or advance status.
444
+ * Admin-only server-side (`PATCH item/:id`), and gated there exactly as
445
+ * documented in game-backlog-v1-rest-api.ts -- entering `in-review` or
446
+ * `complete` without commits is refused with a `message` this function
447
+ * passes straight through rather than flattening into a generic
448
+ * "rejected", because that message is the instruction for what to do
449
+ * differently (the MCP tool surfaces it verbatim to whoever is holding
450
+ * the AI's hand at that point). */
451
+ export async function patchBacklogItem(itemId, body, token) {
452
+ try {
453
+ const res = await request('PATCH', `/${API_BASE}/item/${encodeURIComponent(itemId)}`, token, body);
454
+ if (res.statusCode === 200 || res.statusCode === 201) {
455
+ const parsed = parseData(res.data);
456
+ return { outcome: 'ok', status: parsed?.status, commits: parsed?.commits };
457
+ }
458
+ if (res.statusCode === 401)
459
+ return { outcome: 'expired' };
460
+ if (res.statusCode === 403)
461
+ return { outcome: 'forbidden' };
462
+ if (res.statusCode === 400 || res.statusCode === 404) {
463
+ const parsed = parseData(res.data);
464
+ return { outcome: 'rejected', message: parsed?.message };
465
+ }
466
+ return { outcome: 'offline' };
467
+ }
468
+ catch {
469
+ return { outcome: 'offline' };
470
+ }
471
+ }
472
+ /** ASK THE SERVER TO DRAFT A BENCH for one item. Lands in `reproDraft`,
473
+ * never `repro` -- promoting a draft to a real bench is a separate,
474
+ * deliberate PATCH (patchBacklogItem with a `repro` body), same as the
475
+ * web board and the REST route's own doc comment describe. Admin-only,
476
+ * costs a model call server-side. */
477
+ export async function draftBacklogBench(itemId, token, verbs) {
478
+ try {
479
+ const res = await request('POST', `/${API_BASE}/item/${encodeURIComponent(itemId)}/bench-draft`, token, verbs ? { verbs } : undefined);
480
+ if (res.statusCode === 200 || res.statusCode === 201) {
481
+ const parsed = parseData(res.data);
482
+ return { outcome: 'ok', reproDraft: parsed?.reproDraft };
483
+ }
484
+ if (res.statusCode === 401)
485
+ return { outcome: 'expired' };
486
+ if (res.statusCode === 403)
487
+ return { outcome: 'forbidden' };
488
+ if (res.statusCode === 400 || res.statusCode === 404) {
489
+ const parsed = parseData(res.data);
490
+ return { outcome: 'rejected', message: parsed?.message, code: parsed?.code };
491
+ }
492
+ return { outcome: 'offline' };
493
+ }
494
+ catch {
495
+ return { outcome: 'offline' };
496
+ }
497
+ }
498
+ /** THE BATCH FORM -- named ids only, never a selector (see the route's
499
+ * own doc comment on why: a sweep over a spend path is exactly the
500
+ * failure mode the per-item rule exists to prevent). Capped at 20 by
501
+ * the server; this does not re-enforce that limit, so a caller sees the
502
+ * server's own refusal if it is exceeded. */
503
+ export async function draftBacklogBenches(ids, token) {
504
+ try {
505
+ const res = await request('POST', `/${API_BASE}/bench-drafts`, token, { ids });
506
+ if (res.statusCode === 200 || res.statusCode === 201) {
507
+ const parsed = parseData(res.data);
508
+ return { outcome: 'ok', drafted: parsed?.drafted, attempted: parsed?.attempted, results: parsed?.results };
509
+ }
510
+ if (res.statusCode === 401)
511
+ return { outcome: 'expired' };
512
+ if (res.statusCode === 403)
513
+ return { outcome: 'forbidden' };
514
+ if (res.statusCode === 400) {
515
+ const parsed = parseData(res.data);
516
+ return { outcome: 'rejected', message: parsed?.message };
517
+ }
518
+ return { outcome: 'offline' };
519
+ }
520
+ catch {
521
+ return { outcome: 'offline' };
522
+ }
523
+ }
524
+ /** RE-JUDGE ITEMS AGAINST THE RULEBOOKS. `all: true` re-grades the whole
525
+ * board (a model call per item -- see the route's own doc comment on
526
+ * why that is opt-in); the default only catches items whose first pass
527
+ * ran without session context and now has one. */
528
+ export async function revetBacklog(token, opts = {}) {
529
+ try {
530
+ const res = await request('POST', `/${API_BASE}/revet`, token, opts);
531
+ if (res.statusCode === 200 || res.statusCode === 201) {
532
+ const parsed = parseData(res.data);
533
+ return { outcome: 'ok', ...parsed };
534
+ }
535
+ if (res.statusCode === 401)
536
+ return { outcome: 'expired' };
537
+ if (res.statusCode === 403)
538
+ return { outcome: 'forbidden' };
539
+ return { outcome: 'offline' };
540
+ }
541
+ catch {
542
+ return { outcome: 'offline' };
543
+ }
544
+ }
545
+ /** THE M2M TOKEN EXCHANGE. Not gated behind authToken() at all -- there
546
+ * is no Meteor session yet at the point this is called, that is
547
+ * precisely what it establishes. Hits `/api/v1/auth`, not
548
+ * `/api/v1/game-backlog`; `request()` takes a raw path, so this is the
549
+ * one caller in the file that does not go through API_BASE. See
550
+ * utilities/service-account-auth.ts for what obtains the Auth0 token
551
+ * this expects and what caches the result. */
552
+ export async function mintServiceAccountToken(auth0Token) {
553
+ try {
554
+ const res = await request('POST', '/api/v1/auth/m2m-login', '', { auth0Token });
555
+ if (res.statusCode === 200) {
556
+ const parsed = parseData(res.data);
557
+ return { outcome: 'ok', authToken: parsed?.authToken, userId: parsed?.userId, when: parsed?.when };
558
+ }
559
+ if (res.statusCode === 401)
560
+ return { outcome: 'expired' };
561
+ return { outcome: 'offline' };
562
+ }
563
+ catch {
564
+ return { outcome: 'offline' };
565
+ }
566
+ }
398
567
  //# sourceMappingURL=backlog.js.map
@@ -0,0 +1,68 @@
1
+ import https from 'https';
2
+ import { mintServiceAccountToken } from './backlog.js';
3
+ /** Client-credentials grant, direct to Auth0. Throws on any failure --
4
+ * there is no partial-success shape worth returning here, and the
5
+ * caller (ensureServiceSession below) is the one place that decides
6
+ * what a failure means for the MCP server's own state. */
7
+ async function getAuth0AccessToken(creds) {
8
+ const body = new URLSearchParams({
9
+ grant_type: 'client_credentials',
10
+ client_id: creds.clientId,
11
+ client_secret: creds.clientSecret,
12
+ audience: creds.audience,
13
+ }).toString();
14
+ return new Promise((resolve, reject) => {
15
+ const req = https.request({
16
+ hostname: creds.domain,
17
+ port: 443,
18
+ path: '/oauth/token',
19
+ method: 'POST',
20
+ timeout: 10000,
21
+ headers: {
22
+ 'Content-Type': 'application/x-www-form-urlencoded',
23
+ 'Content-Length': Buffer.byteLength(body),
24
+ },
25
+ }, (res) => {
26
+ let data = '';
27
+ res.on('data', (chunk) => { data += chunk; });
28
+ res.on('end', () => {
29
+ if (res.statusCode !== 200) {
30
+ reject(new Error(`Auth0 token request failed (${res.statusCode}): ${data.slice(0, 300)}`));
31
+ return;
32
+ }
33
+ try {
34
+ const parsed = JSON.parse(data);
35
+ if (!parsed.access_token)
36
+ throw new Error('no access_token in Auth0 response');
37
+ resolve(parsed.access_token);
38
+ }
39
+ catch (e) {
40
+ reject(e instanceof Error ? e : new Error(String(e)));
41
+ }
42
+ });
43
+ });
44
+ req.on('timeout', () => req.destroy(new Error('Auth0 token request timeout')));
45
+ req.on('error', reject);
46
+ req.write(body);
47
+ req.end();
48
+ });
49
+ }
50
+ let cachedToken;
51
+ /** THE ONE ENTRY POINT mcp.sub.cmd.ts calls. Returns a Meteor
52
+ * x-auth-token for the service account, minting one on first use and
53
+ * reusing it after that. `force: true` is what a 401 from any admin
54
+ * call should trigger -- the cached token may have been revoked
55
+ * (logoutAll, expiry) and one retry with a freshly minted token is
56
+ * cheap insurance against surfacing a spurious failure. */
57
+ export async function ensureServiceSession(creds, opts = {}) {
58
+ if (cachedToken && !opts.force)
59
+ return cachedToken;
60
+ const auth0Token = await getAuth0AccessToken(creds);
61
+ const minted = await mintServiceAccountToken(auth0Token);
62
+ if (minted.outcome !== 'ok' || !minted.authToken) {
63
+ throw new Error(`Could not establish a service-account session (${minted.outcome}).`);
64
+ }
65
+ cachedToken = minted.authToken;
66
+ return cachedToken;
67
+ }
68
+ //# sourceMappingURL=service-account-auth.js.map
@@ -0,0 +1,175 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { Command } from '../../command.js';
4
+ import { UsageError } from '../../error.js';
5
+ export function registerBacklogMcp(parent) {
6
+ return Command.create({
7
+ name: 'mcp',
8
+ usage: 'maka play:backlog:mcp [--env <env>] [--site <path>]',
9
+ // OFF, DELIBERATELY -- see the file header. The framework's own
10
+ // check fires before this handler runs at all and only knows how
11
+ // to look at process.cwd(); --site is this command's own way to
12
+ // satisfy the same requirement, so it needs the chance to try that
13
+ // first rather than being refused upstream of ever seeing it.
14
+ mustBeInMakaProject: false,
15
+ // NO maxArgLength HERE, DELIBERATELY. Command's own arg-length check
16
+ // is `args.length >= maxArgLength` (command.ts), so `maxArgLength: 0`
17
+ // refuses EVERY invocation regardless of what was typed -- an
18
+ // off-by-one in the shared framework, not something to work around
19
+ // per-command. No command in this codebase sets it to 0 for exactly
20
+ // that reason. This command takes no positionals; any stray one is
21
+ // simply ignored rather than rejected.
22
+ validOpts: [
23
+ {
24
+ name: 'env',
25
+ description: 'Which maka-cli.com config/<env>/process.env supplies the Auth0 M2M credentials (default: production).',
26
+ },
27
+ {
28
+ name: 'site',
29
+ description: 'Path to a maka-cli.com checkout. Defaults to resolving one from the current working directory if omitted -- pass this explicitly when the launching MCP client does not set cwd reliably.',
30
+ },
31
+ ],
32
+ shortDesc: 'Run an MCP server exposing game-backlog admin tools, authenticated as the M2M service account.',
33
+ description: `Starts a local MCP server (stdio transport) with tools for listing and reading
34
+ backlog items, attaching evidence and advancing status, drafting an AI bench, and posting
35
+ admin notes -- all against maka-cli.com's existing game-backlog REST API, authenticated as
36
+ the claude-agent@maka-cli.com service account via the Auth0 M2M token exchange
37
+ (api/v1/auth/m2m-login). Needs a maka-cli.com checkout to read AUTHZERO_DOMAIN /
38
+ AUTHZERO_CLIENT_ID / AUTHZERO_SECRET / AUTHZERO_AUDIENCE from config/<env>/process.env --
39
+ pass --site explicitly, or run with cwd already set to one.`,
40
+ examples: [
41
+ 'maka play:backlog:mcp --site C:\\path\\to\\maka-cli.com',
42
+ 'maka play:backlog:mcp --env local --site C:\\path\\to\\maka-cli.com',
43
+ ],
44
+ }, async function (_args, opts) {
45
+ const env = opts.env ?? 'production';
46
+ let processEnvPath;
47
+ if (opts.site) {
48
+ const siteConfigDir = path.join(path.resolve(opts.site), 'config', env);
49
+ processEnvPath = path.join(siteConfigDir, 'process.env');
50
+ if (!fs.existsSync(processEnvPath)) {
51
+ throw new UsageError(`No config/${env}/process.env under --site ${opts.site} (looked for ${processEnvPath}).`);
52
+ }
53
+ }
54
+ else {
55
+ if (!this.cfg.checkConfigExists(env)) {
56
+ // Pre-handshake failure -- nothing has touched stdout as MCP
57
+ // protocol yet, so a plain thrown Error (which Command's own
58
+ // wrapper reports via Log.error) is fine here.
59
+ throw new UsageError(`Not in a maka-cli.com checkout and no --site given (looked for config/${env} under ${process.cwd()}).`);
60
+ }
61
+ const configPath = this.cfg.getAppConfigPath(env);
62
+ processEnvPath = path.join(configPath, 'process.env');
63
+ }
64
+ const [domain, clientId, clientSecret, audience] = await Promise.all([
65
+ this.dotenvx.get('AUTHZERO_DOMAIN', processEnvPath),
66
+ this.dotenvx.get('AUTHZERO_CLIENT_ID', processEnvPath),
67
+ this.dotenvx.get('AUTHZERO_SECRET', processEnvPath),
68
+ this.dotenvx.get('AUTHZERO_AUDIENCE', processEnvPath),
69
+ ]);
70
+ if (!domain || !clientId || !clientSecret || !audience) {
71
+ throw new Error(`AUTHZERO_DOMAIN/CLIENT_ID/SECRET/AUDIENCE must all be set in config/${env}/process.env`);
72
+ }
73
+ const creds = { domain, clientId, clientSecret, audience };
74
+ // WHICH HOSTNAME THE HTTP TRANSPORT HITS is a SEPARATE switch from
75
+ // which config supplied these credentials -- utilities/backlog.ts's
76
+ // request() picks localhost vs www.maka-cli.com off `MAKA_DEV`
77
+ // (read once, at that module's first import), which is this CLI's
78
+ // own long-standing convention and has nothing to do with `--env`
79
+ // by default. Without this, `--env local` would mint a token
80
+ // against maka-cli.com's real local Auth0 credentials and then
81
+ // still call PRODUCTION with it. Only `local` flips it -- there is
82
+ // no separately-served "development" host in this cluster today
83
+ // (nothing deploys that environment; see maka-cli.com's CLAUDE.md),
84
+ // so that one still targets production same as the default.
85
+ if (env === 'local' && !process.env.MAKA_DEV) {
86
+ process.env.MAKA_DEV = 'True';
87
+ }
88
+ // Heavy/optional imports, lazily loaded inside the handler -- the
89
+ // same discipline sideQuest-backlog.sub.cmd.ts documents for the
90
+ // game graph, so `maka --help` and unrelated commands never pay for
91
+ // these.
92
+ const { McpServer } = await import('@modelcontextprotocol/sdk/server/mcp.js');
93
+ const { StdioServerTransport } = await import('@modelcontextprotocol/sdk/server/stdio.js');
94
+ const { z } = await import('zod');
95
+ const { ensureServiceSession } = await import('./sideQuest/utilities/service-account-auth.js');
96
+ const { fetchBacklog, fetchBacklogItemById, patchBacklogItem, draftBacklogBench, postBacklogComment, BACKLOG_STATUSES, } = await import('./sideQuest/utilities/backlog.js');
97
+ const statusEnum = BACKLOG_STATUSES;
98
+ /** Every tool call goes through this: reuse the cached session, and
99
+ * on the first `expired` outcome, mint a fresh one and retry
100
+ * exactly once. A second failure is surfaced rather than looping
101
+ * forever. */
102
+ async function withSession(call) {
103
+ const token = await ensureServiceSession(creds);
104
+ const result = await call(token);
105
+ if (result.outcome !== 'expired')
106
+ return result;
107
+ const fresh = await ensureServiceSession(creds, { force: true });
108
+ return call(fresh);
109
+ }
110
+ /** House shape for a tool result: JSON in, MCP text-content out. A
111
+ * non-'ok' outcome is reported as `isError` so the calling model
112
+ * sees a refusal it can act on (or explain to the human) rather
113
+ * than a raw protocol exception. */
114
+ function toolResult(value) {
115
+ return {
116
+ content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],
117
+ isError: value.outcome !== 'ok',
118
+ };
119
+ }
120
+ const server = new McpServer({ name: 'maka-play-backlog', version: '1.0.0' });
121
+ server.registerTool('backlog_list', {
122
+ title: 'List backlog items',
123
+ description: 'List game-backlog items, optionally filtered by status. Closed (complete) items are excluded unless a status is given explicitly.',
124
+ inputSchema: {
125
+ status: z.enum(statusEnum).optional()
126
+ .describe('Filter to one status. Omit to see everything not yet complete.'),
127
+ },
128
+ }, async ({ status }) => toolResult(await withSession((token) => fetchBacklog(status, { all: false }, token))));
129
+ server.registerTool('backlog_get', {
130
+ title: 'Read one backlog item',
131
+ description: 'Read a single game-backlog item in full, by id.',
132
+ inputSchema: { itemId: z.string().describe('The backlog item id.') },
133
+ }, async ({ itemId }) => toolResult(await withSession((token) => fetchBacklogItemById(itemId, token))));
134
+ server.registerTool('backlog_comment', {
135
+ title: 'Comment on a backlog item',
136
+ description: 'Post an admin note or a question to the reporter on a backlog item. A "question" with awaitingReporter puts it in the reporter\'s review queue and tomorrow\'s digest; a "note" does neither.',
137
+ inputSchema: {
138
+ itemId: z.string(),
139
+ text: z.string().min(1),
140
+ kind: z.enum(['question', 'note']).default('note'),
141
+ awaitingReporter: z.boolean().optional()
142
+ .describe('Only meaningful with kind "question" -- puts this item in the reporter\'s queue.'),
143
+ },
144
+ }, async ({ itemId, text, kind, awaitingReporter }) => toolResult(await withSession(async (token) => ({
145
+ outcome: await postBacklogComment(itemId, text, { kind, awaitingReporter }, token),
146
+ }))));
147
+ server.registerTool('backlog_advance', {
148
+ title: 'Advance a backlog item (attach evidence / change status)',
149
+ description: `Move a backlog item's status and/or attach fix-commit evidence. This is the
150
+ actual resolution action -- it will be refused by the server (with a message explaining
151
+ why) if you try to enter "in-review" or "complete" without at least one commit attached,
152
+ or "complete" without the reporter's sign-off already on the item. This tool never signs
153
+ off or rejects a fix on the reporter's behalf -- only the person who filed the item can
154
+ do that, in-game.`,
155
+ inputSchema: {
156
+ itemId: z.string(),
157
+ status: z.enum(statusEnum),
158
+ commits: z.array(z.string()).optional()
159
+ .describe('Fix-commit SHAs to attach (merged with whatever is already there).'),
160
+ },
161
+ }, async ({ itemId, status, commits }) => toolResult(await withSession((token) => patchBacklogItem(itemId, { status, commits }, token))));
162
+ server.registerTool('backlog_draft_bench', {
163
+ title: 'Draft an AI bench for a backlog item',
164
+ description: 'Ask the server to AI-draft a two-room repro bench for one item (lands in reproDraft, not repro -- a human has to dry-run and promote it separately via backlog_advance with a repro body). Costs a model call server-side.',
165
+ inputSchema: {
166
+ itemId: z.string(),
167
+ verbs: z.array(z.string()).optional().describe('The verbs available to reference in the bench steps.'),
168
+ },
169
+ }, async ({ itemId, verbs }) => toolResult(await withSession((token) => draftBacklogBench(itemId, token, verbs))));
170
+ const transport = new StdioServerTransport();
171
+ await server.connect(transport);
172
+ console.error('[play:backlog:mcp] ready');
173
+ }, parent);
174
+ }
175
+ //# sourceMappingURL=sideQuest-backlog-mcp.sub.cmd.js.map
@@ -1,5 +1,6 @@
1
1
  import ShadowrunCommand, { ShadowrunCompat } from './sideQuest.sub.cmd.js';
2
2
  import { Command } from '../../command.js';
3
+ import { registerBacklogMcp } from './sideQuest-backlog-mcp.sub.cmd.js';
3
4
  // Registered under BOTH the new spelling (play:backlog) and the compat
4
5
  // sub (play:shadowrun:backlog) from ONE definition -- two commands, one
5
6
  // options object, one handler, so the pair cannot drift.
@@ -62,6 +63,9 @@ const registerBacklog = (parent) => Command.create({
62
63
  detachAsked,
63
64
  });
64
65
  }, parent);
65
- registerBacklog(ShadowrunCommand);
66
+ const backlogCmd = registerBacklog(ShadowrunCommand);
66
67
  registerBacklog(ShadowrunCompat);
68
+ // mcp: only under the live `play:` spelling -- a brand-new command has
69
+ // no compat surface to preserve under `play:shadowrun:backlog:mcp`.
70
+ registerBacklogMcp(backlogCmd);
67
71
  //# sourceMappingURL=sideQuest-backlog.sub.cmd.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maka/maka-cli",
3
- "version": "5.142.0",
3
+ "version": "5.143.1",
4
4
  "type": "module",
5
5
  "summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
6
6
  "description": "A command line tool for scaffolding Meteor 3.x applications using React.",
@@ -49,6 +49,7 @@
49
49
  "@inquirer/prompts": "^8.5.2",
50
50
  "@maka/meteor-sdk": "^0.2.13",
51
51
  "@maka/tar": "^6.2.2",
52
+ "@modelcontextprotocol/sdk": "^1.30.0",
52
53
  "blessed": "^0.1.81",
53
54
  "chalk": "^4.1.2",
54
55
  "change-case": "^5.4.4",
@@ -70,7 +71,8 @@
70
71
  "tar": "^7.4.3",
71
72
  "uuid": "^14.0.1",
72
73
  "ws": "^8.21.3",
73
- "xtend": "^4.0.2"
74
+ "xtend": "^4.0.2",
75
+ "zod": "^4.5.4"
74
76
  },
75
77
  "devDependencies": {
76
78
  "@aws-sdk/types": "^3.974.4",