@constructive-io/graphql-codegen 4.24.4 → 4.25.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.
Files changed (36) hide show
  1. package/core/codegen/cli/docs-generator.d.ts +2 -4
  2. package/core/codegen/cli/docs-generator.js +74 -472
  3. package/core/codegen/cli/index.d.ts +2 -2
  4. package/core/codegen/cli/index.js +1 -3
  5. package/core/codegen/cli/table-command-generator.js +165 -16
  6. package/core/codegen/docs-utils.d.ts +0 -6
  7. package/core/codegen/docs-utils.js +3 -4
  8. package/core/codegen/hooks-docs-generator.d.ts +1 -2
  9. package/core/codegen/hooks-docs-generator.js +0 -113
  10. package/core/codegen/orm/docs-generator.d.ts +1 -2
  11. package/core/codegen/orm/docs-generator.js +0 -126
  12. package/core/codegen/target-docs-generator.d.ts +1 -2
  13. package/core/codegen/target-docs-generator.js +0 -13
  14. package/core/codegen/templates/cli-utils.ts +117 -0
  15. package/core/codegen/utils.d.ts +2 -2
  16. package/core/codegen/utils.js +2 -2
  17. package/core/generate.js +0 -26
  18. package/esm/core/codegen/cli/docs-generator.d.ts +2 -4
  19. package/esm/core/codegen/cli/docs-generator.js +75 -471
  20. package/esm/core/codegen/cli/index.d.ts +2 -2
  21. package/esm/core/codegen/cli/index.js +1 -1
  22. package/esm/core/codegen/cli/table-command-generator.js +166 -17
  23. package/esm/core/codegen/docs-utils.d.ts +0 -6
  24. package/esm/core/codegen/docs-utils.js +3 -4
  25. package/esm/core/codegen/hooks-docs-generator.d.ts +1 -2
  26. package/esm/core/codegen/hooks-docs-generator.js +2 -114
  27. package/esm/core/codegen/orm/docs-generator.d.ts +1 -2
  28. package/esm/core/codegen/orm/docs-generator.js +1 -126
  29. package/esm/core/codegen/target-docs-generator.d.ts +1 -2
  30. package/esm/core/codegen/target-docs-generator.js +0 -12
  31. package/esm/core/codegen/utils.d.ts +2 -2
  32. package/esm/core/codegen/utils.js +2 -2
  33. package/esm/core/generate.js +4 -30
  34. package/esm/types/config.d.ts +1 -8
  35. package/package.json +9 -9
  36. package/types/config.d.ts +1 -8
@@ -1,7 +1,8 @@
1
1
  import * as t from '@babel/types';
2
2
  import { toKebabCase } from 'komoji';
3
3
  import { generateCode } from '../babel-ast';
4
- import { getGeneratedFileHeader, getPrimaryKeyInfo, getScalarFields, getSelectableScalarFields, getTableNames, getWritableFieldNames, resolveInnerInputType, ucFirst, lcFirst, getCreateInputTypeName, getPatchTypeName, } from '../utils';
4
+ import { getGeneratedFileHeader, getPrimaryKeyInfo, getScalarFields, getSelectableScalarFields, getTableNames, getWritableFieldNames, resolveInnerInputType, ucFirst, lcFirst, toPascalCase, getCreateInputTypeName, getPatchTypeName, } from '../utils';
5
+ import { categorizeSpecialFields } from '../docs-utils';
5
6
  function createImportDeclaration(moduleSpecifier, namedImports, typeOnly = false) {
6
7
  const specifiers = namedImports.map((name) => t.importSpecifier(t.identifier(name), t.identifier(name)));
7
8
  const decl = t.importDeclaration(specifiers, t.stringLiteral(moduleSpecifier));
@@ -153,7 +154,7 @@ function buildArgvType() {
153
154
  }
154
155
  function buildSubcommandSwitch(subcommands, handlerPrefix, usageVarName) {
155
156
  const cases = subcommands.map((sub) => t.switchCase(t.stringLiteral(sub), [
156
- t.returnStatement(t.callExpression(t.identifier(`${handlerPrefix}${ucFirst(sub)}`), [
157
+ t.returnStatement(t.callExpression(t.identifier(`${handlerPrefix}${toPascalCase(sub)}`), [
157
158
  t.identifier('argv'),
158
159
  t.identifier('prompter'),
159
160
  ])),
@@ -166,17 +167,27 @@ function buildSubcommandSwitch(subcommands, handlerPrefix, usageVarName) {
166
167
  }
167
168
  function buildListHandler(table, targetName, typeRegistry) {
168
169
  const { singularName } = getTableNames(table);
169
- const selectObj = buildSelectObject(table, typeRegistry);
170
- const tryBody = [
171
- buildGetClientStatement(targetName),
172
- t.variableDeclaration('const', [
173
- t.variableDeclarator(t.identifier('result'), t.awaitExpression(buildOrmCall(singularName, 'findMany', t.objectExpression([
174
- t.objectProperty(t.identifier('select'), selectObj),
175
- ])))),
176
- ]),
177
- buildJsonLog(t.identifier('result')),
178
- ];
179
- const argvParam = t.identifier('_argv');
170
+ const defaultSelectObj = buildSelectObject(table, typeRegistry);
171
+ // --- Build the try body ---
172
+ const tryBody = [];
173
+ // const defaultSelect = { id: true, name: true, ... };
174
+ tryBody.push(t.variableDeclaration('const', [
175
+ t.variableDeclarator(t.identifier('defaultSelect'), defaultSelectObj),
176
+ ]));
177
+ // const findManyArgs = parseFindManyArgs(argv, defaultSelect);
178
+ tryBody.push(t.variableDeclaration('const', [
179
+ t.variableDeclarator(t.identifier('findManyArgs'), t.callExpression(t.identifier('parseFindManyArgs'), [
180
+ t.identifier('argv'),
181
+ t.identifier('defaultSelect'),
182
+ ])),
183
+ ]));
184
+ tryBody.push(buildGetClientStatement(targetName));
185
+ // const result = await client.<singular>.findMany(findManyArgs).execute();
186
+ tryBody.push(t.variableDeclaration('const', [
187
+ t.variableDeclarator(t.identifier('result'), t.awaitExpression(t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.memberExpression(t.identifier('client'), t.identifier(singularName)), t.identifier('findMany')), [t.identifier('findManyArgs')]), t.identifier('execute')), []))),
188
+ ]));
189
+ tryBody.push(buildJsonLog(t.identifier('result')));
190
+ const argvParam = t.identifier('argv');
180
191
  argvParam.typeAnnotation = buildArgvType();
181
192
  const prompterParam = t.identifier('_prompter');
182
193
  prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
@@ -184,6 +195,129 @@ function buildListHandler(table, targetName, typeRegistry) {
184
195
  t.tryStatement(t.blockStatement(tryBody), buildErrorCatch('Failed to list records.')),
185
196
  ]), false, true);
186
197
  }
198
+ /**
199
+ * Build a `handleFindFirst` function — CLI equivalent of the TS SDK's findFirst().
200
+ * Accepts --fields, --where.<field>.<op>, --condition.<field>.<op> flags.
201
+ * Internally calls findMany with first:1 and returns a single record (or null).
202
+ */
203
+ function buildFindFirstHandler(table, targetName, typeRegistry) {
204
+ const { singularName } = getTableNames(table);
205
+ const defaultSelectObj = buildSelectObject(table, typeRegistry);
206
+ const tryBody = [];
207
+ // const defaultSelect = { ... };
208
+ tryBody.push(t.variableDeclaration('const', [
209
+ t.variableDeclarator(t.identifier('defaultSelect'), defaultSelectObj),
210
+ ]));
211
+ // const findFirstArgs = parseFindFirstArgs(argv, defaultSelect);
212
+ tryBody.push(t.variableDeclaration('const', [
213
+ t.variableDeclarator(t.identifier('findFirstArgs'), t.callExpression(t.identifier('parseFindFirstArgs'), [
214
+ t.identifier('argv'),
215
+ t.identifier('defaultSelect'),
216
+ ])),
217
+ ]));
218
+ tryBody.push(buildGetClientStatement(targetName));
219
+ // const result = await client.<singular>.findFirst(findFirstArgs).execute();
220
+ tryBody.push(t.variableDeclaration('const', [
221
+ t.variableDeclarator(t.identifier('result'), t.awaitExpression(t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.memberExpression(t.identifier('client'), t.identifier(singularName)), t.identifier('findFirst')), [t.identifier('findFirstArgs')]), t.identifier('execute')), []))),
222
+ ]));
223
+ tryBody.push(buildJsonLog(t.identifier('result')));
224
+ const argvParam = t.identifier('argv');
225
+ argvParam.typeAnnotation = buildArgvType();
226
+ const prompterParam = t.identifier('_prompter');
227
+ prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
228
+ return t.functionDeclaration(t.identifier('handleFindFirst'), [argvParam, prompterParam], t.blockStatement([
229
+ t.tryStatement(t.blockStatement(tryBody), buildErrorCatch('Failed to find record.')),
230
+ ]), false, true);
231
+ }
232
+ /**
233
+ * Build a `handleSearch` function for tables with search-capable fields.
234
+ * Extracts the first positional arg as the query string and auto-builds a
235
+ * `where` clause that targets all detected search fields (tsvector, BM25,
236
+ * trigram, vector embedding). Supports --limit, --offset, --fields, --orderBy.
237
+ */
238
+ function buildSearchHandler(table, specialGroups, targetName, typeRegistry) {
239
+ const { singularName } = getTableNames(table);
240
+ const defaultSelectObj = buildSelectObject(table, typeRegistry);
241
+ const tryBody = [];
242
+ // const query = Array.isArray(argv._) && argv._.length > 0 ? String(argv._[0]) : undefined;
243
+ tryBody.push(t.variableDeclaration('const', [
244
+ t.variableDeclarator(t.identifier('query'), t.conditionalExpression(t.logicalExpression('&&', t.callExpression(t.memberExpression(t.identifier('Array'), t.identifier('isArray')), [t.memberExpression(t.identifier('argv'), t.identifier('_'))]), t.binaryExpression('>', t.memberExpression(t.memberExpression(t.identifier('argv'), t.identifier('_')), t.identifier('length')), t.numericLiteral(0))), t.callExpression(t.identifier('String'), [
245
+ t.memberExpression(t.memberExpression(t.identifier('argv'), t.identifier('_')), t.numericLiteral(0), true),
246
+ ]), t.identifier('undefined'))),
247
+ ]));
248
+ // if (!query) { console.error('Usage: ... search <query>'); process.exit(1); }
249
+ tryBody.push(t.ifStatement(t.unaryExpression('!', t.identifier('query')), t.blockStatement([
250
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('console'), t.identifier('error')), [t.stringLiteral('Error: search requires a <query> argument')])),
251
+ t.expressionStatement(t.callExpression(t.memberExpression(t.identifier('process'), t.identifier('exit')), [t.numericLiteral(1)])),
252
+ ])));
253
+ // Build the where clause properties from detected search fields
254
+ // e.g. { tsvContent: { query }, bm25Body: { query }, trgmTitle: { value: query, threshold: 0.3 }, vectorEmbedding: { value: query } }
255
+ const whereProps = [];
256
+ for (const group of specialGroups) {
257
+ for (const field of group.fields) {
258
+ if (field.type.pgType?.toLowerCase() === 'tsvector') {
259
+ // tsvector field: { query }
260
+ whereProps.push(t.objectProperty(t.identifier(field.name), t.objectExpression([
261
+ t.objectProperty(t.identifier('query'), t.identifier('query'), false, true),
262
+ ])));
263
+ }
264
+ else if (/Bm25Score$/.test(field.name)) {
265
+ // BM25 computed score field — derive the input field name:
266
+ // bodyBm25Score -> bm25Body (strip trailing "Bm25Score", prefix with "bm25")
267
+ const baseName = field.name.replace(/Bm25Score$/, '');
268
+ const inputFieldName = `bm25${baseName.charAt(0).toUpperCase()}${baseName.slice(1)}`;
269
+ whereProps.push(t.objectProperty(t.identifier(inputFieldName), t.objectExpression([
270
+ t.objectProperty(t.identifier('query'), t.identifier('query'), false, true),
271
+ ])));
272
+ }
273
+ else if (/TrgmSimilarity$/.test(field.name)) {
274
+ // Trigram computed score field — derive the input field name:
275
+ // titleTrgmSimilarity -> trgmTitle
276
+ const baseName = field.name.replace(/TrgmSimilarity$/, '');
277
+ const inputFieldName = `trgm${baseName.charAt(0).toUpperCase()}${baseName.slice(1)}`;
278
+ whereProps.push(t.objectProperty(t.identifier(inputFieldName), t.objectExpression([
279
+ t.objectProperty(t.identifier('value'), t.identifier('query')),
280
+ t.objectProperty(t.identifier('threshold'), t.numericLiteral(0.3)),
281
+ ])));
282
+ }
283
+ else if (group.category === 'embedding') {
284
+ // Vector embedding field: { value: query }
285
+ whereProps.push(t.objectProperty(t.identifier(field.name), t.objectExpression([
286
+ t.objectProperty(t.identifier('value'), t.identifier('query')),
287
+ ])));
288
+ }
289
+ }
290
+ }
291
+ // const searchWhere = { tsvContent: { query }, ... };
292
+ tryBody.push(t.variableDeclaration('const', [
293
+ t.variableDeclarator(t.identifier('searchWhere'), t.objectExpression(whereProps)),
294
+ ]));
295
+ // const defaultSelect = { ... };
296
+ tryBody.push(t.variableDeclaration('const', [
297
+ t.variableDeclarator(t.identifier('defaultSelect'), defaultSelectObj),
298
+ ]));
299
+ // const findManyArgs = parseFindManyArgs(argv, defaultSelect, searchWhere);
300
+ tryBody.push(t.variableDeclaration('const', [
301
+ t.variableDeclarator(t.identifier('findManyArgs'), t.callExpression(t.identifier('parseFindManyArgs'), [
302
+ t.identifier('argv'),
303
+ t.identifier('defaultSelect'),
304
+ t.identifier('searchWhere'),
305
+ ])),
306
+ ]));
307
+ tryBody.push(buildGetClientStatement(targetName));
308
+ // const result = await client.<singular>.findMany(findManyArgs).execute();
309
+ tryBody.push(t.variableDeclaration('const', [
310
+ t.variableDeclarator(t.identifier('result'), t.awaitExpression(t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.memberExpression(t.identifier('client'), t.identifier(singularName)), t.identifier('findMany')), [t.identifier('findManyArgs')]), t.identifier('execute')), []))),
311
+ ]));
312
+ tryBody.push(buildJsonLog(t.identifier('result')));
313
+ const argvParam = t.identifier('argv');
314
+ argvParam.typeAnnotation = buildArgvType();
315
+ const prompterParam = t.identifier('_prompter');
316
+ prompterParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('Inquirerer')));
317
+ return t.functionDeclaration(t.identifier('handleSearch'), [argvParam, prompterParam], t.blockStatement([
318
+ t.tryStatement(t.blockStatement(tryBody), buildErrorCatch('Failed to search records.')),
319
+ ]), false, true);
320
+ }
187
321
  function buildGetHandler(table, targetName, typeRegistry) {
188
322
  const { singularName } = getTableNames(table);
189
323
  const pkFields = getPrimaryKeyInfo(table);
@@ -394,7 +528,7 @@ export function generateTableCommand(table, options) {
394
528
  ]));
395
529
  statements.push(createImportDeclaration(executorPath, ['getClient']));
396
530
  const utilsPath = options?.targetName ? '../../utils' : '../utils';
397
- statements.push(createImportDeclaration(utilsPath, ['coerceAnswers', 'stripUndefined']));
531
+ statements.push(createImportDeclaration(utilsPath, ['coerceAnswers', 'parseFindFirstArgs', 'parseFindManyArgs', 'stripUndefined']));
398
532
  statements.push(createImportDeclaration(utilsPath, ['FieldSchema'], true));
399
533
  // Import ORM input types for proper type assertions in mutation handlers.
400
534
  // These types ensure that cleanedData is cast to the correct ORM input type
@@ -426,7 +560,12 @@ export function generateTableCommand(table, options) {
426
560
  const hasUpdate = table.query?.update !== undefined && table.query?.update !== null;
427
561
  const hasDelete = table.query?.delete !== undefined && table.query?.delete !== null;
428
562
  const hasGet = table.query?.one !== null || hasUpdate || hasDelete;
429
- const subcommands = ['list'];
563
+ // Detect whether this table has search-capable fields (tsvector, BM25, trgm, vector embedding)
564
+ const specialGroups = categorizeSpecialFields(table, options?.typeRegistry);
565
+ const hasSearchFields = specialGroups.some((g) => g.category === 'search' || g.category === 'embedding');
566
+ const subcommands = ['list', 'find-first'];
567
+ if (hasSearchFields)
568
+ subcommands.push('search');
430
569
  if (hasGet)
431
570
  subcommands.push('get');
432
571
  subcommands.push('create');
@@ -439,8 +578,11 @@ export function generateTableCommand(table, options) {
439
578
  `${commandName} <command>`,
440
579
  '',
441
580
  'Commands:',
442
- ` list List all ${singularName} records`,
581
+ ` list List ${singularName} records`,
582
+ ` find-first Find first matching ${singularName} record`,
443
583
  ];
584
+ if (hasSearchFields)
585
+ usageLines.push(` search <query> Search ${singularName} records`);
444
586
  if (hasGet)
445
587
  usageLines.push(` get Get a ${singularName} by ID`);
446
588
  usageLines.push(` create Create a new ${singularName}`);
@@ -448,7 +590,11 @@ export function generateTableCommand(table, options) {
448
590
  usageLines.push(` update Update an existing ${singularName}`);
449
591
  if (hasDelete)
450
592
  usageLines.push(` delete Delete a ${singularName}`);
451
- usageLines.push('', ' --help, -h Show this help message', '');
593
+ usageLines.push('', 'List Options:', ' --limit <n> Max number of records to return (forward pagination)', ' --last <n> Number of records from the end (backward pagination)', ' --after <cursor> Cursor for forward pagination', ' --before <cursor> Cursor for backward pagination', ' --offset <n> Number of records to skip', ' --fields <fields> Comma-separated list of fields to return', ' --where.<field>.<op> Filter (dot-notation, e.g. --where.name.equalTo foo)', ' --condition.<f>.<op> Condition filter (dot-notation)', ' --orderBy <values> Comma-separated ordering values (e.g. NAME_ASC,CREATED_AT_DESC)', '', 'Find-First Options:', ' --fields <fields> Comma-separated list of fields to return', ' --where.<field>.<op> Filter (dot-notation, e.g. --where.status.equalTo active)', ' --condition.<f>.<op> Condition filter (dot-notation)', '');
594
+ if (hasSearchFields) {
595
+ usageLines.push('Search Options:', ' <query> Search query string (required)', ' --limit <n> Max number of records to return', ' --offset <n> Number of records to skip', ' --fields <fields> Comma-separated list of fields to return', ' --orderBy <values> Comma-separated list of ordering values', '');
596
+ }
597
+ usageLines.push(' --help, -h Show this help message', '');
452
598
  statements.push(t.variableDeclaration('const', [
453
599
  t.variableDeclarator(t.identifier('usage'), t.stringLiteral(usageLines.join('\n'))),
454
600
  ]));
@@ -510,6 +656,9 @@ export function generateTableCommand(table, options) {
510
656
  const tn = options?.targetName;
511
657
  const ormTypes = { createInputTypeName, patchTypeName, innerFieldName };
512
658
  statements.push(buildListHandler(table, tn, options?.typeRegistry));
659
+ statements.push(buildFindFirstHandler(table, tn, options?.typeRegistry));
660
+ if (hasSearchFields)
661
+ statements.push(buildSearchHandler(table, specialGroups, tn, options?.typeRegistry));
513
662
  if (hasGet)
514
663
  statements.push(buildGetHandler(table, tn, options?.typeRegistry));
515
664
  statements.push(buildMutationHandler(table, 'create', tn, options?.typeRegistry, ormTypes));
@@ -4,12 +4,6 @@ export interface GeneratedDocFile {
4
4
  fileName: string;
5
5
  content: string;
6
6
  }
7
- export interface McpTool {
8
- name: string;
9
- description: string;
10
- inputSchema: Record<string, unknown>;
11
- _meta?: Record<string, unknown>;
12
- }
13
7
  export interface SkillDefinition {
14
8
  name: string;
15
9
  description: string;
@@ -29,18 +29,17 @@ export function getReadmeFooter() {
29
29
  }
30
30
  export function resolveDocsConfig(docs) {
31
31
  if (docs === true) {
32
- return { readme: true, agents: true, mcp: true, skills: true };
32
+ return { readme: true, agents: true, skills: true };
33
33
  }
34
34
  if (docs === false) {
35
- return { readme: false, agents: false, mcp: false, skills: false };
35
+ return { readme: false, agents: false, skills: false };
36
36
  }
37
37
  if (!docs) {
38
- return { readme: true, agents: true, mcp: false, skills: false };
38
+ return { readme: true, agents: true, skills: false };
39
39
  }
40
40
  return {
41
41
  readme: docs.readme ?? true,
42
42
  agents: docs.agents ?? true,
43
- mcp: docs.mcp ?? false,
44
43
  skills: docs.skills ?? false,
45
44
  };
46
45
  }
@@ -1,6 +1,5 @@
1
1
  import type { Operation, Table, TypeRegistry } from '../../types/schema';
2
- import type { GeneratedDocFile, McpTool } from './docs-utils';
2
+ import type { GeneratedDocFile } from './docs-utils';
3
3
  export declare function generateHooksReadme(tables: Table[], customOperations: Operation[], registry?: TypeRegistry): GeneratedDocFile;
4
4
  export declare function generateHooksAgentsDocs(tables: Table[], customOperations: Operation[]): GeneratedDocFile;
5
- export declare function getHooksMcpTools(tables: Table[], customOperations: Operation[]): McpTool[];
6
5
  export declare function generateHooksSkills(tables: Table[], customOperations: Operation[], targetName: string, registry?: TypeRegistry): GeneratedDocFile[];
@@ -1,6 +1,6 @@
1
1
  import { toKebabCase } from 'komoji';
2
- import { buildSkillFile, buildSkillReference, formatArgType, fieldPlaceholder, pkPlaceholder, argPlaceholder, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, } from './docs-utils';
3
- import { getTableNames, getScalarFields, getPrimaryKeyInfo, getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, hasValidPrimaryKey, ucFirst, lcFirst, } from './utils';
2
+ import { buildSkillFile, buildSkillReference, formatArgType, fieldPlaceholder, pkPlaceholder, argPlaceholder, getReadmeHeader, getReadmeFooter, } from './docs-utils';
3
+ import { getTableNames, getScalarFields, getPrimaryKeyInfo, getListQueryHookName, getSingleQueryHookName, getCreateMutationHookName, getUpdateMutationHookName, getDeleteMutationHookName, hasValidPrimaryKey, ucFirst, } from './utils';
4
4
  function getCustomHookName(op) {
5
5
  if (op.kind === 'query') {
6
6
  return `use${ucFirst(op.name)}Query`;
@@ -161,118 +161,6 @@ export function generateHooksAgentsDocs(tables, customOperations) {
161
161
  content: lines.join('\n'),
162
162
  };
163
163
  }
164
- export function getHooksMcpTools(tables, customOperations) {
165
- const tools = [];
166
- for (const table of tables) {
167
- const { singularName, pluralName } = getTableNames(table);
168
- const pk = getPrimaryKeyInfo(table)[0];
169
- const scalarFields = getScalarFields(table);
170
- tools.push({
171
- name: `hooks_${lcFirst(pluralName)}_query`,
172
- description: table.description || `React Query hook to list all ${pluralName}`,
173
- inputSchema: {
174
- type: 'object',
175
- properties: {
176
- fields: {
177
- type: 'object',
178
- description: `Fields to select: { ${scalarFields.map((f) => f.name).join(', ')} }`,
179
- },
180
- },
181
- },
182
- });
183
- if (hasValidPrimaryKey(table)) {
184
- tools.push({
185
- name: `hooks_${lcFirst(singularName)}_query`,
186
- description: table.description || `React Query hook to get a single ${singularName} by ${pk.name}`,
187
- inputSchema: {
188
- type: 'object',
189
- properties: {
190
- [pk.name]: {
191
- type: gqlTypeToJsonSchemaType(pk.gqlType),
192
- description: `${table.name} ${pk.name}`,
193
- },
194
- },
195
- required: [pk.name],
196
- },
197
- });
198
- }
199
- tools.push({
200
- name: `hooks_create_${lcFirst(singularName)}_mutation`,
201
- description: table.description || `React Query mutation hook to create a ${singularName}`,
202
- inputSchema: {
203
- type: 'object',
204
- properties: Object.fromEntries(scalarFields
205
- .filter((f) => f.name !== pk.name &&
206
- f.name !== 'nodeId' &&
207
- f.name !== 'createdAt' &&
208
- f.name !== 'updatedAt')
209
- .map((f) => [
210
- f.name,
211
- {
212
- type: gqlTypeToJsonSchemaType(f.type.gqlType),
213
- description: `${table.name} ${f.name}`,
214
- },
215
- ])),
216
- },
217
- });
218
- if (hasValidPrimaryKey(table)) {
219
- tools.push({
220
- name: `hooks_update_${lcFirst(singularName)}_mutation`,
221
- description: table.description || `React Query mutation hook to update a ${singularName}`,
222
- inputSchema: {
223
- type: 'object',
224
- properties: {
225
- [pk.name]: {
226
- type: gqlTypeToJsonSchemaType(pk.gqlType),
227
- description: `${table.name} ${pk.name}`,
228
- },
229
- },
230
- required: [pk.name],
231
- },
232
- });
233
- tools.push({
234
- name: `hooks_delete_${lcFirst(singularName)}_mutation`,
235
- description: table.description || `React Query mutation hook to delete a ${singularName}`,
236
- inputSchema: {
237
- type: 'object',
238
- properties: {
239
- [pk.name]: {
240
- type: gqlTypeToJsonSchemaType(pk.gqlType),
241
- description: `${table.name} ${pk.name}`,
242
- },
243
- },
244
- required: [pk.name],
245
- },
246
- });
247
- }
248
- }
249
- for (const op of customOperations) {
250
- const hookName = getCustomHookName(op);
251
- const props = {};
252
- const required = [];
253
- for (const arg of op.args) {
254
- const isRequired = arg.type.kind === 'NON_NULL';
255
- const baseType = isRequired && arg.type.ofType ? arg.type.ofType : arg.type;
256
- props[arg.name] = {
257
- type: gqlTypeToJsonSchemaType(baseType.name ?? 'String'),
258
- description: arg.description || arg.name,
259
- };
260
- if (isRequired) {
261
- required.push(arg.name);
262
- }
263
- }
264
- tools.push({
265
- name: `hooks_${hookName}`,
266
- description: op.description || `${ucFirst(op.kind)} hook for ${op.name}`,
267
- inputSchema: {
268
- type: 'object',
269
- properties: props,
270
- ...(required.length > 0 ? { required } : {}),
271
- },
272
- });
273
- }
274
- return tools;
275
- }
276
164
  export function generateHooksSkills(tables, customOperations, targetName, registry) {
277
165
  const files = [];
278
166
  const skillName = `hooks-${targetName}`;
@@ -1,6 +1,5 @@
1
1
  import type { Operation, Table, TypeRegistry } from '../../../types/schema';
2
- import type { GeneratedDocFile, McpTool } from '../docs-utils';
2
+ import type { GeneratedDocFile } from '../docs-utils';
3
3
  export declare function generateOrmReadme(tables: Table[], customOperations: Operation[], registry?: TypeRegistry): GeneratedDocFile;
4
4
  export declare function generateOrmAgentsDocs(tables: Table[], customOperations: Operation[]): GeneratedDocFile;
5
- export declare function getOrmMcpTools(tables: Table[], customOperations: Operation[]): McpTool[];
6
5
  export declare function generateOrmSkills(tables: Table[], customOperations: Operation[], targetName: string, registry?: TypeRegistry): GeneratedDocFile[];
@@ -1,5 +1,5 @@
1
1
  import { toKebabCase } from 'komoji';
2
- import { buildSkillFile, buildSkillReference, formatArgType, fieldPlaceholder, pkPlaceholder, argPlaceholder, getEditableFields, categorizeSpecialFields, buildSpecialFieldsMarkdown, getReadmeHeader, getReadmeFooter, gqlTypeToJsonSchemaType, } from '../docs-utils';
2
+ import { buildSkillFile, buildSkillReference, formatArgType, fieldPlaceholder, pkPlaceholder, argPlaceholder, getEditableFields, categorizeSpecialFields, buildSpecialFieldsMarkdown, getReadmeHeader, getReadmeFooter, } from '../docs-utils';
3
3
  import { getScalarFields, getTableNames, getPrimaryKeyInfo, lcFirst, } from '../utils';
4
4
  export function generateOrmReadme(tables, customOperations, registry) {
5
5
  const lines = [];
@@ -154,131 +154,6 @@ export function generateOrmAgentsDocs(tables, customOperations) {
154
154
  content: lines.join('\n'),
155
155
  };
156
156
  }
157
- export function getOrmMcpTools(tables, customOperations) {
158
- const tools = [];
159
- for (const table of tables) {
160
- const { singularName } = getTableNames(table);
161
- const pk = getPrimaryKeyInfo(table)[0];
162
- const scalarFields = getScalarFields(table);
163
- const editableFields = getEditableFields(table);
164
- tools.push({
165
- name: `orm_${lcFirst(singularName)}_findMany`,
166
- description: table.description || `List all ${table.name} records via ORM`,
167
- inputSchema: {
168
- type: 'object',
169
- properties: {
170
- first: {
171
- type: 'integer',
172
- description: 'Limit number of results',
173
- },
174
- offset: {
175
- type: 'integer',
176
- description: 'Offset for pagination',
177
- },
178
- },
179
- },
180
- });
181
- tools.push({
182
- name: `orm_${lcFirst(singularName)}_findOne`,
183
- description: table.description || `Get a single ${table.name} record by ${pk.name}`,
184
- inputSchema: {
185
- type: 'object',
186
- properties: {
187
- [pk.name]: {
188
- type: gqlTypeToJsonSchemaType(pk.gqlType),
189
- description: `${table.name} ${pk.name}`,
190
- },
191
- },
192
- required: [pk.name],
193
- },
194
- });
195
- const createProps = {};
196
- for (const f of editableFields) {
197
- createProps[f.name] = {
198
- type: gqlTypeToJsonSchemaType(f.type.gqlType),
199
- description: `${table.name} ${f.name}`,
200
- };
201
- }
202
- tools.push({
203
- name: `orm_${lcFirst(singularName)}_create`,
204
- description: table.description || `Create a new ${table.name} record`,
205
- inputSchema: {
206
- type: 'object',
207
- properties: createProps,
208
- required: editableFields.map((f) => f.name),
209
- },
210
- });
211
- const updateProps = {
212
- [pk.name]: {
213
- type: gqlTypeToJsonSchemaType(pk.gqlType),
214
- description: `${table.name} ${pk.name}`,
215
- },
216
- };
217
- for (const f of editableFields) {
218
- updateProps[f.name] = {
219
- type: gqlTypeToJsonSchemaType(f.type.gqlType),
220
- description: `${table.name} ${f.name}`,
221
- };
222
- }
223
- tools.push({
224
- name: `orm_${lcFirst(singularName)}_update`,
225
- description: table.description || `Update an existing ${table.name} record`,
226
- inputSchema: {
227
- type: 'object',
228
- properties: updateProps,
229
- required: [pk.name],
230
- },
231
- });
232
- tools.push({
233
- name: `orm_${lcFirst(singularName)}_delete`,
234
- description: table.description || `Delete a ${table.name} record by ${pk.name}`,
235
- inputSchema: {
236
- type: 'object',
237
- properties: {
238
- [pk.name]: {
239
- type: gqlTypeToJsonSchemaType(pk.gqlType),
240
- description: `${table.name} ${pk.name}`,
241
- },
242
- },
243
- required: [pk.name],
244
- },
245
- _meta: {
246
- fields: scalarFields.map((f) => ({
247
- name: f.name,
248
- type: f.type.gqlType,
249
- editable: editableFields.some((ef) => ef.name === f.name),
250
- primaryKey: f.name === pk.name,
251
- })),
252
- },
253
- });
254
- }
255
- for (const op of customOperations) {
256
- const accessor = op.kind === 'query' ? 'query' : 'mutation';
257
- const props = {};
258
- const required = [];
259
- for (const arg of op.args) {
260
- const isRequired = arg.type.kind === 'NON_NULL';
261
- const baseType = isRequired && arg.type.ofType ? arg.type.ofType : arg.type;
262
- props[arg.name] = {
263
- type: gqlTypeToJsonSchemaType(baseType.name ?? 'String'),
264
- description: arg.description || arg.name,
265
- };
266
- if (isRequired) {
267
- required.push(arg.name);
268
- }
269
- }
270
- tools.push({
271
- name: `orm_${accessor}_${op.name}`,
272
- description: op.description || `Execute ${op.name} ${op.kind}`,
273
- inputSchema: {
274
- type: 'object',
275
- properties: props,
276
- ...(required.length > 0 ? { required } : {}),
277
- },
278
- });
279
- }
280
- return tools;
281
- }
282
157
  export function generateOrmSkills(tables, customOperations, targetName, registry) {
283
158
  const files = [];
284
159
  const skillName = `orm-${targetName}`;
@@ -1,5 +1,5 @@
1
1
  import type { GraphQLSDKConfigTarget } from '../../types/config';
2
- import type { GeneratedDocFile, McpTool } from './docs-utils';
2
+ import type { GeneratedDocFile } from './docs-utils';
3
3
  export interface TargetReadmeOptions {
4
4
  hasOrm: boolean;
5
5
  hasHooks: boolean;
@@ -10,7 +10,6 @@ export interface TargetReadmeOptions {
10
10
  config: GraphQLSDKConfigTarget;
11
11
  }
12
12
  export declare function generateTargetReadme(options: TargetReadmeOptions): GeneratedDocFile;
13
- export declare function generateCombinedMcpConfig(tools: McpTool[], name: string): GeneratedDocFile;
14
13
  export interface RootRootReadmeTarget {
15
14
  name: string;
16
15
  output: string;
@@ -72,18 +72,6 @@ export function generateTargetReadme(options) {
72
72
  content: lines.join('\n'),
73
73
  };
74
74
  }
75
- export function generateCombinedMcpConfig(tools, name) {
76
- const mcpConfig = {
77
- name,
78
- version: '1.0.0',
79
- description: `MCP tool definitions for ${name} SDK (auto-generated from GraphQL schema)`,
80
- tools,
81
- };
82
- return {
83
- fileName: 'mcp.json',
84
- content: JSON.stringify(mcpConfig, null, 2) + '\n',
85
- };
86
- }
87
75
  export function generateRootRootReadme(targets) {
88
76
  const lines = [];
89
77
  lines.push(...getReadmeHeader('GraphQL SDK'));
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Codegen utilities - naming conventions, type mapping, and helpers
3
3
  */
4
- import { lcFirst, toCamelCase, toPascalCase, toScreamingSnake, ucFirst } from 'inflekt';
4
+ import { lcFirst, toCamelCase, toConstantCase, toPascalCase, ucFirst } from 'inflekt';
5
5
  import type { Field, FieldType, Table, TypeRegistry } from '../../types/schema';
6
- export { lcFirst, toCamelCase, toPascalCase, toScreamingSnake, ucFirst };
6
+ export { lcFirst, toCamelCase, toPascalCase, toConstantCase, ucFirst };
7
7
  export interface TableNames {
8
8
  /** PascalCase singular (e.g., "Car") */
9
9
  typeName: string;
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Codegen utilities - naming conventions, type mapping, and helpers
3
3
  */
4
- import { lcFirst, pluralize, toCamelCase, toPascalCase, toScreamingSnake, ucFirst, } from 'inflekt';
4
+ import { lcFirst, pluralize, toCamelCase, toConstantCase, toPascalCase, ucFirst, } from 'inflekt';
5
5
  import { scalarToFilterType, scalarToTsType } from './scalars';
6
6
  // Re-export string manipulation helpers from inflekt (single source of truth)
7
- export { lcFirst, toCamelCase, toPascalCase, toScreamingSnake, ucFirst };
7
+ export { lcFirst, toCamelCase, toPascalCase, toConstantCase, ucFirst };
8
8
  /**
9
9
  * Derive all naming variants from a table
10
10
  */