@happyvertical/smrt-core 0.44.1 → 0.45.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.
Files changed (83) hide show
  1. package/agents/change-feed.md +3 -2
  2. package/dist/browser.js +2 -1
  3. package/dist/change-feed.d.ts +60 -1
  4. package/dist/change-feed.d.ts.map +1 -1
  5. package/dist/change-feed.js +464 -27
  6. package/dist/change-feed.js.map +1 -1
  7. package/dist/change-signals.d.ts.map +1 -1
  8. package/dist/change-signals.js +13 -11
  9. package/dist/change-signals.js.map +1 -1
  10. package/dist/generators/cli.d.ts.map +1 -1
  11. package/dist/generators/cli.js +10 -18
  12. package/dist/generators/cli.js.map +1 -1
  13. package/dist/generators/custom-action.d.ts +216 -0
  14. package/dist/generators/custom-action.d.ts.map +1 -1
  15. package/dist/generators/custom-action.js +256 -1
  16. package/dist/generators/custom-action.js.map +1 -1
  17. package/dist/generators/index.d.ts +1 -1
  18. package/dist/generators/index.d.ts.map +1 -1
  19. package/dist/generators/index.js +2 -2
  20. package/dist/generators/mcp.d.ts.map +1 -1
  21. package/dist/generators/mcp.js +14 -39
  22. package/dist/generators/mcp.js.map +1 -1
  23. package/dist/generators/rest.d.ts.map +1 -1
  24. package/dist/generators/rest.js +2 -0
  25. package/dist/generators/rest.js.map +1 -1
  26. package/dist/generators/tool-schema.d.ts.map +1 -1
  27. package/dist/generators/tool-schema.js +2 -8
  28. package/dist/generators/tool-schema.js.map +1 -1
  29. package/dist/generators.js +2 -2
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +4 -3
  33. package/dist/knowledge.d.ts.map +1 -1
  34. package/dist/knowledge.js +283 -35
  35. package/dist/knowledge.js.map +1 -1
  36. package/dist/manifest/static-manifest.js +1 -1
  37. package/dist/manifest/static-manifest.js.map +1 -1
  38. package/dist/manifest/store.js +1 -1
  39. package/dist/manifest.json +1 -1
  40. package/dist/registry/framework-base-classes.d.ts +10 -0
  41. package/dist/registry/framework-base-classes.d.ts.map +1 -0
  42. package/dist/registry/framework-base-classes.js +92 -0
  43. package/dist/registry/framework-base-classes.js.map +1 -0
  44. package/dist/registry/schema-builder.d.ts.map +1 -1
  45. package/dist/registry/schema-builder.js +2 -0
  46. package/dist/registry/schema-builder.js.map +1 -1
  47. package/dist/registry/types.d.ts +27 -6
  48. package/dist/registry/types.d.ts.map +1 -1
  49. package/dist/registry.d.ts +1 -0
  50. package/dist/registry.d.ts.map +1 -1
  51. package/dist/registry.js +2 -1
  52. package/dist/registry.js.map +1 -1
  53. package/dist/scanner/manifest-generator.d.ts +46 -16
  54. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  55. package/dist/scanner/manifest-generator.js +198 -45
  56. package/dist/scanner/manifest-generator.js.map +1 -1
  57. package/dist/smrt-knowledge.json +5 -5
  58. package/dist/system/bootstrap.d.ts.map +1 -1
  59. package/dist/system/bootstrap.js +2 -1
  60. package/dist/system/bootstrap.js.map +1 -1
  61. package/dist/system/schema.d.ts +75 -2
  62. package/dist/system/schema.d.ts.map +1 -1
  63. package/dist/system/schema.js +326 -4
  64. package/dist/system/schema.js.map +1 -1
  65. package/dist/vite-plugin/index.d.ts +10 -0
  66. package/dist/vite-plugin/index.d.ts.map +1 -1
  67. package/dist/vite-plugin/index.js +52 -23
  68. package/dist/vite-plugin/index.js.map +1 -1
  69. package/dist/vite-plugin/resources-route.d.ts +18 -0
  70. package/dist/vite-plugin/resources-route.d.ts.map +1 -0
  71. package/dist/vite-plugin/resources-route.js +154 -0
  72. package/dist/vite-plugin/resources-route.js.map +1 -0
  73. package/dist/vite-plugin/sveltekit-generator.d.ts +43 -7
  74. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  75. package/dist/vite-plugin/sveltekit-generator.js +90 -17
  76. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  77. package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
  78. package/dist/vite-plugin/sync-apply-route.js +2 -0
  79. package/dist/vite-plugin/sync-apply-route.js.map +1 -1
  80. package/dist/vite-plugin/web-collections.d.ts.map +1 -1
  81. package/dist/vite-plugin/web-collections.js +28 -5
  82. package/dist/vite-plugin/web-collections.js.map +1 -1
  83. package/package.json +4 -4
@@ -131,22 +131,6 @@ export declare class ManifestGenerator {
131
131
  * Also checks external SMRT packages for parent class definitions.
132
132
  */
133
133
  private isSTIChildClass;
134
- /**
135
- * Check whether `obj` extends a framework abstract base class anywhere
136
- * in its chain.
137
- *
138
- * Framework abstract bases (`SmrtHierarchical`, `SmrtJunction`, …) have
139
- * no table of their own — fields they declare must be merged into every
140
- * subclass's manifest, even when the subclass uses CTI. Without this,
141
- * a class like `Account extends SmrtHierarchical` would silently lose
142
- * `parentId` from its `fields` map and downstream WHERE-clause
143
- * validation would reject queries on the inherited column.
144
- *
145
- * Identified by name against the same hardcoded set the scanner's
146
- * `FRAMEWORK_BASE_CLASSES` recognizes (`packages/scanner/src/
147
- * inheritance-resolver.ts`). Keep the two lists in sync.
148
- */
149
- private extendsFrameworkAbstractBase;
150
134
  /**
151
135
  * Resolve a class name (simple `Content` or qualified `@pkg:Content`) to
152
136
  * the key it is stored under in `manifest.objects`, or `undefined` when the
@@ -290,6 +274,52 @@ export declare class ManifestGenerator {
290
274
  * Generate simple MCP tool names for testing/documentation
291
275
  */
292
276
  generateMCPTools(manifest: SmartObjectManifest): string;
277
+ /**
278
+ * Choose exactly one canonical MCP owner per `collection`.
279
+ *
280
+ * MCP tool names are keyed on `collection`, but the framework's normal shape
281
+ * puts a model class and its `FooCollection` access class in the manifest
282
+ * under the SAME collection. Emitting both produces duplicate tool names with
283
+ * differing create/update schemas, and an MCP client that indexes tools by
284
+ * name then resolves an arbitrary one (#2631 review).
285
+ *
286
+ * Owners are ranked, in order:
287
+ *
288
+ * 1. The model class beats the collection-access class. `create`/`update`
289
+ * input schemas derive from `obj.fields`, and the row shape lives on the
290
+ * model, not on the access class.
291
+ * 2. The shallowest class in the collection's own inheritance tree wins. An
292
+ * STI family shares one `collection`, and the base defines the shared row
293
+ * contract; a leaf subclass would narrow the emitted schema to one variant
294
+ * of a table the tool addresses as a whole.
295
+ * 3. Remaining ties break lexically on the qualified name, so the emitted
296
+ * surface is deterministic across generation passes.
297
+ */
298
+ private selectMCPToolOwners;
299
+ /** Stable identity for an MCP owner candidate. */
300
+ private mcpOwnerKey;
301
+ /**
302
+ * Depth of each candidate inside the candidate set's OWN inheritance tree,
303
+ * keyed by {@link mcpOwnerKey}. A candidate with no candidate ancestor is a
304
+ * root at depth 0. Ancestors outside the set (framework bases, classes in
305
+ * other collections) do not add depth: only the shared-collection family
306
+ * decides which member is canonical.
307
+ *
308
+ * Parents resolve on `extendsQualified` first. Two packages may legally
309
+ * contribute same-simple-name classes to one collection, so a simple-name
310
+ * match is used only when it is unambiguous within the candidate set —
311
+ * otherwise the walk stops rather than linking unrelated classes.
312
+ */
313
+ private inheritanceDepthsWithin;
314
+ /** True when `candidate` is the better canonical MCP owner than `current`. */
315
+ private preferMCPToolOwner;
316
+ /**
317
+ * Collection-access class detection, matching the convention already used by
318
+ * `generateRestRoutes` (direct base / generic type argument) plus the
319
+ * `FooCollection` naming fallback this generator uses elsewhere, so a deeper
320
+ * subclass that carries no type argument of its own is still recognised.
321
+ */
322
+ private isCollectionAccessClass;
293
323
  /**
294
324
  * Generate MCP tool JSON definitions
295
325
  */
@@ -1 +1 @@
1
- {"version":3,"file":"manifest-generator.d.ts","sourceRoot":"","sources":["../../src/scanner/manifest-generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manifest-generator.d.ts","sourceRoot":"","sources":["../../src/scanner/manifest-generator.ts"],"names":[],"mappings":"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"}
@@ -47,6 +47,57 @@ var FRAMEWORK_ABSTRACT_BASE_NAMES = /* @__PURE__ */ new Set([
47
47
  "SmrtReportCollection"
48
48
  ]);
49
49
  /**
50
+ * Framework base classes to exclude from the ancestor **method** merge
51
+ * below (#2624). Unlike fields, methods are never columns, so they never
52
+ * needed the STI-vs-CTI field-merge rule above — they inherited it only by
53
+ * sharing the same loop.
54
+ *
55
+ * Deliberately NARROWER than `FRAMEWORK_ABSTRACT_BASE_NAMES`: only the
56
+ * three universal object/collection primitives (`SmrtObject`, `SmrtClass`,
57
+ * `SmrtCollection`) are internal-only. These are the same three the
58
+ * runtime resolver's `getAllMethods()`
59
+ * (`packages/core/src/registry/inheritance-resolver.ts`) explicitly skips
60
+ * by name, and the same three the field-merge doc comment above excludes
61
+ * from `FRAMEWORK_ABSTRACT_BASE_NAMES` for the identical reason: their
62
+ * surface (`save`, `destroy`, `toJSON`, `withTransaction`, ...) is generic
63
+ * object-lifecycle plumbing, never a subclass-specific action.
64
+ *
65
+ * `FRAMEWORK_ABSTRACT_BASE_NAMES` (`SmrtJunction`, `SmrtHierarchical`,
66
+ * `SmrtPolymorphicAssociation`, `SmrtReport`, `SmrtReportCollection`) is
67
+ * intentionally NOT folded in here, unlike an earlier version of this fix.
68
+ * Those classes are mixin-style bases whose declared methods ARE the
69
+ * subclass's real, intended public API — the same way `SmrtHierarchical.
70
+ * parentId` is a real, intended field, which is exactly why
71
+ * `FRAMEWORK_ABSTRACT_BASE_NAMES` fields already merge into every subclass
72
+ * regardless of STI/CTI (see above). Directly confirmed for two of the
73
+ * five: `SmrtJunction.attach`/`detach`/`byLeft`/`byRight`/`setLinks`
74
+ * (`packages/core/src/junction.ts`) — a `packages/template-sveltekit/
75
+ * __tests__/runtimeProfileParity.test.ts` snapshot broke when an earlier
76
+ * revision of this fix folded all 8 `FRAMEWORK_BASE_CLASSES` in here,
77
+ * which is how this got caught — and `SmrtHierarchical.getParent`/
78
+ * `getChildren`/`getAncestors`/`getDescendants`/`getHierarchy`/`moveTo`
79
+ * (`packages/core/src/hierarchical.ts`). `SmrtPolymorphicAssociation`
80
+ * follows the identical pattern by inspection but has no dedicated
81
+ * regression test yet. `SmrtReport`/`SmrtReportCollection` are the
82
+ * least-audited of the five: `SmrtReportCollection` overrides `list`/`get`
83
+ * (`packages/reports/src/report.ts`), which are also the universal CRUD
84
+ * operation names — a downstream `class X extends SmrtReportCollection`
85
+ * with default (non-strict) MCP config would have seen a generated
86
+ * custom-action tool collide with the standard `list`/`get` tool name.
87
+ * #2646 closed that separately and independently of this exclusion set: the
88
+ * MCP generator now refuses to expose a CRUD-named method as a custom action,
89
+ * matching case-folded because its tool ids are lowercased whole. Each emitter
90
+ * reserves on its own terms, and not all of them read the shared
91
+ * `CRUD_OPERATIONS` list yet — see its docblock in
92
+ * `generators/custom-action.ts` for the per-emitter rules. Keep in sync with the field-merge set's own doc comment; this one
93
+ * governs method merging only.
94
+ */
95
+ var FRAMEWORK_METHOD_BASE_NAMES = /* @__PURE__ */ new Set([
96
+ "SmrtObject",
97
+ "SmrtClass",
98
+ "SmrtCollection"
99
+ ]);
100
+ /**
50
101
  * Infer visibility from file path and explicit config
51
102
  *
52
103
  * Priority:
@@ -605,36 +656,6 @@ var ManifestGenerator = class {
605
656
  return false;
606
657
  }
607
658
  /**
608
- * Check whether `obj` extends a framework abstract base class anywhere
609
- * in its chain.
610
- *
611
- * Framework abstract bases (`SmrtHierarchical`, `SmrtJunction`, …) have
612
- * no table of their own — fields they declare must be merged into every
613
- * subclass's manifest, even when the subclass uses CTI. Without this,
614
- * a class like `Account extends SmrtHierarchical` would silently lose
615
- * `parentId` from its `fields` map and downstream WHERE-clause
616
- * validation would reject queries on the inherited column.
617
- *
618
- * Identified by name against the same hardcoded set the scanner's
619
- * `FRAMEWORK_BASE_CLASSES` recognizes (`packages/scanner/src/
620
- * inheritance-resolver.ts`). Keep the two lists in sync.
621
- */
622
- extendsFrameworkAbstractBase(obj, objectsByName, manifest) {
623
- if (!obj.extends) return false;
624
- let currentClass = obj.extends;
625
- const visited = /* @__PURE__ */ new Set();
626
- while (currentClass) {
627
- if (visited.has(currentClass)) break;
628
- visited.add(currentClass);
629
- if (FRAMEWORK_ABSTRACT_BASE_NAMES.has(currentClass)) return true;
630
- let parentObj = objectsByName.get(currentClass);
631
- if (!parentObj && manifest.smrtDependencies && manifest.smrtDependencies.length > 0) parentObj = this.loadParentFromExternalPackage(currentClass, manifest.smrtDependencies, objectsByName);
632
- if (!parentObj) break;
633
- currentClass = parentObj.extends;
634
- }
635
- return false;
636
- }
637
- /**
638
659
  * Resolve a class name (simple `Content` or qualified `@pkg:Content`) to
639
660
  * the key it is stored under in `manifest.objects`, or `undefined` when the
640
661
  * manifest does not carry it. Manifest keys are qualified names; `extends`
@@ -728,8 +749,6 @@ var ManifestGenerator = class {
728
749
  for (const obj of Object.values(manifest.objects)) {
729
750
  if (!obj.extends) continue;
730
751
  const usesSTI = this.isSTIClass(obj, objectsByName, manifest);
731
- const extendsFrameworkBase = this.extendsFrameworkAbstractBase(obj, objectsByName, manifest);
732
- if (!usesSTI && !extendsFrameworkBase) continue;
733
752
  logger.info(`[manifest-generator] Merging inherited fields for ${obj.className} from ${obj.extends}`);
734
753
  const inheritanceChain = [];
735
754
  let currentClass = obj.extends;
@@ -755,8 +774,7 @@ var ManifestGenerator = class {
755
774
  const ancestor = objectsByName.get(ancestorName);
756
775
  if (!ancestor) continue;
757
776
  const ancestorIsFrameworkBase = FRAMEWORK_ABSTRACT_BASE_NAMES.has(ancestorName);
758
- if (!usesSTI && !ancestorIsFrameworkBase) continue;
759
- for (const [fieldName, fieldDef] of Object.entries(ancestor.fields)) {
777
+ if (usesSTI || ancestorIsFrameworkBase) for (const [fieldName, fieldDef] of Object.entries(ancestor.fields)) {
760
778
  const existingOwner = mergedFieldOwners.get(fieldName);
761
779
  const replacesFrameworkDefault = existingOwner !== void 0 && FRAMEWORK_ABSTRACT_BASE_NAMES.has(this.simpleClassName(existingOwner)) && !FRAMEWORK_ABSTRACT_BASE_NAMES.has(this.simpleClassName(ancestorName));
762
780
  if (!existingOwner || replacesFrameworkDefault) {
@@ -764,7 +782,7 @@ var ManifestGenerator = class {
764
782
  mergedFieldOwners.set(fieldName, ancestorName);
765
783
  }
766
784
  }
767
- for (const [methodName, methodDef] of Object.entries(ancestor.methods || {})) if (!mergedMethods[methodName]) mergedMethods[methodName] = methodDef;
785
+ if (!FRAMEWORK_METHOD_BASE_NAMES.has(ancestorName)) for (const [methodName, methodDef] of Object.entries(ancestor.methods || {})) mergedMethods[methodName] = methodDef;
768
786
  }
769
787
  for (const [fieldName, fieldDef] of Object.entries(obj.fields)) mergedFields[fieldName] = fieldDef;
770
788
  for (const [methodName, methodDef] of Object.entries(obj.methods || {})) mergedMethods[methodName] = methodDef;
@@ -1159,16 +1177,123 @@ ${fields}
1159
1177
  */
1160
1178
  generateMCPTools(manifest) {
1161
1179
  const tools = [];
1162
- for (const [_name, obj] of Object.entries(manifest.objects)) if (obj.decoratorConfig.mcp !== false) tools.push(...this.getSimpleMCPToolNames(obj));
1180
+ for (const obj of this.selectMCPToolOwners(manifest)) tools.push(...this.getSimpleMCPToolNames(obj));
1163
1181
  return tools.join("\n");
1164
1182
  }
1165
1183
  /**
1184
+ * Choose exactly one canonical MCP owner per `collection`.
1185
+ *
1186
+ * MCP tool names are keyed on `collection`, but the framework's normal shape
1187
+ * puts a model class and its `FooCollection` access class in the manifest
1188
+ * under the SAME collection. Emitting both produces duplicate tool names with
1189
+ * differing create/update schemas, and an MCP client that indexes tools by
1190
+ * name then resolves an arbitrary one (#2631 review).
1191
+ *
1192
+ * Owners are ranked, in order:
1193
+ *
1194
+ * 1. The model class beats the collection-access class. `create`/`update`
1195
+ * input schemas derive from `obj.fields`, and the row shape lives on the
1196
+ * model, not on the access class.
1197
+ * 2. The shallowest class in the collection's own inheritance tree wins. An
1198
+ * STI family shares one `collection`, and the base defines the shared row
1199
+ * contract; a leaf subclass would narrow the emitted schema to one variant
1200
+ * of a table the tool addresses as a whole.
1201
+ * 3. Remaining ties break lexically on the qualified name, so the emitted
1202
+ * surface is deterministic across generation passes.
1203
+ */
1204
+ selectMCPToolOwners(manifest) {
1205
+ const candidatesByCollection = /* @__PURE__ */ new Map();
1206
+ for (const obj of Object.values(manifest.objects)) {
1207
+ if (obj.decoratorConfig.mcp === false) continue;
1208
+ const bucket = candidatesByCollection.get(obj.collection);
1209
+ if (bucket) bucket.push(obj);
1210
+ else candidatesByCollection.set(obj.collection, [obj]);
1211
+ }
1212
+ return [...candidatesByCollection.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([, candidates]) => {
1213
+ const depths = this.inheritanceDepthsWithin(candidates);
1214
+ let owner = candidates[0];
1215
+ for (const candidate of candidates.slice(1)) if (this.preferMCPToolOwner(candidate, owner, depths)) owner = candidate;
1216
+ return owner;
1217
+ });
1218
+ }
1219
+ /** Stable identity for an MCP owner candidate. */
1220
+ mcpOwnerKey(obj) {
1221
+ return obj.qualifiedName || obj.className;
1222
+ }
1223
+ /**
1224
+ * Depth of each candidate inside the candidate set's OWN inheritance tree,
1225
+ * keyed by {@link mcpOwnerKey}. A candidate with no candidate ancestor is a
1226
+ * root at depth 0. Ancestors outside the set (framework bases, classes in
1227
+ * other collections) do not add depth: only the shared-collection family
1228
+ * decides which member is canonical.
1229
+ *
1230
+ * Parents resolve on `extendsQualified` first. Two packages may legally
1231
+ * contribute same-simple-name classes to one collection, so a simple-name
1232
+ * match is used only when it is unambiguous within the candidate set —
1233
+ * otherwise the walk stops rather than linking unrelated classes.
1234
+ */
1235
+ inheritanceDepthsWithin(candidates) {
1236
+ const byQualified = /* @__PURE__ */ new Map();
1237
+ const bySimpleName = /* @__PURE__ */ new Map();
1238
+ for (const candidate of candidates) {
1239
+ byQualified.set(this.mcpOwnerKey(candidate), candidate);
1240
+ const simple = this.simpleClassName(candidate.className);
1241
+ bySimpleName.set(simple, bySimpleName.has(simple) ? null : candidate);
1242
+ }
1243
+ const resolveParent = (obj) => {
1244
+ if (obj.extendsQualified) return byQualified.get(obj.extendsQualified);
1245
+ if (!obj.extends) return void 0;
1246
+ return bySimpleName.get(this.simpleClassName(obj.extends)) ?? void 0;
1247
+ };
1248
+ const depths = /* @__PURE__ */ new Map();
1249
+ for (const candidate of candidates) {
1250
+ const key = this.mcpOwnerKey(candidate);
1251
+ let depth = 0;
1252
+ let current = candidate;
1253
+ const visited = /* @__PURE__ */ new Set([key]);
1254
+ while (current) {
1255
+ const parent = resolveParent(current);
1256
+ if (!parent) break;
1257
+ const parentKey = this.mcpOwnerKey(parent);
1258
+ if (visited.has(parentKey)) break;
1259
+ visited.add(parentKey);
1260
+ depth += 1;
1261
+ current = parent;
1262
+ }
1263
+ depths.set(key, depth);
1264
+ }
1265
+ return depths;
1266
+ }
1267
+ /** True when `candidate` is the better canonical MCP owner than `current`. */
1268
+ preferMCPToolOwner(candidate, current, depths) {
1269
+ const candidateIsCollection = this.isCollectionAccessClass(candidate);
1270
+ if (candidateIsCollection !== this.isCollectionAccessClass(current)) return !candidateIsCollection;
1271
+ const candidateKey = this.mcpOwnerKey(candidate);
1272
+ const currentKey = this.mcpOwnerKey(current);
1273
+ const candidateDepth = depths.get(candidateKey) ?? 0;
1274
+ const currentDepth = depths.get(currentKey) ?? 0;
1275
+ if (candidateDepth !== currentDepth) return candidateDepth < currentDepth;
1276
+ return candidateKey < currentKey;
1277
+ }
1278
+ /**
1279
+ * Collection-access class detection, matching the convention already used by
1280
+ * `generateRestRoutes` (direct base / generic type argument) plus the
1281
+ * `FooCollection` naming fallback this generator uses elsewhere, so a deeper
1282
+ * subclass that carries no type argument of its own is still recognised.
1283
+ */
1284
+ isCollectionAccessClass(obj) {
1285
+ return obj.extends === "SmrtCollection" || !!obj.extendsTypeArg || obj.className.endsWith("Collection");
1286
+ }
1287
+ /**
1166
1288
  * Generate MCP tool JSON definitions
1167
1289
  */
1168
1290
  generateMCPToolsCode(manifest) {
1169
1291
  const tools = [];
1170
- for (const [_name, obj] of Object.entries(manifest.objects)) if (obj.decoratorConfig.mcp !== false) tools.push(this.generateMCPTool(obj));
1171
- return `[\n${tools.join(",\n")}\n]`;
1292
+ for (const obj of this.selectMCPToolOwners(manifest)) {
1293
+ const code = this.generateMCPTool(obj);
1294
+ if (code) tools.push(code);
1295
+ }
1296
+ return tools.length > 0 ? `[\n${tools.join(",\n")}\n]` : "[]";
1172
1297
  }
1173
1298
  /**
1174
1299
  * Get simple MCP tool names for an object
@@ -1195,7 +1320,7 @@ ${fields}
1195
1320
  * Generate a single MCP tool
1196
1321
  */
1197
1322
  generateMCPTool(obj) {
1198
- const { collection, className, name } = obj;
1323
+ const { collection } = obj;
1199
1324
  const config = obj.decoratorConfig.mcp;
1200
1325
  const exclude = typeof config === "object" && config?.exclude || [];
1201
1326
  const include = typeof config === "object" && config?.include || void 0;
@@ -1218,28 +1343,56 @@ ${fields}
1218
1343
  }
1219
1344
  }`);
1220
1345
  if (shouldInclude("get")) tools.push(` {
1221
- name: "get_${name}",
1222
- description: "Get a ${name} by ID",
1346
+ name: "get_${collection}",
1347
+ description: "Get a ${collection} entry by ID",
1223
1348
  inputSchema: {
1224
1349
  type: "object",
1225
1350
  properties: {
1226
- id: { type: "string", description: "The ${name} ID" }
1351
+ id: { type: "string", description: "The ${collection} entry ID" }
1227
1352
  },
1228
1353
  required: ["id"]
1229
1354
  }
1230
1355
  }`);
1356
+ const schemaProperties = JSON.stringify(this.generateSchemaProperties(obj.fields), null, 6);
1231
1357
  if (shouldInclude("create")) {
1232
1358
  const requiredFields = Object.entries(obj.fields).filter(([_, field]) => field.required).map(([fieldName]) => fieldName);
1233
1359
  tools.push(` {
1234
- name: "create_${name}",
1235
- description: "Create a new ${name}",
1360
+ name: "create_${collection}",
1361
+ description: "Create a new ${collection} entry",
1236
1362
  inputSchema: {
1237
1363
  type: "object",
1238
- properties: ${JSON.stringify(this.generateSchemaProperties(obj.fields), null, 6)},
1364
+ properties: ${schemaProperties},
1239
1365
  required: ${JSON.stringify(requiredFields)}
1240
1366
  }
1241
1367
  }`);
1242
1368
  }
1369
+ if (shouldInclude("update")) tools.push(` {
1370
+ name: "update_${collection}",
1371
+ description: "Update an existing ${collection} entry",
1372
+ inputSchema: {
1373
+ type: "object",
1374
+ properties: {
1375
+ id: { type: "string", description: "The ${collection} entry ID" },
1376
+ data: {
1377
+ type: "object",
1378
+ description: "Fields to update on the ${collection} entry",
1379
+ properties: ${schemaProperties}
1380
+ }
1381
+ },
1382
+ required: ["id", "data"]
1383
+ }
1384
+ }`);
1385
+ if (shouldInclude("delete")) tools.push(` {
1386
+ name: "delete_${collection}",
1387
+ description: "Delete a ${collection} entry by ID",
1388
+ inputSchema: {
1389
+ type: "object",
1390
+ properties: {
1391
+ id: { type: "string", description: "The ${collection} entry ID" }
1392
+ },
1393
+ required: ["id"]
1394
+ }
1395
+ }`);
1243
1396
  return tools.join(",\n");
1244
1397
  }
1245
1398
  /**