create-auto-app 0.8.4 → 0.8.6
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.
- package/package.json +2 -2
- package/templates/shopping-app/.context/schema.graphql +4 -4
- package/templates/shopping-app/.context/schema.json +54 -62
- package/templates/shopping-app/.context/server/server/package.json +1 -2
- package/templates/shopping-app/.context/server/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/decide.specs.ts +0 -1
- package/templates/shopping-app/.context/server/server/src/domain/flows/seasonal-assistant/accepts-items-and-adds-to-their-cart/events.ts +0 -1
- package/templates/shopping-app/.context/server/server/src/domain/flows/seasonal-assistant/creates-a-chat-session/react.specs.ts +0 -1
- package/templates/shopping-app/.context/server/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/decide.specs.ts +0 -1
- package/templates/shopping-app/.context/server/server/src/domain/flows/seasonal-assistant/enters-shopping-criteria-into-assistant/events.ts +0 -1
- package/templates/shopping-app/.context/server/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria/decide.ts +1 -1
- package/templates/shopping-app/.context/server/server/src/domain/flows/seasonal-assistant/selects-items-relevant-to-the-shopping-criteria/handle.ts +2 -2
- package/templates/shopping-app/auto.config.ts +19 -11
- package/templates/shopping-app/example-integrations/ai-chat-completion/package.json +1 -2
- package/templates/shopping-app/example-integrations/cart/package.json +1 -2
- package/templates/shopping-app/example-integrations/cart-api/package.json +1 -2
- package/templates/shopping-app/example-integrations/product-catalogue/package.json +1 -2
- package/templates/shopping-app/example-integrations/product-catalogue-api/package.json +1 -2
- package/templates/shopping-app/flows/shopping-assistant.flow.ts +80 -97
- package/templates/shopping-app/package.json +4 -2
- package/templates/shopping-app/server/package.json +2 -1
- package/templates/shopping-app/server/scripts/generate-schema.ts +1 -1
- package/templates/shopping-app/server/src/integrations/index.ts +1 -1
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client/client.gen.ts +199 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client/index.ts +25 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client/types.gen.ts +232 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client/utils.gen.ts +419 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/client.gen.ts +18 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/auth.gen.ts +42 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/bodySerializer.gen.ts +92 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/params.gen.ts +153 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/pathSerializer.gen.ts +181 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/core/types.gen.ts +120 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/index.ts +2 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/types.gen.ts +133 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/generated/product-catalog/zod.gen.ts +62 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/index.ts +1 -0
- package/templates/shopping-app/server/src/integrations/product-catalogue/product-catalogue-integration.ts +211 -0
- package/templates/shopping-app/tsconfig.json +3 -1
- package/templates/shopping-app/turbo.json +19 -0
- package/templates/shopping-app/client/postcss.config.js +0 -6
- package/templates/shopping-app/server/src/integrations/product-catalogue-integration.ts +0 -363
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// This file is auto-generated by @hey-api/openapi-ts
|
|
2
|
+
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
export const zProductCatalogItem = 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()
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export const zGetApiProductsData = z.object({
|
|
15
|
+
body: z.optional(z.never()),
|
|
16
|
+
path: z.optional(z.never()),
|
|
17
|
+
query: z.optional(z.never())
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* List of all products
|
|
22
|
+
*/
|
|
23
|
+
export const zGetApiProductsResponse = z.array(zProductCatalogItem);
|
|
24
|
+
|
|
25
|
+
export const zGetApiProductsSearchData = z.object({
|
|
26
|
+
body: z.optional(z.never()),
|
|
27
|
+
path: z.optional(z.never()),
|
|
28
|
+
query: z.object({
|
|
29
|
+
q: z.string()
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* List of matching products
|
|
35
|
+
*/
|
|
36
|
+
export const zGetApiProductsSearchResponse = z.array(zProductCatalogItem);
|
|
37
|
+
|
|
38
|
+
export const zGetApiProductsCategoryByCategoryData = z.object({
|
|
39
|
+
body: z.optional(z.never()),
|
|
40
|
+
path: z.object({
|
|
41
|
+
category: z.string()
|
|
42
|
+
}),
|
|
43
|
+
query: z.optional(z.never())
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* List of products in the category
|
|
48
|
+
*/
|
|
49
|
+
export const zGetApiProductsCategoryByCategoryResponse = z.array(zProductCatalogItem);
|
|
50
|
+
|
|
51
|
+
export const zGetApiProductsByIdData = z.object({
|
|
52
|
+
body: z.optional(z.never()),
|
|
53
|
+
path: z.object({
|
|
54
|
+
id: z.string()
|
|
55
|
+
}),
|
|
56
|
+
query: z.optional(z.never())
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Product details
|
|
61
|
+
*/
|
|
62
|
+
export const zGetApiProductsByIdResponse = zProductCatalogItem;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './product-catalogue-integration';
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type { State, Integration } from '@auto-engineer/flow';
|
|
2
|
+
import { registerTool } from '@auto-engineer/ai-gateway';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
import { createClient } from './generated/product-catalog/client';
|
|
6
|
+
import type {
|
|
7
|
+
ProductCatalogItem,
|
|
8
|
+
GetApiProductsResponses,
|
|
9
|
+
GetApiProductsSearchResponses,
|
|
10
|
+
GetApiProductsSearchErrors,
|
|
11
|
+
GetApiProductsCategoryByCategoryResponses,
|
|
12
|
+
GetApiProductsByIdResponses,
|
|
13
|
+
GetApiProductsByIdErrors,
|
|
14
|
+
} from './generated/product-catalog';
|
|
15
|
+
import {
|
|
16
|
+
zGetApiProductsResponse,
|
|
17
|
+
zGetApiProductsSearchResponse,
|
|
18
|
+
zGetApiProductsCategoryByCategoryResponse,
|
|
19
|
+
zGetApiProductsByIdResponse,
|
|
20
|
+
} from './generated/product-catalog/zod.gen';
|
|
21
|
+
|
|
22
|
+
export type Product = ProductCatalogItem;
|
|
23
|
+
|
|
24
|
+
export type Products = State<'Products', { products: Product[] }>;
|
|
25
|
+
export type ProductsByCategory = State<'ProductsByCategory', { category: string; products: Product[] }>;
|
|
26
|
+
export type ProductSearchResults = State<'ProductSearchResults', { query: string; products: Product[] }>;
|
|
27
|
+
export type ProductDetails = State<'ProductDetails', { product: Product | null }>;
|
|
28
|
+
|
|
29
|
+
// ---------- Generated client instance ----------
|
|
30
|
+
const productClient = createClient({
|
|
31
|
+
baseUrl: 'http://localhost:3001',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// ---------- Integration facade ----------
|
|
35
|
+
type ProductCatalogQueries = {
|
|
36
|
+
Products: () => Promise<Products>;
|
|
37
|
+
ProductsByCategory: (params: { category: string }) => Promise<ProductsByCategory>;
|
|
38
|
+
ProductSearchResults: (params: { query: string }) => Promise<ProductSearchResults>;
|
|
39
|
+
ProductDetails: (params: { id: string }) => Promise<ProductDetails>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const _ProductCatalog: Integration<'product-catalog', ProductCatalogQueries> = {
|
|
43
|
+
__brand: 'Integration' as const,
|
|
44
|
+
type: 'product-catalog' as const,
|
|
45
|
+
name: 'product-catalog',
|
|
46
|
+
|
|
47
|
+
Queries: {
|
|
48
|
+
// GET /api/products
|
|
49
|
+
Products: async (): Promise<Products> => {
|
|
50
|
+
try {
|
|
51
|
+
const res = await productClient.get<GetApiProductsResponses, unknown, false>({ url: '/api/products' });
|
|
52
|
+
if (res.error !== undefined) console.error('Failed to fetch products:', res.error);
|
|
53
|
+
return { type: 'Products', data: { products: (res.data as Product[]) ?? [] } };
|
|
54
|
+
} catch (err) {
|
|
55
|
+
console.error('Failed to fetch products:', err);
|
|
56
|
+
return { type: 'Products', data: { products: [] as Product[] } };
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// GET /api/products/category/{category}
|
|
61
|
+
ProductsByCategory: async ({ category }): Promise<ProductsByCategory> => {
|
|
62
|
+
try {
|
|
63
|
+
const res = await productClient.get<GetApiProductsCategoryByCategoryResponses, unknown, false>({
|
|
64
|
+
url: '/api/products/category/{category}',
|
|
65
|
+
path: { category },
|
|
66
|
+
});
|
|
67
|
+
if (res.error !== undefined) console.error(`Category "${category}" error:`, res.error);
|
|
68
|
+
return { type: 'ProductsByCategory', data: { category, products: (res.data as Product[]) ?? [] } };
|
|
69
|
+
} catch (err) {
|
|
70
|
+
console.error(`Failed to fetch products for category ${category}:`, err);
|
|
71
|
+
return { type: 'ProductsByCategory', data: { category, products: [] as Product[] } };
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// GET /api/products/search?q=...
|
|
76
|
+
ProductSearchResults: async ({ query }): Promise<ProductSearchResults> => {
|
|
77
|
+
try {
|
|
78
|
+
const res = await productClient.get<GetApiProductsSearchResponses, GetApiProductsSearchErrors, false>({
|
|
79
|
+
url: '/api/products/search',
|
|
80
|
+
query: { q: query },
|
|
81
|
+
});
|
|
82
|
+
if (res.error !== undefined) console.error(`Search "${query}" error:`, res.error);
|
|
83
|
+
return { type: 'ProductSearchResults', data: { query, products: (res.data as Product[]) ?? [] } };
|
|
84
|
+
} catch (err) {
|
|
85
|
+
console.error(`Failed to search products with query "${query}":`, err);
|
|
86
|
+
return { type: 'ProductSearchResults', data: { query, products: [] as Product[] } };
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// GET /api/products/{id}
|
|
91
|
+
ProductDetails: async ({ id }): Promise<ProductDetails> => {
|
|
92
|
+
try {
|
|
93
|
+
const res = await productClient.get<GetApiProductsByIdResponses, GetApiProductsByIdErrors, false>({
|
|
94
|
+
url: '/api/products/{id}',
|
|
95
|
+
path: { id },
|
|
96
|
+
});
|
|
97
|
+
if (res.response.status === 404 || res.error !== undefined) {
|
|
98
|
+
if (res.response.status !== 404) console.error(`Error fetching product "${id}":`, res.error);
|
|
99
|
+
return { type: 'ProductDetails', data: { product: null } };
|
|
100
|
+
}
|
|
101
|
+
return { type: 'ProductDetails', data: { product: res.data ?? null } };
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error(`Failed to fetch product details for ID ${id}:`, err);
|
|
104
|
+
return { type: 'ProductDetails', data: { product: null } };
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// ---------- Lazy MCP tool registration ----------
|
|
111
|
+
let _toolsRegistered = false;
|
|
112
|
+
|
|
113
|
+
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
114
|
+
function registerProductCatalogToolsOnce(): void {
|
|
115
|
+
if (_toolsRegistered) return;
|
|
116
|
+
_toolsRegistered = true;
|
|
117
|
+
|
|
118
|
+
// All products
|
|
119
|
+
registerTool<Record<string, unknown>>(
|
|
120
|
+
'PRODUCT_CATALOGUE_PRODUCTS',
|
|
121
|
+
{
|
|
122
|
+
title: 'Get All Products',
|
|
123
|
+
description: 'Fetches all products from the product catalog',
|
|
124
|
+
inputSchema: {},
|
|
125
|
+
schema: zGetApiProductsResponse,
|
|
126
|
+
schemaName: 'GetApiProductsResponse',
|
|
127
|
+
schemaDescription: 'Array of ProductCatalogItem',
|
|
128
|
+
},
|
|
129
|
+
async () => {
|
|
130
|
+
const queries = _ProductCatalog.Queries as ProductCatalogQueries;
|
|
131
|
+
const result = await queries.Products();
|
|
132
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result.data.products, null, 2) }] };
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// By category
|
|
137
|
+
interface ProductsByCategoryParams extends Record<string, unknown> {
|
|
138
|
+
category: string;
|
|
139
|
+
}
|
|
140
|
+
registerTool<ProductsByCategoryParams>(
|
|
141
|
+
'PRODUCT_CATALOGUE_PRODUCTS_BY_CATEGORY',
|
|
142
|
+
{
|
|
143
|
+
title: 'Get Products by Category',
|
|
144
|
+
description: 'Fetches products from a specific category',
|
|
145
|
+
inputSchema: { category: z.string().min(1, 'Category is required') },
|
|
146
|
+
schema: zGetApiProductsCategoryByCategoryResponse,
|
|
147
|
+
schemaName: 'GetApiProductsCategoryByCategoryResponse',
|
|
148
|
+
schemaDescription: 'Array of ProductCatalogItem',
|
|
149
|
+
},
|
|
150
|
+
async ({ category }: ProductsByCategoryParams) => {
|
|
151
|
+
const queries = _ProductCatalog.Queries as ProductCatalogQueries;
|
|
152
|
+
const result = await queries.ProductsByCategory({ category });
|
|
153
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result.data.products, null, 2) }] };
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Search
|
|
158
|
+
interface ProductSearchParams extends Record<string, unknown> {
|
|
159
|
+
query: string;
|
|
160
|
+
}
|
|
161
|
+
registerTool<ProductSearchParams>(
|
|
162
|
+
'PRODUCT_CATALOGUE_SEARCH',
|
|
163
|
+
{
|
|
164
|
+
title: 'Search Products',
|
|
165
|
+
description: 'Search for products using a query string',
|
|
166
|
+
inputSchema: { query: z.string().min(1, 'Search query is required') },
|
|
167
|
+
schema: zGetApiProductsSearchResponse,
|
|
168
|
+
schemaName: 'GetApiProductsSearchResponse',
|
|
169
|
+
schemaDescription: 'Array of ProductCatalogItem',
|
|
170
|
+
},
|
|
171
|
+
async ({ query }: ProductSearchParams) => {
|
|
172
|
+
const queries = _ProductCatalog.Queries as ProductCatalogQueries;
|
|
173
|
+
const result = await queries.ProductSearchResults({ query });
|
|
174
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result.data.products, null, 2) }] };
|
|
175
|
+
},
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
// Details
|
|
179
|
+
interface ProductDetailsParams extends Record<string, unknown> {
|
|
180
|
+
id: string;
|
|
181
|
+
}
|
|
182
|
+
registerTool<ProductDetailsParams>(
|
|
183
|
+
'PRODUCT_CATALOGUE_PRODUCT_DETAILS',
|
|
184
|
+
{
|
|
185
|
+
title: 'Get Product Details',
|
|
186
|
+
description: 'Fetches detailed information about a specific product',
|
|
187
|
+
inputSchema: { id: z.string().min(1, 'Product ID is required') },
|
|
188
|
+
schema: zGetApiProductsByIdResponse,
|
|
189
|
+
schemaName: 'GetApiProductsByIdResponse',
|
|
190
|
+
schemaDescription: 'Single ProductCatalogItem',
|
|
191
|
+
},
|
|
192
|
+
async ({ id }: ProductDetailsParams) => {
|
|
193
|
+
const queries = _ProductCatalog.Queries as ProductCatalogQueries;
|
|
194
|
+
const result = await queries.ProductDetails({ id });
|
|
195
|
+
if (result.data.product === null) {
|
|
196
|
+
return { content: [{ type: 'text' as const, text: `Product with ID "${id}" not found` }], isError: true };
|
|
197
|
+
}
|
|
198
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result.data.product, null, 2) }] };
|
|
199
|
+
},
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
/* eslint-enable @typescript-eslint/no-unsafe-call */
|
|
203
|
+
|
|
204
|
+
// registers tools on *first usage* of the integration
|
|
205
|
+
export const ProductCatalog: Integration<'product-catalog', ProductCatalogQueries> = new Proxy(_ProductCatalog, {
|
|
206
|
+
get(target, prop, receiver) {
|
|
207
|
+
// First touch of ProductCatalog triggers tool registration
|
|
208
|
+
registerProductCatalogToolsOnce();
|
|
209
|
+
return Reflect.get(target, prop, receiver) as unknown;
|
|
210
|
+
},
|
|
211
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://turbo.build/schema.json",
|
|
3
|
+
"extends": ["//"],
|
|
4
|
+
"tasks": {
|
|
5
|
+
"auto": {
|
|
6
|
+
"dependsOn": ["^build"],
|
|
7
|
+
"inputs": []
|
|
8
|
+
},
|
|
9
|
+
"auto:debug": {
|
|
10
|
+
"dependsOn": ["^build"],
|
|
11
|
+
"inputs": []
|
|
12
|
+
},
|
|
13
|
+
"build": {
|
|
14
|
+
"dependsOn": ["^build"],
|
|
15
|
+
"inputs": [],
|
|
16
|
+
"outputs": []
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -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
|
-
);
|