mcp-state-machine-test-framework 1.0.7 → 1.0.8

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 (3) hide show
  1. package/README.md +7 -4
  2. package/index.js +91 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -27,10 +27,13 @@ Or run it directly via MCP in your configuration:
27
27
  ```
28
28
 
29
29
  ## 🏁 Basic Workflow
30
- 1. **Initialize Maps**: Create a state map in `/maps`.
31
- 2. **Define Test Cases**: Create modular steps in `/test_cases`.
32
- 3. **Assemble Suite**: Group cases in `/suites`.
33
- 4. **Execute**: Call the `execute_suite` tool from your AI agent or orchestrator.
30
+ 1. **Initialize Project**: Run `init_project` to setup folders and templates.
31
+ 2. **Guided Design**: Tell the agent to "Create a new node". The agent will ask for the mandatory fields (Map, Node Name, Transitions) before executing.
32
+ 3. **Automatic Visualization**: Every map change regenerates a Mermaid diagram in `/maps`.
33
+ 4. **Execution**: Run `execute_suite` to launch the automated flow.
34
+
35
+ ## 🤖 AI Agent Interaction (Guided Design)
36
+ This framework enforces a "Strict Contract" via MCP tools. Agents are instructed NOT to edit JSON files directly, but to use the management tools (`upsert_node`, `save_test_case`, `save_suite`) which provide validation and structure.
34
37
 
35
38
  ## 📚 Technical Documentation
36
39
  - [🧠 Technical Protocol & History](./SMS_Protocol.md)
package/index.js CHANGED
@@ -326,18 +326,20 @@ export class MaquinaDeEstados {
326
326
 
327
327
  const maquina = new MaquinaDeEstados();
328
328
  const server = new McpServer({ name: "demo-state-machine", version: "11.1.0" });
329
- server.tool("execute_suite", "Ejecución de Suite Completa", { name: z.string() }, async ({ name }) => {
329
+ server.tool("execute_suite", "Ejecución de Suite Completa", {
330
+ name: z.string().describe("Nombre de la suite a ejecutar (ej: 'Suite_Login'). Buscará el archivo en /suites.")
331
+ }, async ({ name }) => {
330
332
  await maquina.cargar();
331
333
  const dir = await maquina.ejecutarSuite(name);
332
334
  return { content: [{ type: "text", text: `Reporte: ${dir}` }] };
333
335
  });
334
336
 
335
- server.tool("upsert_node", "Añadir o actualizar un nodo en el mapa de estados", {
336
- mapName: z.string(),
337
- nodeName: z.string(),
337
+ server.tool("upsert_node", "Añadir o actualizar un nodo en el mapa de estados.", {
338
+ mapName: z.string().describe("Nombre del archivo del mapa (ej: 'home_map.json'). El servidor lo buscará en la carpeta /maps."),
339
+ nodeName: z.string().describe("Identificador único del nodo (ej: 'LOGIN_PAGE', 'HOME')."),
338
340
  nodeData: z.object({
339
- transiciones: z.any().optional()
340
- }).passthrough()
341
+ transiciones: z.any().optional().describe("Objeto que define las salidas del nodo. Ej: { 'IR_A_LOGIN': { 'destino': 'LOGIN_PAGE', 'accion': 'mcp:wdio-mcp/click_element ...' } }")
342
+ }).passthrough().describe("Datos completos del nodo, incluyendo transiciones y metadatos.")
341
343
  }, async ({ mapName, nodeName, nodeData }) => {
342
344
  const mapPath = path.join(__dirname, 'maps', mapName);
343
345
  let map = { nodos: {} };
@@ -403,12 +405,12 @@ server.tool("inspect_framework", "Inspeccionar integridad y listar entidades del
403
405
  return { content: [{ type: "text", text: JSON.stringify(entities, null, 2) }] };
404
406
  });
405
407
 
406
- server.tool("save_test_case", "Crear o actualizar un caso de prueba", {
407
- name: z.string(),
408
+ server.tool("save_test_case", "Crear o actualizar un caso de prueba.", {
409
+ name: z.string().describe("Nombre identificador del test (ej: 'TC_Login_Exitoso')."),
408
410
  steps: z.array(z.object({
409
- name: z.string().optional(),
410
- action: z.string().optional()
411
- }).passthrough())
411
+ name: z.string().optional().describe("Descripción amigable del paso (ej: 'Ingresar Usuario')."),
412
+ action: z.string().optional().describe("Acción a realizar. Puede ser un nombre de transición del mapa o un comando mcp:wdio-mcp/...")
413
+ }).passthrough()).describe("Lista ordenada de pasos lógicos a ejecutar.")
412
414
  }, async ({ name, steps }) => {
413
415
  const fileName = name.endsWith('.json') ? name : `${name}.json`;
414
416
  const filePath = path.join(__dirname, 'test_cases', fileName);
@@ -418,12 +420,12 @@ server.tool("save_test_case", "Crear o actualizar un caso de prueba", {
418
420
  return { content: [{ type: "text", text: `Caso de prueba '${name}' guardado correctamente.` }] };
419
421
  });
420
422
 
421
- server.tool("save_suite", "Crear o actualizar una suite de pruebas", {
422
- name: z.string(),
423
- state_map: z.string(),
424
- tests: z.array(z.string()),
425
- beforeSuite: z.array(z.string()).optional().default([]),
426
- afterSuite: z.array(z.string()).optional().default([])
423
+ server.tool("save_suite", "Crear o actualizar una suite de pruebas.", {
424
+ name: z.string().describe("Nombre de la suite (ej: 'Suite_E2E_Sanity')."),
425
+ state_map: z.string().describe("Archivo del mapa de estados a usar (ej: 'mob_perfecto_map.json')."),
426
+ tests: z.array(z.string()).describe("Lista de nombres de casos de prueba a incluir en la suite."),
427
+ beforeSuite: z.array(z.string()).optional().default([]).describe("Acciones globales antes de la suite (ej: iniciar sesión)."),
428
+ afterSuite: z.array(z.string()).optional().default([]).describe("Acciones globales tras la suite (ej: cerrar sesión).")
427
429
  }, async ({ name, state_map, tests, beforeSuite, afterSuite }) => {
428
430
  const fileName = name.endsWith('.json') ? name : `${name}.json`;
429
431
  const filePath = path.join(__dirname, 'suites', fileName);
@@ -433,5 +435,76 @@ server.tool("save_suite", "Crear o actualizar una suite de pruebas", {
433
435
  return { content: [{ type: "text", text: `Suite '${name}' guardada correctamente.` }] };
434
436
  });
435
437
 
436
- async function main() { await maquina.cargar(); const transport = new StdioServerTransport(); await server.connect(transport); }
438
+ server.tool("init_project", "Inicializa el proyecto con carpetas y archivos de plantilla", {
439
+ force: z.optional(z.boolean()).default(false)
440
+ }, async ({ force }) => {
441
+ await ensureDirectories();
442
+
443
+ const templates = {
444
+ 'maps/template_map.json': {
445
+ nodos: {
446
+ HOME: {
447
+ transiciones: {
448
+ IR_A_LOGIN: { destino: "LOGIN", accion: "mcp:wdio-mcp/click_element { \"selector\": \"~Login\" }" }
449
+ }
450
+ },
451
+ LOGIN: {
452
+ transiciones: {
453
+ VOLVER: { destino: "HOME", accion: "mcp:wdio-mcp/click_element { \"selector\": \"~Back\" }" }
454
+ }
455
+ }
456
+ }
457
+ },
458
+ 'test_cases/template_case.json': {
459
+ name: "Template Case",
460
+ steps: [
461
+ { name: "Ir a Login", action: "IR_A_LOGIN" },
462
+ { name: "Verificar Pantalla", action: "mcp:wdio-mcp/get_screenshot {}" }
463
+ ]
464
+ },
465
+ 'suites/template_suite.json': {
466
+ name: "Template Suite",
467
+ state_map: "template_map.json",
468
+ tests: ["template_case"],
469
+ beforeSuite: ["mcp:wdio-mcp/start_session { \"platform\": \"android\", \"provider\": \"perfecto\" }"],
470
+ afterSuite: ["mcp:wdio-mcp/close_session {}"]
471
+ }
472
+ };
473
+
474
+ for (const [relPath, content] of Object.entries(templates)) {
475
+ const fullPath = path.join(__dirname, relPath);
476
+ try {
477
+ if (!force) {
478
+ try {
479
+ await fs.access(fullPath);
480
+ continue; // Skip if exists
481
+ } catch (e) {}
482
+ }
483
+ await fs.writeFile(fullPath, JSON.stringify(content, null, 2));
484
+ } catch (e) {
485
+ process.stderr.write(`[WARN] Error creando template ${relPath}: ${e.message}\n`);
486
+ }
487
+ }
488
+
489
+ return { content: [{ type: "text", text: "✅ Proyecto inicializado. Se han creado las carpetas y los archivos de plantilla (templates) para guiar al agente." }] };
490
+ });
491
+
492
+ async function ensureDirectories() {
493
+ const dirs = ['maps', 'suites', 'test_cases', 'reports', 'data'];
494
+ for (const dir of dirs) {
495
+ const dirPath = path.join(__dirname, dir);
496
+ try {
497
+ await fs.mkdir(dirPath, { recursive: true });
498
+ } catch (e) {
499
+ process.stderr.write(`[WARN] Error creando directorio ${dir}: ${e.message}\n`);
500
+ }
501
+ }
502
+ }
503
+
504
+ async function main() {
505
+ await ensureDirectories();
506
+ await maquina.cargar();
507
+ const transport = new StdioServerTransport();
508
+ await server.connect(transport);
509
+ }
437
510
  main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-state-machine-test-framework",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "High-fidelity State Machine MCP Server for autonomous E2E testing orchestration.",
5
5
  "main": "index.js",
6
6
  "type": "module",