apprecio-mcp-base 1.1.0 → 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.
- package/cli/dist/generate-feature.js +143 -55
- package/package.json +1 -1
- package/cli/README.md +0 -432
|
@@ -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.
|
|
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
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** 🚀
|