apprecio-mcp-base 1.0.9 → 1.1.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.
@@ -6,17 +6,156 @@ import path from 'path';
6
6
  import fs from 'fs-extra';
7
7
  import { generateFeature } from './generators/feature-generator.js';
8
8
  import { updateMainFile } from './generators/main-updater.js';
9
+ import { initProject } from './generators/init-generator.js';
9
10
  const program = new Command();
10
11
  program
11
12
  .name('mcp-generate')
12
- .description('CLI para generar features MCP automáticamente')
13
- .version('1.0.0');
13
+ .description('CLI para generar proyectos y features MCP automáticamente')
14
+ .version('1.2.0');
15
+ // ==========================================
16
+ // COMANDO: init
17
+ // ==========================================
18
+ program
19
+ .command('init')
20
+ .description('Inicializa un nuevo proyecto MCP con estructura completa')
21
+ .action(async () => {
22
+ console.log(chalk.blue.bold('\n🚀 Inicializar Nuevo Proyecto MCP\n'));
23
+ const answers = await inquirer.prompt([
24
+ {
25
+ type: 'input',
26
+ name: 'projectName',
27
+ message: 'Nombre del proyecto:',
28
+ default: 'my-mcp-server',
29
+ validate: (input) => {
30
+ if (!input)
31
+ return 'El nombre es requerido';
32
+ if (!/^[a-z][a-z0-9-]*$/.test(input)) {
33
+ return 'El nombre debe ser kebab-case (solo minúsculas, números y guiones)';
34
+ }
35
+ return true;
36
+ }
37
+ },
38
+ {
39
+ type: 'input',
40
+ name: 'projectPath',
41
+ message: 'Ruta donde crear el proyecto:',
42
+ default: (answers) => `./${answers.projectName}`,
43
+ validate: async (input) => {
44
+ if (!input)
45
+ return 'La ruta es requerida';
46
+ const exists = await fs.pathExists(input);
47
+ if (exists) {
48
+ return `La ruta "${input}" ya existe. Elige otra ubicación.`;
49
+ }
50
+ return true;
51
+ }
52
+ },
53
+ {
54
+ type: 'input',
55
+ name: 'description',
56
+ message: 'Descripción del proyecto:',
57
+ default: (answers) => `MCP Server for ${answers.projectName}`
58
+ },
59
+ {
60
+ type: 'input',
61
+ name: 'author',
62
+ message: 'Autor:',
63
+ default: ''
64
+ },
65
+ {
66
+ type: 'confirm',
67
+ name: 'useDatabase',
68
+ message: '¿Usar MongoDB?',
69
+ default: false
70
+ },
71
+ {
72
+ type: 'input',
73
+ name: 'apiBaseUrl',
74
+ message: 'Base URL de tu API:',
75
+ default: 'https://api.example.com',
76
+ when: (answers) => !answers.useDatabase
77
+ }
78
+ ]);
79
+ try {
80
+ console.log(chalk.yellow('\n📦 Creando proyecto...\n'));
81
+ await initProject({
82
+ projectName: answers.projectName,
83
+ projectPath: answers.projectPath,
84
+ description: answers.description,
85
+ author: answers.author,
86
+ useDatabase: answers.useDatabase,
87
+ apiBaseUrl: answers.apiBaseUrl
88
+ });
89
+ console.log(chalk.green('\n✅ ¡Proyecto creado exitosamente!\n'));
90
+ console.log(chalk.blue.bold('📚 Próximos pasos:\n'));
91
+ console.log(chalk.white(`1. cd ${answers.projectPath}`));
92
+ console.log(chalk.white(`2. npm install`));
93
+ console.log(chalk.white(`3. Edita .env con tu configuración`));
94
+ console.log(chalk.white(`4. npm run dev\n`));
95
+ console.log(chalk.blue.bold('🔧 Comandos útiles:\n'));
96
+ console.log(chalk.white(` npm run generate - Generar nueva feature`));
97
+ console.log(chalk.white(` npm run dev - Ejecutar en desarrollo`));
98
+ console.log(chalk.white(` npm run build - Compilar proyecto\n`));
99
+ }
100
+ catch (error) {
101
+ console.error(chalk.red('\n❌ Error creando proyecto:'), error.message);
102
+ process.exit(1);
103
+ }
104
+ });
105
+ program
106
+ .command('tool')
107
+ .description('Agrega un tool a una feature existente')
108
+ .action(async () => {
109
+ console.log(chalk.blue.bold('\n🔧 Agregar Tool a Feature\n'));
110
+ // Listar features existentes
111
+ const featuresPath = path.join(process.cwd(), 'src', 'features');
112
+ const features = await fs.readdir(featuresPath);
113
+ if (features.length === 0) {
114
+ console.log(chalk.red('No hay features disponibles. Crea una primero con: mcp-generate feature'));
115
+ process.exit(1);
116
+ }
117
+ const answers = await inquirer.prompt([
118
+ {
119
+ type: 'list',
120
+ name: 'feature',
121
+ message: 'Selecciona la feature:',
122
+ choices: features
123
+ },
124
+ {
125
+ type: 'input',
126
+ name: 'toolName',
127
+ message: 'Nombre del tool (ej: approve_item):',
128
+ validate: (input) => {
129
+ if (!input)
130
+ return 'El nombre es requerido';
131
+ if (!/^[a-z_]+$/.test(input)) {
132
+ return 'El nombre debe ser snake_case';
133
+ }
134
+ return true;
135
+ }
136
+ },
137
+ {
138
+ type: 'input',
139
+ name: 'toolDescription',
140
+ message: 'Descripción del tool:',
141
+ },
142
+ {
143
+ type: 'confirm',
144
+ name: 'addToService',
145
+ message: '¿Agregar método al service?',
146
+ default: true
147
+ }
148
+ ]);
149
+ console.log(chalk.green(`\n✅ Tool ${answers.toolName} agregado a ${answers.feature}\n`));
150
+ });
151
+ // ==========================================
152
+ // COMANDO: feature
153
+ // ==========================================
14
154
  program
15
155
  .command('feature')
16
156
  .description('Genera una nueva feature completa (router, service, validation)')
17
157
  .action(async () => {
18
158
  console.log(chalk.blue.bold('\n🚀 Generador de Features MCP\n'));
19
- // Preguntas interactivas
20
159
  const answers = await inquirer.prompt([
21
160
  {
22
161
  type: 'input',
@@ -90,9 +229,7 @@ program
90
229
  try {
91
230
  console.log(chalk.yellow('\n📝 Generando archivos...\n'));
92
231
  const featurePath = path.join(process.cwd(), 'src', 'features', answers.featureName);
93
- // Crear directorio de la feature
94
232
  await fs.ensureDir(featurePath);
95
- // Generar archivos
96
233
  const files = await generateFeature({
97
234
  featureName: answers.featureName,
98
235
  featureDescription: answers.featureDescription,
@@ -101,18 +238,15 @@ program
101
238
  serviceType: answers.needsService ? answers.serviceType : 'none',
102
239
  featurePath
103
240
  });
104
- // Mostrar archivos generados
105
241
  console.log(chalk.green('✅ Archivos generados:\n'));
106
- files.forEach(file => {
242
+ files.forEach((file) => {
107
243
  console.log(chalk.gray(` ${file}`));
108
244
  });
109
- // Actualizar main.ts si el usuario lo solicitó
110
245
  if (answers.updateMain) {
111
246
  console.log(chalk.yellow('\n🔄 Actualizando main.ts...\n'));
112
247
  await updateMainFile(answers.featureName, answers.entityName);
113
248
  console.log(chalk.green('✅ main.ts actualizado\n'));
114
249
  }
115
- // Instrucciones finales
116
250
  console.log(chalk.blue.bold('\n📚 Próximos pasos:\n'));
117
251
  console.log(chalk.white(`1. Revisa los archivos generados en: ${chalk.cyan(`src/features/${answers.featureName}`)}`));
118
252
  console.log(chalk.white(`2. Implementa la lógica en: ${chalk.cyan(`${answers.featureName}.service.ts`)}`));
@@ -128,51 +262,5 @@ program
128
262
  process.exit(1);
129
263
  }
130
264
  });
131
- program
132
- .command('tool')
133
- .description('Agrega un tool a una feature existente')
134
- .action(async () => {
135
- console.log(chalk.blue.bold('\n🔧 Agregar Tool a Feature\n'));
136
- // Listar features existentes
137
- const featuresPath = path.join(process.cwd(), 'src', 'features');
138
- const features = await fs.readdir(featuresPath);
139
- if (features.length === 0) {
140
- console.log(chalk.red('No hay features disponibles. Crea una primero con: mcp-generate feature'));
141
- process.exit(1);
142
- }
143
- const answers = await inquirer.prompt([
144
- {
145
- type: 'list',
146
- name: 'feature',
147
- message: 'Selecciona la feature:',
148
- choices: features
149
- },
150
- {
151
- type: 'input',
152
- name: 'toolName',
153
- message: 'Nombre del tool (ej: approve_item):',
154
- validate: (input) => {
155
- if (!input)
156
- return 'El nombre es requerido';
157
- if (!/^[a-z_]+$/.test(input)) {
158
- return 'El nombre debe ser snake_case';
159
- }
160
- return true;
161
- }
162
- },
163
- {
164
- type: 'input',
165
- name: 'toolDescription',
166
- message: 'Descripción del tool:',
167
- },
168
- {
169
- type: 'confirm',
170
- name: 'addToService',
171
- message: '¿Agregar método al service?',
172
- default: true
173
- }
174
- ]);
175
- console.log(chalk.green(`\n✅ Tool ${answers.toolName} agregado a ${answers.feature}\n`));
176
- });
177
265
  program.parse();
178
266
  //# sourceMappingURL=generate-feature.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apprecio-mcp-base",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "description": "Base package for creating Apprecio MCP servers with consistency",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/cli/README.md DELETED
@@ -1,432 +0,0 @@
1
- # 🛠️ @apprecio/mcp-cli
2
-
3
- CLI para generar features MCP automáticamente siguiendo la estructura de Apprecio.
4
-
5
- ## 🚀 Instalación
6
-
7
- ```bash
8
- # Instalación global
9
- npm install -g @apprecio/mcp-cli
10
-
11
- # O usar npx directamente
12
- npx @apprecio/mcp-cli feature
13
- ```
14
-
15
- ## 📖 Uso
16
-
17
- ### Generar una Feature Completa
18
-
19
- ```bash
20
- mcp-generate feature
21
- ```
22
-
23
- Este comando iniciará un asistente interactivo que te guiará paso a paso:
24
-
25
- ```
26
- 🚀 Generador de Features MCP
27
-
28
- ? Nombre de la feature (ej: giftCards, users, products): products
29
- ? Descripción de la feature: Manage products catalog
30
- ? ¿Necesita un service (lógica de negocio)? Yes
31
- ? Tipo de service: API Client (llamadas HTTP)
32
- ? Selecciona los tools a generar:
33
- ◉ list (listar items)
34
- ◉ get (obtener un item)
35
- ◉ create (crear item)
36
- ◉ update (actualizar item)
37
- ◯ delete (eliminar item)
38
- ◉ search (buscar items)
39
- ? Nombre de la entidad (singular, ej: giftCard, user): product
40
- ? ¿Auto-registrar en main.ts? Yes
41
- ```
42
-
43
- ### Estructura Generada
44
-
45
- El CLI generará la siguiente estructura:
46
-
47
- ```
48
- src/features/products/
49
- ├── products.feature.ts # Router con tools MCP
50
- ├── products.service.ts # Lógica de negocio
51
- └── products.validation.ts # Schemas de validación Zod
52
- ```
53
-
54
- #### Ejemplo: `products.feature.ts`
55
-
56
- ```typescript
57
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
58
- import { z } from 'zod';
59
- import Products from "./products.service.js";
60
-
61
- export function registerProductsTools(server: McpServer, productsInstance: Products) {
62
-
63
- server.registerTool(
64
- 'list_products',
65
- {
66
- description: 'List all products',
67
- inputSchema: {
68
- page: z.number().optional().describe('Page number (number, optional)'),
69
- per_page: z.number().optional().describe('Results per page (number, optional)'),
70
- filters: z.any().optional().describe('Additional filters (object, optional)'),
71
- }
72
- },
73
- async (args, req: any) => {
74
- try {
75
- const result = await productsInstance.listProducts(args, req.requestInfo?.headers?.authorization);
76
- return {
77
- content: [{
78
- type: 'text',
79
- text: JSON.stringify(result, null, 2)
80
- }]
81
- };
82
- } catch (error: any) {
83
- return {
84
- content: [{
85
- type: 'text',
86
- text: `Error: ${error.message}`
87
- }],
88
- isError: true
89
- };
90
- }
91
- }
92
- );
93
-
94
- server.registerTool(
95
- 'get_product',
96
- {
97
- description: 'Get a specific product by ID',
98
- inputSchema: {
99
- id: z.string().describe('product ID (string, required)'),
100
- }
101
- },
102
- async (args, req: any) => {
103
- try {
104
- const result = await productsInstance.getProduct(args, req.requestInfo?.headers?.authorization);
105
- return {
106
- content: [{
107
- type: 'text',
108
- text: JSON.stringify(result, null, 2)
109
- }]
110
- };
111
- } catch (error: any) {
112
- return {
113
- content: [{
114
- type: 'text',
115
- text: `Error: ${error.message}`
116
- }],
117
- isError: true
118
- };
119
- }
120
- }
121
- );
122
-
123
- // ... más tools
124
- }
125
- ```
126
-
127
- #### Ejemplo: `products.service.ts`
128
-
129
- ```typescript
130
- import ApiClient from '#services/api';
131
- import { sanitize } from '../../utils/sanitize.js';
132
-
133
- // --- Interfaces para Opciones de Métodos ---
134
-
135
- export interface ListProductOptions { page?: number; per_page?: number; filters?: any; }
136
- export interface GetProductOptions { id: string; }
137
- export interface CreateProductOptions { data: any; }
138
- export interface UpdateProductOptions { id: string; data: any; }
139
- export interface SearchProductOptions { query: string; filters?: any; }
140
-
141
- class Products extends ApiClient {
142
-
143
- async listProducts(options: ListProductOptions, token?: string) {
144
- const { page = 1, per_page = 10, ...params } = options;
145
- return this.get('products', { page, per_page, ...params }, token || '');
146
- }
147
-
148
- async getProduct(options: GetProductOptions, token?: string) {
149
- const { id } = options;
150
- return this.get(`products/${id}`, {}, token || '');
151
- }
152
-
153
- async createProduct(options: CreateProductOptions, token?: string) {
154
- const { data } = options;
155
- // Sanitize data aquí si es necesario
156
- return this.post('products', data, token || '');
157
- }
158
-
159
- async updateProduct(options: UpdateProductOptions, token?: string) {
160
- const { id, data } = options;
161
- return this.patch(`products/${id}`, data, token || '');
162
- }
163
-
164
- async searchProducts(options: SearchProductOptions, token?: string) {
165
- const { query, filters } = options;
166
- return this.get('search/products', { q: sanitize(query), ...filters }, token || '');
167
- }
168
- }
169
-
170
- export default Products;
171
- ```
172
-
173
- #### Ejemplo: `products.validation.ts`
174
-
175
- ```typescript
176
- import { z } from 'zod';
177
-
178
- // --- Campos Comunes Reutilizables ---
179
-
180
- const id = z.string().min(1, "ID is required.");
181
- const name = z.string().min(1, "Name is required.");
182
- const description = z.string().optional();
183
- const page = z.number().int().positive().optional();
184
- const perPage = z.number().int().positive().optional();
185
- const query = z.string().min(1, "Query is required.");
186
-
187
- // --- Schemas de Validación por Tool ---
188
-
189
- export const listProductSchema = z.object({
190
- page,
191
- perPage,
192
- // TODO: Agregar filtros específicos
193
- });
194
-
195
- export const getProductSchema = z.object({
196
- id,
197
- });
198
-
199
- export const createProductSchema = z.object({
200
- name,
201
- description,
202
- // TODO: Agregar campos específicos del product
203
- });
204
-
205
- export const updateProductSchema = z.object({
206
- id,
207
- name: z.string().optional(),
208
- description,
209
- // TODO: Agregar campos actualizables
210
- });
211
-
212
- export const searchProductSchema = z.object({
213
- query,
214
- page,
215
- perPage,
216
- });
217
- ```
218
-
219
- ### Auto-registro en main.ts
220
-
221
- Si seleccionaste "Yes" en auto-registrar, el CLI actualizará automáticamente tu `main.ts`:
222
-
223
- ```typescript
224
- // Services imports
225
- import Products from './features/products/products.service.js';
226
-
227
- // Feature imports
228
- import { registerProductsTools } from './features/products/products.feature.js';
229
-
230
- class ApprecioMcpServer extends McpBaseServer {
231
- private productsInstance = new Products();
232
-
233
- protected async registerFeatures(): Promise<void> {
234
- registerProductsTools(this.mcpServer, this.productsInstance);
235
-
236
- logger.info('All Apprecio features registered successfully');
237
- }
238
- }
239
- ```
240
-
241
- ## 📋 Comandos Disponibles
242
-
243
- ### `mcp-generate feature`
244
-
245
- Genera una feature completa con router, service y validation.
246
-
247
- **Opciones interactivas:**
248
- - Nombre de la feature
249
- - Descripción
250
- - Tipo de service (API, Database, Mixed, Custom)
251
- - Tools a generar (list, get, create, update, delete, search)
252
- - Nombre de la entidad
253
- - Auto-registro en main.ts
254
-
255
- ### `mcp-generate tool`
256
-
257
- Agrega un tool adicional a una feature existente.
258
-
259
- ```bash
260
- mcp-generate tool
261
- ```
262
-
263
- **Opciones interactivas:**
264
- - Seleccionar feature existente
265
- - Nombre del tool
266
- - Descripción
267
- - ¿Agregar método al service?
268
-
269
- ## 🎨 Tipos de Service
270
-
271
- ### API Client
272
- Genera un service que hereda de `ApiClient` para hacer llamadas HTTP.
273
-
274
- ```typescript
275
- class Products extends ApiClient {
276
- async listProducts(options, token) {
277
- return this.get('products', { page: 1 }, token);
278
- }
279
- }
280
- ```
281
-
282
- ### Database (MongoDB)
283
- Genera un service para operaciones directas con MongoDB.
284
-
285
- ```typescript
286
- class Products {
287
- constructor(db: MongoDBConnector) {
288
- this.db = db;
289
- }
290
-
291
- async listProducts(options) {
292
- const collection = this.db.getCollection('products');
293
- return collection.find({}).toArray();
294
- }
295
- }
296
- ```
297
-
298
- ### Mixed (API + Database)
299
- Combina ambos tipos para casos híbridos.
300
-
301
- ### Custom
302
- Genera un service vacío para implementación personalizada.
303
-
304
- ## 🔧 Personalización
305
-
306
- ### Modificar Templates
307
-
308
- Los generadores están en:
309
- ```
310
- cli/generators/
311
- ├── feature-generator.ts
312
- ├── service-generator.ts
313
- ├── router-generator.ts
314
- └── validation-generator.ts
315
- ```
316
-
317
- Puedes modificarlos para ajustar la estructura generada.
318
-
319
- ### Agregar Nuevos Tool Types
320
-
321
- Edita `router-generator.ts` y agrega tu tipo custom:
322
-
323
- ```typescript
324
- const toolMap = {
325
- 'list': '...',
326
- 'get': '...',
327
- 'approve': 'Approve an item', // ← Nuevo
328
- };
329
- ```
330
-
331
- ## 📝 Ejemplo Completo de Uso
332
-
333
- ```bash
334
- # 1. Crear proyecto MCP
335
- mkdir my-mcp-server
336
- cd my-mcp-server
337
- npm init -y
338
-
339
- # 2. Instalar dependencias
340
- npm install @apprecio/mcp-base
341
- npm install -D @apprecio/mcp-cli
342
-
343
- # 3. Generar primera feature
344
- npx mcp-generate feature
345
- # Nombre: users
346
- # Tools: list, get, create, update
347
- # Service type: Database
348
-
349
- # 4. Generar segunda feature
350
- npx mcp-generate feature
351
- # Nombre: products
352
- # Tools: list, get, search
353
- # Service type: API Client
354
-
355
- # 5. Agregar tool custom a users
356
- npx mcp-generate tool
357
- # Feature: users
358
- # Tool: verify_email
359
- # Descripción: Send verification email
360
-
361
- # 6. Ejecutar servidor
362
- npm run dev
363
- ```
364
-
365
- ## 🎯 Best Practices
366
-
367
- 1. **Nombres descriptivos**: Usa nombres claros en camelCase
368
- 2. **Un feature por dominio**: No mezcles conceptos diferentes
369
- 3. **Valida todo**: Las validaciones Zod son tu primera línea de defensa
370
- 4. **Sanitiza inputs**: Siempre sanitiza datos de usuario
371
- 5. **Maneja errores**: Cada tool debe tener try/catch
372
-
373
- ## 🆘 Troubleshooting
374
-
375
- ### Error: "Feature already exists"
376
-
377
- El directorio ya existe. Elimínalo o usa otro nombre.
378
-
379
- ```bash
380
- rm -rf src/features/myfeature
381
- ```
382
-
383
- ### Error: "Cannot find main.ts"
384
-
385
- Asegúrate de ejecutar el CLI desde la raíz del proyecto.
386
-
387
- ```bash
388
- cd /ruta/a/tu/proyecto
389
- npx mcp-generate feature
390
- ```
391
-
392
- ### Los imports no funcionan
393
-
394
- Verifica que tengas los path aliases configurados en `tsconfig.json`:
395
-
396
- ```json
397
- {
398
- "compilerOptions": {
399
- "baseUrl": ".",
400
- "paths": {
401
- "#services/*": ["./src/services/*"],
402
- "#features/*": ["./src/features/*"]
403
- }
404
- }
405
- }
406
- ```
407
-
408
- ## 📦 Publicación
409
-
410
- Para publicar el CLI en npm privado:
411
-
412
- ```bash
413
- cd cli
414
- npm run build
415
- npm publish
416
- ```
417
-
418
- ## 🤝 Contribuir
419
-
420
- 1. Fork el repositorio
421
- 2. Crea una rama: `git checkout -b feature/nueva-funcionalidad`
422
- 3. Commit: `git commit -am 'Add nueva funcionalidad'`
423
- 4. Push: `git push origin feature/nueva-funcionalidad`
424
- 5. Pull Request
425
-
426
- ## 📄 Licencia
427
-
428
- MIT
429
-
430
- ---
431
-
432
- **Desarrollado por el equipo de Apprecio** 🚀