create-auto-app 0.8.4 → 0.8.5

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 (20) hide show
  1. package/package.json +1 -1
  2. package/templates/shopping-app/package.json +3 -1
  3. package/templates/shopping-app/server/package.json +2 -1
  4. package/templates/shopping-app/server/src/integrations/index.ts +1 -1
  5. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client/client.gen.ts +199 -0
  6. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client/index.ts +25 -0
  7. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client/types.gen.ts +232 -0
  8. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client/utils.gen.ts +419 -0
  9. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client.gen.ts +18 -0
  10. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/auth.gen.ts +42 -0
  11. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/bodySerializer.gen.ts +92 -0
  12. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/params.gen.ts +153 -0
  13. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/pathSerializer.gen.ts +181 -0
  14. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/types.gen.ts +120 -0
  15. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/index.ts +2 -0
  16. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/types.gen.ts +133 -0
  17. package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/zod.gen.ts +62 -0
  18. package/templates/shopping-app/server/src/integrations/product-catalogue/index.ts +1 -0
  19. package/templates/shopping-app/server/src/integrations/product-catalogue/product-catalogue-integration.ts +232 -0
  20. package/templates/shopping-app/server/src/integrations/product-catalogue-integration.ts +0 -363
@@ -0,0 +1,232 @@
1
+ import type { State, Integration } from '@auto-engineer/flow';
2
+ import { registerTool, z } from '@auto-engineer/ai-gateway';
3
+
4
+ import { createClient } from './generated/product-catalog/client';
5
+ import type {
6
+ ProductCatalogItem,
7
+ GetApiProductsResponses,
8
+ GetApiProductsSearchResponses,
9
+ GetApiProductsSearchErrors,
10
+ GetApiProductsCategoryByCategoryResponses,
11
+ GetApiProductsByIdResponses,
12
+ GetApiProductsByIdErrors,
13
+ } from './generated/product-catalog';
14
+ import {
15
+ zGetApiProductsResponse,
16
+ zGetApiProductsSearchResponse,
17
+ zGetApiProductsCategoryByCategoryResponse,
18
+ zGetApiProductsByIdResponse,
19
+ } from './generated/product-catalog/zod.gen';
20
+
21
+ export type Product = ProductCatalogItem;
22
+
23
+ export type Products = State<'Products', { products: Product[] }>;
24
+ export type ProductsByCategory = State<'ProductsByCategory', { category: string; products: Product[] }>;
25
+ export type ProductSearchResults = State<'ProductSearchResults', { query: string; products: Product[] }>;
26
+ export type ProductDetails = State<'ProductDetails', { product: Product | null }>;
27
+
28
+ // ---------- Generated client instance ----------
29
+ const productClient = createClient({
30
+ baseUrl: 'http://localhost:3001',
31
+ });
32
+
33
+ // ---------- Integration facade ----------
34
+ type ProductCatalogQueries = {
35
+ Products: () => Promise<Products>;
36
+ ProductsByCategory: (params: { category: string }) => Promise<ProductsByCategory>;
37
+ ProductSearchResults: (params: { query: string }) => Promise<ProductSearchResults>;
38
+ ProductDetails: (params: { id: string }) => Promise<ProductDetails>;
39
+ };
40
+
41
+ const _ProductCatalog: Integration<'product-catalog', ProductCatalogQueries> = {
42
+ __brand: 'Integration' as const,
43
+ type: 'product-catalog' as const,
44
+ name: 'product-catalog',
45
+
46
+ Queries: {
47
+ // GET /api/products
48
+ Products: async (): Promise<Products> => {
49
+ try {
50
+ const res = await productClient.get<GetApiProductsResponses, unknown, false>({ url: '/api/products' });
51
+ if (res.error !== undefined) console.error('Failed to fetch products:', res.error);
52
+ return { type: 'Products', data: { products: (res.data as Product[]) ?? [] } };
53
+ } catch (err) {
54
+ console.error('Failed to fetch products:', err);
55
+ return { type: 'Products', data: { products: [] as Product[] } };
56
+ }
57
+ },
58
+
59
+ // GET /api/products/category/{category}
60
+ ProductsByCategory: async ({ category }): Promise<ProductsByCategory> => {
61
+ try {
62
+ const res = await productClient.get<GetApiProductsCategoryByCategoryResponses, unknown, false>({
63
+ url: '/api/products/category/{category}',
64
+ path: { category },
65
+ });
66
+ if (res.error !== undefined) console.error(`Category "${category}" error:`, res.error);
67
+ return { type: 'ProductsByCategory', data: { category, products: (res.data as Product[]) ?? [] } };
68
+ } catch (err) {
69
+ console.error(`Failed to fetch products for category ${category}:`, err);
70
+ return { type: 'ProductsByCategory', data: { category, products: [] as Product[] } };
71
+ }
72
+ },
73
+
74
+ // GET /api/products/search?q=...
75
+ ProductSearchResults: async ({ query }): Promise<ProductSearchResults> => {
76
+ try {
77
+ const res = await productClient.get<GetApiProductsSearchResponses, GetApiProductsSearchErrors, false>({
78
+ url: '/api/products/search',
79
+ query: { q: query },
80
+ });
81
+ if (res.error !== undefined) console.error(`Search "${query}" error:`, res.error);
82
+ return { type: 'ProductSearchResults', data: { query, products: (res.data as Product[]) ?? [] } };
83
+ } catch (err) {
84
+ console.error(`Failed to search products with query "${query}":`, err);
85
+ return { type: 'ProductSearchResults', data: { query, products: [] as Product[] } };
86
+ }
87
+ },
88
+
89
+ // GET /api/products/{id}
90
+ ProductDetails: async ({ id }): Promise<ProductDetails> => {
91
+ try {
92
+ const res = await productClient.get<GetApiProductsByIdResponses, GetApiProductsByIdErrors, false>({
93
+ url: '/api/products/{id}',
94
+ path: { id },
95
+ });
96
+ if (res.response.status === 404 || res.error !== undefined) {
97
+ if (res.response.status !== 404) console.error(`Error fetching product "${id}":`, res.error);
98
+ return { type: 'ProductDetails', data: { product: null } };
99
+ }
100
+ return { type: 'ProductDetails', data: { product: res.data ?? null } };
101
+ } catch (err) {
102
+ console.error(`Failed to fetch product details for ID ${id}:`, err);
103
+ return { type: 'ProductDetails', data: { product: null } };
104
+ }
105
+ },
106
+ },
107
+ };
108
+
109
+ // ---------- Lazy MCP tool registration ----------
110
+ let _toolsRegistered = false;
111
+
112
+ function registerProductCatalogToolsOnce(): void {
113
+ if (_toolsRegistered) return;
114
+ _toolsRegistered = true;
115
+
116
+ // All products
117
+ registerTool<Record<string, unknown>>(
118
+ 'PRODUCT_CATALOGUE_PRODUCTS',
119
+ {
120
+ title: 'Get All Products',
121
+ description: 'Fetches all products from the product catalog',
122
+ inputSchema: {},
123
+ schema: zGetApiProductsResponse,
124
+ schemaName: 'GetApiProductsResponse',
125
+ schemaDescription: 'Array of ProductCatalogItem',
126
+ },
127
+ async () => {
128
+ const queries = _ProductCatalog.Queries as ProductCatalogQueries;
129
+ if (!queries?.Products) {
130
+ return {
131
+ content: [{ type: 'text' as const, text: 'ProductCatalog.Queries.Products is not available' }],
132
+ isError: true,
133
+ };
134
+ }
135
+ const result = await queries.Products();
136
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result.data.products, null, 2) }] };
137
+ },
138
+ );
139
+
140
+ // By category
141
+ interface ProductsByCategoryParams extends Record<string, unknown> {
142
+ category: string;
143
+ }
144
+ registerTool<ProductsByCategoryParams>(
145
+ 'PRODUCT_CATALOGUE_PRODUCTS_BY_CATEGORY',
146
+ {
147
+ title: 'Get Products by Category',
148
+ description: 'Fetches products from a specific category',
149
+ inputSchema: { category: z.string().min(1, 'Category is required') },
150
+ schema: zGetApiProductsCategoryByCategoryResponse,
151
+ schemaName: 'GetApiProductsCategoryByCategoryResponse',
152
+ schemaDescription: 'Array of ProductCatalogItem',
153
+ },
154
+ async ({ category }) => {
155
+ const queries = _ProductCatalog.Queries as ProductCatalogQueries;
156
+ if (!queries?.ProductsByCategory) {
157
+ return {
158
+ content: [{ type: 'text' as const, text: 'ProductCatalog.Queries.ProductsByCategory is not available' }],
159
+ isError: true,
160
+ };
161
+ }
162
+ const result = await queries.ProductsByCategory({ category });
163
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result.data.products, null, 2) }] };
164
+ },
165
+ );
166
+
167
+ // Search
168
+ interface ProductSearchParams extends Record<string, unknown> {
169
+ query: string;
170
+ }
171
+ registerTool<ProductSearchParams>(
172
+ 'PRODUCT_CATALOGUE_SEARCH',
173
+ {
174
+ title: 'Search Products',
175
+ description: 'Search for products using a query string',
176
+ inputSchema: { query: z.string().min(1, 'Search query is required') },
177
+ schema: zGetApiProductsSearchResponse,
178
+ schemaName: 'GetApiProductsSearchResponse',
179
+ schemaDescription: 'Array of ProductCatalogItem',
180
+ },
181
+ async ({ query }) => {
182
+ const queries = _ProductCatalog.Queries as ProductCatalogQueries;
183
+ if (!queries?.ProductSearchResults) {
184
+ return {
185
+ content: [{ type: 'text' as const, text: 'ProductCatalog.Queries.ProductSearchResults is not available' }],
186
+ isError: true,
187
+ };
188
+ }
189
+ const result = await queries.ProductSearchResults({ query });
190
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result.data.products, null, 2) }] };
191
+ },
192
+ );
193
+
194
+ // Details
195
+ interface ProductDetailsParams extends Record<string, unknown> {
196
+ id: string;
197
+ }
198
+ registerTool<ProductDetailsParams>(
199
+ 'PRODUCT_CATALOGUE_PRODUCT_DETAILS',
200
+ {
201
+ title: 'Get Product Details',
202
+ description: 'Fetches detailed information about a specific product',
203
+ inputSchema: { id: z.string().min(1, 'Product ID is required') },
204
+ schema: zGetApiProductsByIdResponse,
205
+ schemaName: 'GetApiProductsByIdResponse',
206
+ schemaDescription: 'Single ProductCatalogItem',
207
+ },
208
+ async ({ id }) => {
209
+ const queries = _ProductCatalog.Queries as ProductCatalogQueries;
210
+ if (!queries?.ProductDetails) {
211
+ return {
212
+ content: [{ type: 'text' as const, text: 'ProductCatalog.Queries.ProductDetails is not available' }],
213
+ isError: true,
214
+ };
215
+ }
216
+ const result = await queries.ProductDetails({ id });
217
+ if (result.data.product === null) {
218
+ return { content: [{ type: 'text' as const, text: `Product with ID "${id}" not found` }], isError: true };
219
+ }
220
+ return { content: [{ type: 'text' as const, text: JSON.stringify(result.data.product, null, 2) }] };
221
+ },
222
+ );
223
+ }
224
+
225
+ // registers tools on *first usage* of the integration
226
+ export const ProductCatalog: Integration<'product-catalog', ProductCatalogQueries> = new Proxy(_ProductCatalog, {
227
+ get(target, prop, receiver) {
228
+ // First touch of ProductCatalog triggers tool registration
229
+ registerProductCatalogToolsOnce();
230
+ return Reflect.get(target, prop, receiver);
231
+ },
232
+ });
@@ -1,363 +0,0 @@
1
- import type { State, Integration } from '@auto-engineer/flow';
2
- import axios from 'axios';
3
- import { registerTool, z } from '@auto-engineer/ai-gateway';
4
-
5
- export const ProductSchema = z.object({
6
- productId: z.string(),
7
- name: z.string(),
8
- category: z.string(),
9
- price: z.number(),
10
- tags: z.array(z.string()),
11
- imageUrl: z.string().url(),
12
- });
13
-
14
- const ProductsSchema = z.object({
15
- type: z.literal('Products'),
16
- data: z.object({
17
- products: z.array(ProductSchema),
18
- }),
19
- });
20
-
21
- export type Product = {
22
- productId: string;
23
- name: string;
24
- category: string;
25
- price: number;
26
- tags: string[];
27
- imageUrl: string;
28
- };
29
-
30
- export type Products = State<
31
- 'Products',
32
- {
33
- products: Product[];
34
- }
35
- >;
36
-
37
- export type ProductsByCategory = State<
38
- 'ProductsByCategory',
39
- {
40
- category: string;
41
- products: Product[];
42
- }
43
- >;
44
-
45
- export type ProductSearchResults = State<
46
- 'ProductSearchResults',
47
- {
48
- query: string;
49
- products: Product[];
50
- }
51
- >;
52
-
53
- export type ProductDetails = State<
54
- 'ProductDetails',
55
- {
56
- product: Product | null;
57
- }
58
- >;
59
-
60
- const client = axios.create({
61
- baseURL: 'http://localhost:3001',
62
- timeout: 10000,
63
- headers: {
64
- 'Content-Type': 'application/json',
65
- },
66
- });
67
-
68
- type ProductCatalogQueries = {
69
- Products: () => Promise<Products>;
70
- ProductsByCategory: (params: { category: string }) => Promise<ProductsByCategory>;
71
- ProductSearchResults: (params: { query: string }) => Promise<ProductSearchResults>;
72
- ProductDetails: (params: { id: string }) => Promise<ProductDetails>;
73
- };
74
-
75
- export const ProductCatalog: Integration<'product-catalog', ProductCatalogQueries> = {
76
- __brand: 'Integration' as const,
77
- type: 'product-catalog' as const,
78
- name: 'product-catalog',
79
- Queries: {
80
- schema: {
81
- Products: ProductsSchema,
82
- // ProductsByCategory: z.object({
83
- // category: z.string(),
84
- // products: z.array(ProductSchema),
85
- // }),
86
- // ProductSearchResults: z.object({
87
- // query: z.string(),
88
- // products: z.array(ProductSchema),
89
- // }),
90
- // ProductDetails: z.object({
91
- // product: ProductSchema.nullable(),
92
- // }),
93
- },
94
- Products: async (): Promise<Products> => {
95
- try {
96
- const products = (await client.get<Product[]>('/api/products')).data;
97
- return {
98
- type: 'Products',
99
- data: {
100
- products,
101
- },
102
- };
103
- } catch (error) {
104
- console.error('Failed to fetch products:', error);
105
- return {
106
- type: 'Products',
107
- data: {
108
- products: [],
109
- },
110
- };
111
- }
112
- },
113
- ProductsByCategory: async (params: { category: string }): Promise<ProductsByCategory> => {
114
- try {
115
- const products = (await client.get<Product[]>(`/api/products/category/${params.category}`)).data;
116
- return {
117
- type: 'ProductsByCategory',
118
- data: {
119
- category: params.category,
120
- products,
121
- },
122
- };
123
- } catch (error) {
124
- console.error(`Failed to fetch products for category ${params.category}:`, error);
125
- return {
126
- type: 'ProductsByCategory',
127
- data: {
128
- category: params.category,
129
- products: [],
130
- },
131
- };
132
- }
133
- },
134
- ProductSearchResults: async (params: { query: string }): Promise<ProductSearchResults> => {
135
- try {
136
- const products = (
137
- await client.get<Product[]>('/api/products/search', {
138
- params: { q: params.query },
139
- })
140
- ).data;
141
- return {
142
- type: 'ProductSearchResults',
143
- data: {
144
- query: params.query,
145
- products,
146
- },
147
- };
148
- } catch (error) {
149
- console.error(`Failed to search products with query "${params.query}":`, error);
150
- return {
151
- type: 'ProductSearchResults',
152
- data: {
153
- query: params.query,
154
- products: [],
155
- },
156
- };
157
- }
158
- },
159
- ProductDetails: async (params: { id: string }): Promise<ProductDetails> => {
160
- try {
161
- const product = (await client.get<Product>(`/api/products/${params.id}`)).data;
162
- return {
163
- type: 'ProductDetails',
164
- data: {
165
- product,
166
- },
167
- };
168
- } catch (error) {
169
- if (axios.isAxiosError(error) && error.response?.status === 404) {
170
- return {
171
- type: 'ProductDetails',
172
- data: {
173
- product: null,
174
- },
175
- };
176
- }
177
- console.error(`Failed to fetch product details for ID ${params.id}:`, error);
178
- return {
179
- type: 'ProductDetails',
180
- data: {
181
- product: null,
182
- },
183
- };
184
- }
185
- },
186
- },
187
- };
188
-
189
- // Register MCP tools for ProductCatalog queries
190
-
191
- // Type definitions for query functions
192
- type ProductsQuery = () => Promise<Products>;
193
- type ProductsByCategoryQuery = (params: { category: string }) => Promise<ProductsByCategory>;
194
- type ProductSearchQuery = (params: { query: string }) => Promise<ProductSearchResults>;
195
- type ProductDetailsQuery = (params: { id: string }) => Promise<ProductDetails>;
196
-
197
- // Tool for fetching all products
198
- registerTool<Record<string, unknown>>(
199
- 'PRODUCT_CATALOGUE_PRODUCTS',
200
- {
201
- title: 'Get All Products',
202
- description: 'Fetches all products from the product catalog',
203
- inputSchema: {},
204
- schema: ProductsSchema,
205
- schemaName: 'Products',
206
- schemaDescription: 'A list of products with id, name, category, price, tags, and imageUrl',
207
- },
208
- async () => {
209
- const queries = ProductCatalog.Queries;
210
- if (!queries?.Products) {
211
- return {
212
- content: [
213
- {
214
- type: 'text' as const,
215
- text: 'ProductCatalog.Queries.Products is not available',
216
- },
217
- ],
218
- isError: true,
219
- };
220
- }
221
- const productsQuery = queries.Products as ProductsQuery;
222
- const result = await productsQuery();
223
- return {
224
- content: [
225
- {
226
- type: 'text' as const,
227
- text: JSON.stringify(result.data, null, 2),
228
- },
229
- ],
230
- };
231
- },
232
- );
233
-
234
- // Tool for fetching products by category
235
- interface ProductsByCategoryParams extends Record<string, unknown> {
236
- category: string;
237
- }
238
-
239
- registerTool<ProductsByCategoryParams>(
240
- 'PRODUCT_CATALOGUE_PRODUCTS_BY_CATEGORY',
241
- {
242
- title: 'Get Products by Category',
243
- description: 'Fetches products from a specific category',
244
- inputSchema: {
245
- category: z.string().min(1, 'Category is required'),
246
- },
247
- },
248
- async ({ category }) => {
249
- const queries = ProductCatalog.Queries;
250
- if (!queries?.ProductsByCategory) {
251
- return {
252
- content: [
253
- {
254
- type: 'text' as const,
255
- text: 'ProductCatalog.Queries.ProductsByCategory is not available',
256
- },
257
- ],
258
- isError: true,
259
- };
260
- }
261
- const categoryQuery = queries.ProductsByCategory as ProductsByCategoryQuery;
262
- const result = await categoryQuery({ category });
263
- return {
264
- content: [
265
- {
266
- type: 'text' as const,
267
- text: JSON.stringify(result.data, null, 2),
268
- },
269
- ],
270
- };
271
- },
272
- );
273
-
274
- // Tool for searching products
275
- interface ProductSearchParams extends Record<string, unknown> {
276
- query: string;
277
- }
278
-
279
- registerTool<ProductSearchParams>(
280
- 'PRODUCT_CATALOGUE_SEARCH',
281
- {
282
- title: 'Search Products',
283
- description: 'Search for products using a query string',
284
- inputSchema: {
285
- query: z.string().min(1, 'Search query is required'),
286
- },
287
- },
288
- async ({ query }) => {
289
- const queries = ProductCatalog.Queries;
290
- if (!queries?.ProductSearchResults) {
291
- return {
292
- content: [
293
- {
294
- type: 'text' as const,
295
- text: 'ProductCatalog.Queries.ProductSearchResults is not available',
296
- },
297
- ],
298
- isError: true,
299
- };
300
- }
301
- const searchQuery = queries.ProductSearchResults as ProductSearchQuery;
302
- const result = await searchQuery({ query });
303
- return {
304
- content: [
305
- {
306
- type: 'text' as const,
307
- text: JSON.stringify(result.data, null, 2),
308
- },
309
- ],
310
- };
311
- },
312
- );
313
-
314
- // Tool for getting product details
315
- interface ProductDetailsParams extends Record<string, unknown> {
316
- id: string;
317
- }
318
-
319
- registerTool<ProductDetailsParams>(
320
- 'PRODUCT_CATALOGUE_PRODUCT_DETAILS',
321
- {
322
- title: 'Get Product Details',
323
- description: 'Fetches detailed information about a specific product',
324
- inputSchema: {
325
- id: z.string().min(1, 'Product ID is required'),
326
- },
327
- },
328
- async ({ id }) => {
329
- const queries = ProductCatalog.Queries;
330
- if (!queries?.ProductDetails) {
331
- return {
332
- content: [
333
- {
334
- type: 'text' as const,
335
- text: 'ProductCatalog.Queries.ProductDetails is not available',
336
- },
337
- ],
338
- isError: true,
339
- };
340
- }
341
- const detailsQuery = queries.ProductDetails as ProductDetailsQuery;
342
- const result = await detailsQuery({ id });
343
- if (result.data.product === null) {
344
- return {
345
- content: [
346
- {
347
- type: 'text' as const,
348
- text: `Product with ID "${id}" not found`,
349
- },
350
- ],
351
- isError: true,
352
- };
353
- }
354
- return {
355
- content: [
356
- {
357
- type: 'text' as const,
358
- text: JSON.stringify(result.data, null, 2),
359
- },
360
- ],
361
- };
362
- },
363
- );