@dereekb/dbx-cli 13.13.0 → 13.15.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.
@@ -162,6 +162,7 @@ import { join as join2 } from "node:path";
162
162
  // packages/dbx-cli/manifest-extract/src/lib/extract-crud.ts
163
163
  import { Node as Node2, Project as Project2 } from "ts-morph";
164
164
  var SUPPORTED_VERBS = /* @__PURE__ */ new Set(["create", "read", "update", "delete", "query", "invoke"]);
165
+ var DBX_MODEL_API_MCP_RESULT_MARKER = "dbxModelApiMcpResult";
165
166
  function extractCrudEntries(source) {
166
167
  const project = new Project2({ useInMemoryFileSystem: true, skipAddingFilesFromTsConfig: true });
167
168
  const sourceFile = project.createSourceFile(source.name, source.text, { overwrite: true });
@@ -215,7 +216,7 @@ function extractCrudEntries(source) {
215
216
  if (!verbValueNode) {
216
217
  continue;
217
218
  }
218
- collectVerbEntries({ modelName, verb, valueNode: verbValueNode, entries, fallbackDescription: readJsDocSummary(verbMember), resolveTypeDocs });
219
+ collectVerbEntries({ modelName, verb, valueNode: verbValueNode, entries, fallbackDescription: readJsDocSummary(verbMember), fallbackMcpResultTypeName: readJsDocTagValue(verbMember, DBX_MODEL_API_MCP_RESULT_MARKER), resolveTypeDocs });
219
220
  }
220
221
  }
221
222
  }
@@ -234,6 +235,8 @@ function extractCrudEntries(source) {
234
235
  const tuple = valueNode ? readTupleParamsResult(valueNode) : void 0;
235
236
  const paramsDocs = resolveTypeDocs(tuple?.params);
236
237
  const resultDocs = resolveTypeDocs(tuple?.result);
238
+ const mcpResultTypeName = readJsDocTagValue(member, DBX_MODEL_API_MCP_RESULT_MARKER);
239
+ const mcpResultDocs = resolveTypeDocs(mcpResultTypeName);
237
240
  entries.push({
238
241
  model: key,
239
242
  verb: "standalone",
@@ -246,7 +249,10 @@ function extractCrudEntries(source) {
246
249
  paramsFields: paramsDocs?.fields,
247
250
  paramsHasApiParamsTag: paramsDocs?.hasApiParamsTag,
248
251
  resultTypeDescription: resultDocs?.typeDescription,
249
- resultFields: resultDocs?.fields
252
+ resultFields: resultDocs?.fields,
253
+ mcpResultTypeName,
254
+ mcpResultTypeDescription: mcpResultDocs?.typeDescription,
255
+ mcpResultFields: mcpResultDocs?.fields
250
256
  });
251
257
  }
252
258
  }
@@ -314,7 +320,7 @@ function isNullLiteralType(node) {
314
320
  return result;
315
321
  }
316
322
  function collectVerbEntries(input) {
317
- const { modelName, verb, valueNode, entries, fallbackDescription, resolveTypeDocs } = input;
323
+ const { modelName, verb, valueNode, entries, fallbackDescription, fallbackMcpResultTypeName, resolveTypeDocs } = input;
318
324
  if (Node2.isTypeLiteral(valueNode)) {
319
325
  for (const specMember of valueNode.getMembers()) {
320
326
  if (!Node2.isPropertySignature(specMember)) {
@@ -325,6 +331,8 @@ function collectVerbEntries(input) {
325
331
  const leaf2 = leafNode ? readTupleParamsResult(leafNode) ?? readBareParams(leafNode) : void 0;
326
332
  const paramsDocs2 = resolveTypeDocs(leaf2?.params);
327
333
  const resultDocs2 = resolveTypeDocs(leaf2?.result);
334
+ const mcpResultTypeName = readJsDocTagValue(specMember, DBX_MODEL_API_MCP_RESULT_MARKER);
335
+ const mcpResultDocs2 = resolveTypeDocs(mcpResultTypeName);
328
336
  entries.push({
329
337
  model: modelName,
330
338
  verb,
@@ -337,7 +345,10 @@ function collectVerbEntries(input) {
337
345
  paramsFields: paramsDocs2?.fields,
338
346
  paramsHasApiParamsTag: paramsDocs2?.hasApiParamsTag,
339
347
  resultTypeDescription: resultDocs2?.typeDescription,
340
- resultFields: resultDocs2?.fields
348
+ resultFields: resultDocs2?.fields,
349
+ mcpResultTypeName,
350
+ mcpResultTypeDescription: mcpResultDocs2?.typeDescription,
351
+ mcpResultFields: mcpResultDocs2?.fields
341
352
  });
342
353
  }
343
354
  return;
@@ -345,6 +356,7 @@ function collectVerbEntries(input) {
345
356
  const leaf = readTupleParamsResult(valueNode) ?? readBareParams(valueNode);
346
357
  const paramsDocs = resolveTypeDocs(leaf?.params);
347
358
  const resultDocs = resolveTypeDocs(leaf?.result);
359
+ const mcpResultDocs = resolveTypeDocs(fallbackMcpResultTypeName);
348
360
  entries.push({
349
361
  model: modelName,
350
362
  verb,
@@ -357,7 +369,10 @@ function collectVerbEntries(input) {
357
369
  paramsFields: paramsDocs?.fields,
358
370
  paramsHasApiParamsTag: paramsDocs?.hasApiParamsTag,
359
371
  resultTypeDescription: resultDocs?.typeDescription,
360
- resultFields: resultDocs?.fields
372
+ resultFields: resultDocs?.fields,
373
+ mcpResultTypeName: fallbackMcpResultTypeName,
374
+ mcpResultTypeDescription: mcpResultDocs?.typeDescription,
375
+ mcpResultFields: mcpResultDocs?.fields
361
376
  });
362
377
  }
363
378
  function readTupleParamsResult(node) {
@@ -436,6 +451,20 @@ function hasJsDocFlag(node, tagName) {
436
451
  }
437
452
  return result;
438
453
  }
454
+ function readJsDocTagValue(node, tagName) {
455
+ let result;
456
+ for (const doc of node.getJsDocs()) {
457
+ for (const tag of doc.getTags()) {
458
+ if (tag.getTagName() === tagName) {
459
+ const token = tag.getCommentText()?.trim().split(/\s+/)[0];
460
+ if (token) {
461
+ result = token;
462
+ }
463
+ }
464
+ }
465
+ }
466
+ return result;
467
+ }
439
468
  function readJsDocSummary(node) {
440
469
  let result;
441
470
  const docs = node.getJsDocs();
@@ -453,7 +482,9 @@ function readJsDocSummary(node) {
453
482
  import { Node as Node3, Project as Project3 } from "ts-morph";
454
483
  var READ_LEVEL_VALUES = /* @__PURE__ */ new Set(["system", "owner", "admin-only", "permissions"]);
455
484
  var SERVICE_FACTORY_TAG = "dbxModelServiceFactory";
485
+ var MCP_TOOL_NAME_SEGMENT_TAG = "dbxModelMcpToolNameSegment";
456
486
  var MODEL_TYPE_VALUE_PATTERN = /^[a-z][A-Za-z0-9_$]*$/;
487
+ var TOOL_NAME_SEGMENT_PATTERN = /^[A-Za-z][A-Za-z0-9_$]*$/;
457
488
  var PASSTHROUGH_TYPE_WRAPPERS = /* @__PURE__ */ new Set(["Partial", "Required", "Readonly", "NonNullable", "MaybeMap", "Pick", "Omit"]);
458
489
  var IDENTITY_FN = "firestoreModelIdentity";
459
490
  var CONVERTER_FN_NAMES = ["snapshotConverterFunctions", "firestoreSubObject", "firestoreObjectArray"];
@@ -527,6 +558,7 @@ function buildInterface(decl) {
527
558
  const jsDocs = decl.getJsDocs();
528
559
  const hasDbxModelTag = jsDocsHaveTag(jsDocs, "dbxModel");
529
560
  const dbxModelRead = readDbxModelReadTag(jsDocs);
561
+ const mcpToolNameSegment = readMcpToolNameSegmentTag(jsDocs);
530
562
  const extendsNames = decl.getExtends().map(resolveExtendsName);
531
563
  const props = [];
532
564
  for (const prop of decl.getProperties()) {
@@ -550,9 +582,26 @@ function buildInterface(decl) {
550
582
  hasDbxModelTag,
551
583
  extendsNames,
552
584
  props,
553
- ...dbxModelRead === void 0 ? {} : { dbxModelRead }
585
+ ...dbxModelRead === void 0 ? {} : { dbxModelRead },
586
+ ...mcpToolNameSegment === void 0 ? {} : { mcpToolNameSegment }
554
587
  };
555
588
  }
589
+ function readMcpToolNameSegmentTag(jsDocs) {
590
+ let result;
591
+ for (const doc of jsDocs) {
592
+ for (const tag of doc.getTags()) {
593
+ if (tag.getTagName() !== MCP_TOOL_NAME_SEGMENT_TAG) continue;
594
+ if (result !== void 0) continue;
595
+ const raw = tag.getCommentText()?.trim();
596
+ if (raw === void 0 || raw.length === 0) continue;
597
+ const firstToken = raw.split(/\s+/)[0];
598
+ if (TOOL_NAME_SEGMENT_PATTERN.test(firstToken)) {
599
+ result = firstToken;
600
+ }
601
+ }
602
+ }
603
+ return result;
604
+ }
556
605
  function readDbxModelReadTag(jsDocs) {
557
606
  let result;
558
607
  for (const doc of jsDocs) {
@@ -1050,6 +1099,7 @@ function buildEntryForIdentity(input) {
1050
1099
  sourcePackage: source.sourcePackage,
1051
1100
  sourceFile: source.sourceFile,
1052
1101
  fields,
1102
+ ...iface.mcpToolNameSegment ? { mcpToolNameSegment: iface.mcpToolNameSegment } : {},
1053
1103
  ...iface.dbxModelRead ? { read: iface.dbxModelRead } : {},
1054
1104
  ...serviceFactory ? { serviceFactory } : {}
1055
1105
  };
@@ -1347,7 +1397,10 @@ function renderEntry({ entry, validatorName }) {
1347
1397
  entry.paramsTypeDescription ? `paramsTypeDescription: ${JSON.stringify(entry.paramsTypeDescription)}` : void 0,
1348
1398
  entry.paramsFields && entry.paramsFields.length > 0 ? `paramsFields: ${renderDocFields(entry.paramsFields)}` : void 0,
1349
1399
  entry.resultTypeDescription ? `resultTypeDescription: ${JSON.stringify(entry.resultTypeDescription)}` : void 0,
1350
- entry.resultFields && entry.resultFields.length > 0 ? `resultFields: ${renderDocFields(entry.resultFields)}` : void 0
1400
+ entry.resultFields && entry.resultFields.length > 0 ? `resultFields: ${renderDocFields(entry.resultFields)}` : void 0,
1401
+ entry.mcpResultTypeName ? `mcpResultTypeName: ${JSON.stringify(entry.mcpResultTypeName)}` : void 0,
1402
+ entry.mcpResultTypeDescription ? `mcpResultTypeDescription: ${JSON.stringify(entry.mcpResultTypeDescription)}` : void 0,
1403
+ entry.mcpResultFields && entry.mcpResultFields.length > 0 ? `mcpResultFields: ${renderDocFields(entry.mcpResultFields)}` : void 0
1351
1404
  ];
1352
1405
  return ` { ${fields.filter((v) => Boolean(v)).join(", ")} }`;
1353
1406
  }
@@ -1375,6 +1428,7 @@ function renderModelEntry(entry, emitConverters) {
1375
1428
  `sourcePackage: ${JSON.stringify(entry.sourcePackage)}`,
1376
1429
  `sourceFile: ${JSON.stringify(entry.sourceFile)}`,
1377
1430
  `fields: ${renderModelFields(entry.fields, emitConverters)}`,
1431
+ entry.mcpToolNameSegment ? `mcpToolNameSegment: ${JSON.stringify(entry.mcpToolNameSegment)}` : void 0,
1378
1432
  entry.read ? `read: ${JSON.stringify(entry.read)}` : void 0,
1379
1433
  entry.serviceFactory ? `serviceFactory: { exportName: ${JSON.stringify(entry.serviceFactory.exportName)}, sourceFile: ${JSON.stringify(entry.serviceFactory.sourceFile)} }` : void 0
1380
1434
  ];
@@ -1487,6 +1541,10 @@ async function main() {
1487
1541
  console.warn(`[no-api-params-tag] ${pkg.packageName} \xB7 ${entry.model}/${entry.verb}${specPart} \u2192 ${entry.paramsTypeName} missing @dbxModelApiParams`);
1488
1542
  }
1489
1543
  }
1544
+ if (entry.mcpResultTypeName && entry.mcpResultFields == null && entry.mcpResultTypeDescription == null) {
1545
+ const specPart = entry.specifier ? "/" + entry.specifier : "";
1546
+ console.warn(`[mcp-result-type-unresolved] ${pkg.packageName} \xB7 ${entry.model}/${entry.verb}${specPart} \u2192 @dbxModelApiMcpResult ${entry.mcpResultTypeName} not declared in the same .api.ts file`);
1547
+ }
1490
1548
  collected.push(enriched);
1491
1549
  }
1492
1550
  }
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-firebase-api-manifest",
3
- "version": "13.13.0",
3
+ "version": "13.15.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "devDependencies": {
7
7
  "ts-morph": "^21.0.0"
8
8
  },
9
9
  "peerDependencies": {
10
- "@dereekb/dbx-cli": "13.13.0",
11
- "@dereekb/util": "13.13.0",
10
+ "@dereekb/dbx-cli": "13.15.0",
11
+ "@dereekb/util": "13.15.0",
12
12
  "prettier": "3.8.3"
13
13
  }
14
14
  }
@@ -5,14 +5,14 @@ const require = __createRequire(import.meta.url);
5
5
  // packages/dbx-cli/generate-firestore-indexes/package.json
6
6
  var package_default = {
7
7
  name: "@dereekb/dbx-cli-generate-firestore-indexes",
8
- version: "13.13.0",
8
+ version: "13.15.0",
9
9
  private: true,
10
10
  type: "module",
11
11
  devDependencies: {
12
12
  eslint: "10.4.0"
13
13
  },
14
14
  peerDependencies: {
15
- "@dereekb/dbx-cli": "13.13.0"
15
+ "@dereekb/dbx-cli": "13.15.0"
16
16
  }
17
17
  };
18
18
 
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-generate-firestore-indexes",
3
- "version": "13.13.0",
3
+ "version": "13.15.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "devDependencies": {
7
7
  "eslint": "10.4.0"
8
8
  },
9
9
  "peerDependencies": {
10
- "@dereekb/dbx-cli": "13.13.0"
10
+ "@dereekb/dbx-cli": "13.15.0"
11
11
  }
12
12
  }
@@ -79,6 +79,38 @@ function mcpManifestKey(modelType, call, specifier) {
79
79
  const isDefault = specifier == null || specifier === "_";
80
80
  return isDefault ? `${modelType}.${call}._` : `${modelType}.${call}.${specifier}`;
81
81
  }
82
+ var DEFAULT_SPECIFIER_KEY = "_";
83
+ var MCP_TOOL_NAME_WARN_LENGTH = 55;
84
+ var MCP_TOOL_NAME_MAX_LENGTH = 64;
85
+ function buildMcpToolName(modelSegment, callType, specifier) {
86
+ const isDefault = specifier == null || specifier === DEFAULT_SPECIFIER_KEY;
87
+ return isDefault ? `${modelSegment}-${callType}` : `${modelSegment}-${specifier}`;
88
+ }
89
+ function validateMcpToolName(name) {
90
+ const length = name.length;
91
+ let level = "ok";
92
+ if (length > MCP_TOOL_NAME_MAX_LENGTH) {
93
+ level = "error";
94
+ } else if (length > MCP_TOOL_NAME_WARN_LENGTH) {
95
+ level = "warn";
96
+ }
97
+ return { name, length, level };
98
+ }
99
+ var MCP_CALL_TYPE_ABBREVIATIONS = {
100
+ create: "c",
101
+ read: "r",
102
+ update: "u",
103
+ delete: "d",
104
+ query: "q",
105
+ invoke: "i"
106
+ };
107
+ function abbreviateMcpCallType(callType) {
108
+ return MCP_CALL_TYPE_ABBREVIATIONS[callType] ?? callType;
109
+ }
110
+ function buildDisambiguatedMcpToolName(modelSegment, callType, specifier) {
111
+ const isDefault = specifier == null || specifier === DEFAULT_SPECIFIER_KEY;
112
+ return isDefault ? `${modelSegment}-${callType}` : `${modelSegment}-${abbreviateMcpCallType(callType)}-${specifier}`;
113
+ }
82
114
 
83
115
  // packages/dbx-cli/src/lib/mcp-scan/registry/auth-runtime.ts
84
116
  // @__NO_SIDE_EFFECTS__
@@ -980,22 +1012,56 @@ function toRelative(absolutePath, cwd) {
980
1012
  // packages/dbx-cli/generate-mcp-manifest/src/generate-mcp-manifest/render.ts
981
1013
  function renderMcpManifest(input, now = /* @__PURE__ */ new Date()) {
982
1014
  const tools = {};
983
- for (const entry of input.apiManifest) {
984
- if (entry.verb === "standalone") {
985
- continue;
1015
+ const warnings = [];
1016
+ const errors = [];
1017
+ const seenNames = /* @__PURE__ */ new Map();
1018
+ const segments = /* @__PURE__ */ new Map();
1019
+ if (input.modelManifest != null) {
1020
+ for (const model of input.modelManifest) {
1021
+ if (model.mcpToolNameSegment != null && model.mcpToolNameSegment.length > 0) {
1022
+ segments.set(model.modelType, model.mcpToolNameSegment);
1023
+ }
986
1024
  }
1025
+ }
1026
+ const toolEntries = input.apiManifest.filter((entry) => entry.verb !== "standalone");
1027
+ const nameCounts = /* @__PURE__ */ new Map();
1028
+ for (const entry of toolEntries) {
1029
+ const segment = segments.get(entry.model) ?? entry.model;
1030
+ const baseName = buildMcpToolName(segment, entry.verb, entry.specifier);
1031
+ nameCounts.set(baseName, (nameCounts.get(baseName) ?? 0) + 1);
1032
+ }
1033
+ for (const entry of toolEntries) {
987
1034
  const key = mcpManifestKey(entry.model, entry.verb, entry.specifier);
988
1035
  tools[key] = buildToolEntry(entry);
1036
+ const segment = segments.get(entry.model) ?? entry.model;
1037
+ const baseName = buildMcpToolName(segment, entry.verb, entry.specifier);
1038
+ const clashes = (nameCounts.get(baseName) ?? 0) > 1;
1039
+ const toolName = clashes ? buildDisambiguatedMcpToolName(segment, entry.verb, entry.specifier) : baseName;
1040
+ if (toolName !== baseName) {
1041
+ warnings.push(`Tool name "${baseName}" is produced by more than one entry; re-derived as "${toolName}" (${key}) with the abbreviated call type to disambiguate.`);
1042
+ }
1043
+ const validation = validateMcpToolName(toolName);
1044
+ if (validation.level === "error") {
1045
+ errors.push(`Tool name "${toolName}" is ${validation.length} chars, over the ${MCP_TOOL_NAME_MAX_LENGTH}-char MCP cap (${key}). Shorten the model/specifier, set a per-model mcpToolNameSegment, or hide the tool.`);
1046
+ } else if (validation.level === "warn") {
1047
+ warnings.push(`Tool name "${toolName}" is ${validation.length} chars, over the ${MCP_TOOL_NAME_WARN_LENGTH}-char soft limit (${key}).`);
1048
+ }
1049
+ const priorKey = seenNames.get(toolName);
1050
+ if (priorKey != null) {
1051
+ warnings.push(`Tool name "${toolName}" is still produced by more than one entry (${priorKey} and ${key}) after disambiguation; one shadows the other unless hidden or renamed at runtime.`);
1052
+ } else {
1053
+ seenNames.set(toolName, key);
1054
+ }
989
1055
  }
990
1056
  const models = input.modelManifest != null && input.modelManifest.length > 0 ? input.modelManifest.map(projectModelEntry) : void 0;
991
1057
  const auth = input.auth == null ? void 0 : projectAuthSection(input.auth.registry, input.auth.app);
992
1058
  const base = { version: MCP_MANIFEST_VERSION, generatedAt: now.toISOString(), tools };
993
- const result = {
1059
+ const manifest = {
994
1060
  ...base,
995
1061
  ...models == null ? {} : { models },
996
1062
  ...auth == null ? {} : { auth }
997
1063
  };
998
- return result;
1064
+ return { manifest, warnings, errors };
999
1065
  }
1000
1066
  function projectAuthSection(registry, appSlug) {
1001
1067
  const primary = registry.findApp(appSlug);
@@ -1052,6 +1118,7 @@ function projectModelEntry(entry) {
1052
1118
  ...entry.modelGroup == null ? {} : { modelGroup: entry.modelGroup },
1053
1119
  ...entry.parentIdentityConst == null ? {} : { parentIdentityConst: entry.parentIdentityConst },
1054
1120
  ...entry.description == null ? {} : { description: entry.description },
1121
+ ...entry.mcpToolNameSegment == null ? {} : { mcpToolNameSegment: entry.mcpToolNameSegment },
1055
1122
  ...entry.read == null ? {} : { read: entry.read },
1056
1123
  ...entry.serviceFactory == null ? {} : { serviceFactory: entry.serviceFactory }
1057
1124
  };
@@ -1079,6 +1146,7 @@ function buildToolEntry(entry) {
1079
1146
  if (description != null) result.description = description;
1080
1147
  if (inputSchema != null) result.inputSchema = inputSchema;
1081
1148
  if (outputSchema != null) result.outputSchema = outputSchema;
1149
+ if (entry.mcpResultTypeName != null) result.mcpResultTypeName = entry.mcpResultTypeName;
1082
1150
  return result;
1083
1151
  }
1084
1152
  function buildDescription(entry) {
@@ -1102,18 +1170,20 @@ function buildInputSchema(entry) {
1102
1170
  return schema;
1103
1171
  }
1104
1172
  function buildOutputSchema(entry) {
1105
- const hasFields = entry.resultFields != null && entry.resultFields.length > 0;
1106
- const hasDescription = typeof entry.resultTypeDescription === "string" && entry.resultTypeDescription.length > 0;
1173
+ const fields = entry.mcpResultFields != null && entry.mcpResultFields.length > 0 ? entry.mcpResultFields : entry.resultFields;
1174
+ const description = typeof entry.mcpResultTypeDescription === "string" && entry.mcpResultTypeDescription.length > 0 ? entry.mcpResultTypeDescription : entry.resultTypeDescription;
1175
+ const hasFields = fields != null && fields.length > 0;
1176
+ const hasDescription = typeof description === "string" && description.length > 0;
1107
1177
  if (!hasFields && !hasDescription) {
1108
1178
  return void 0;
1109
1179
  }
1110
1180
  const schema = { type: "object" };
1111
1181
  if (hasDescription) {
1112
- schema["description"] = entry.resultTypeDescription;
1182
+ schema["description"] = description;
1113
1183
  }
1114
1184
  if (hasFields) {
1115
1185
  const properties = {};
1116
- for (const field of entry.resultFields) {
1186
+ for (const field of fields) {
1117
1187
  properties[field.name] = buildPropertyFromField(field);
1118
1188
  }
1119
1189
  schema["properties"] = properties;
@@ -1212,16 +1282,26 @@ Expected output of \`nx run <cli>:generate-api-manifest\`.`);
1212
1282
  }
1213
1283
  const loaded = await loadManifest(inputPath);
1214
1284
  const auth = await maybeLoadAuth(flags);
1215
- const rendered = renderMcpManifest({ ...loaded, ...auth == null ? {} : { auth } });
1216
- const serialized = `${JSON.stringify(rendered, null, 2)}
1285
+ const { manifest, warnings, errors } = renderMcpManifest({ ...loaded, ...auth == null ? {} : { auth } });
1286
+ for (const warning of warnings) {
1287
+ console.error(`[generate-mcp-manifest] tool name warning: ${warning}`);
1288
+ }
1289
+ for (const error of errors) {
1290
+ console.error(`[generate-mcp-manifest] tool name error: ${error}`);
1291
+ }
1292
+ if (errors.length > 0) {
1293
+ console.error(`generate-mcp-manifest: ${errors.length} tool name(s) exceed the ${MCP_TOOL_NAME_MAX_LENGTH}-char MCP limit; not writing ${relative2(WORKSPACE_ROOT, outputPath)}.`);
1294
+ process.exit(1);
1295
+ }
1296
+ const serialized = `${JSON.stringify(manifest, null, 2)}
1217
1297
  `;
1218
1298
  ensureOutputDir(dirname(outputPath));
1219
1299
  const tmpPath = `${outputPath}.tmp`;
1220
1300
  writeFileSync(tmpPath, serialized);
1221
1301
  renameSync(tmpPath, outputPath);
1222
- const modelCount = rendered.models?.length ?? 0;
1223
- const authCount = rendered.auth?.claims.length ?? 0;
1224
- console.log(`[wrote] ${relative2(WORKSPACE_ROOT, outputPath)} \u2014 ${Object.keys(rendered.tools).length} tools, ${modelCount} models, ${authCount} auth claims`);
1302
+ const modelCount = manifest.models?.length ?? 0;
1303
+ const authCount = manifest.auth?.claims.length ?? 0;
1304
+ console.log(`[wrote] ${relative2(WORKSPACE_ROOT, outputPath)} \u2014 ${Object.keys(manifest.tools).length} tools, ${modelCount} models, ${authCount} auth claims`);
1225
1305
  }
1226
1306
  async function maybeLoadAuth(flags) {
1227
1307
  if (flags.app == null || flags.claimsInputs.length === 0) {
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@dereekb/dbx-cli-generate-mcp-manifest",
3
- "version": "13.13.0",
3
+ "version": "13.15.0",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "peerDependencies": {
7
- "@dereekb/dbx-cli": "13.13.0",
8
- "@dereekb/model": "13.13.0",
7
+ "@dereekb/dbx-cli": "13.15.0",
8
+ "@dereekb/model": "13.15.0",
9
9
  "arktype": "^2.2.0",
10
10
  "jiti": "2.6.1"
11
11
  }