mcp-state-machine-test-framework 1.0.0

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.
@@ -0,0 +1,49 @@
1
+ # ๐ŸŒ API Testing (REST / Services)
2
+
3
+ SMS allows for native integration and sanity testing of APIs.
4
+
5
+ ## ๐Ÿš€ Strategy 1: System Commands (`sh:curl`)
6
+ For quick and direct tests, you can use `curl`.
7
+
8
+ ```json
9
+ {
10
+ "name": "TC_API_Status_Check",
11
+ "steps": [
12
+ {
13
+ "name": "Validate Endpoint",
14
+ "actions": [
15
+ "sh:curl -X GET https://api.example.com/status -I"
16
+ ]
17
+ }
18
+ ]
19
+ }
20
+ ```
21
+
22
+ ## ๐Ÿง  Strategy 2: Data-Driven API Testing
23
+ You can use a CSV to send multiple payloads to an API.
24
+
25
+ ### CSV (`payloads.csv`):
26
+ ```csv
27
+ id,name,status
28
+ 1,"Test 1",201
29
+ 2,"Test 2",400
30
+ ```
31
+
32
+ ### Test Case:
33
+ ```json
34
+ "actions": [
35
+ "sh:curl -X POST https://api.com/users -d '{\"id\": {{id}}, \"name\": \"{{name}}\"}'"
36
+ ]
37
+ ```
38
+
39
+ ## ๐Ÿ› ๏ธ Strategy 3: MCP API Servers
40
+ For more complex validation (JSON Schema, dynamic Headers), it is recommended to connect a specialized HTTP MCP server.
41
+
42
+ - **Advantage**: The report will show the response JSON formatted and elegant.
43
+ - **Conceptual Example**: `mcp:http-client/request { \"method\": \"GET\", \"url\": \"...\" }`
44
+
45
+ ## ๐Ÿ”— Hybrid Testing (Real E2E)
46
+ The greatest power of SMS is mixing layers:
47
+ 1. **Step 1 (API)**: Create a user via `sh:curl`.
48
+ 2. **Step 2 (Web)**: Log in with that user in the browser via `mcp:wdio-mcp/navigate`.
49
+ 3. **Step 3 (DB)**: Validate the record in the database.
@@ -0,0 +1,39 @@
1
+ # ๐Ÿ›๏ธ SMS Universal Architecture V12.3 (Multi-Map & Integrity)
2
+
3
+ The **State Machine Simulator (SMS)** framework has evolved into a **Multi-Project Orchestration** model, introducing isolated data universes and automated integrity checks.
4
+
5
+ ## ๐Ÿ“ก The "Multi-Map" Concept (V12.3)
6
+ To support parallel execution and multiple applications, SMS now uses isolated state map files:
7
+ - **Isolation**: Each project (Web, Mobile, API) has its own file in the `maps/` directory.
8
+ - **Pre-flight Integrity Check**: Before execution, the engine validates that the Suite and the Map are compatible, preventing cross-environment errors.
9
+ Unlike traditional frameworks that import libraries (like Selenium or WDIO) directly into the code, SMS acts as a **Message Bus**.
10
+ - **Transport**: Communicates with external servers (MCP Servers) via `stdio`.
11
+ - **Agnosticism**: The engine doesn't know what a "browser" or a "selector" is; it only knows how to send `JSON-RPC` commands and receive responses.
12
+ - **Session Persistence**: Unlike standard MCP clients, the engine keeps connections open throughout the entire Suite execution, allowing multiple Test Cases to share the same browser/app state.
13
+
14
+ ## ๐Ÿ› ๏ธ Core Components
15
+
16
+ ### 1. The Orchestrator (`index.js`)
17
+ The brain of the system. Its functions are:
18
+ - **Client Management**: Starts and maintains persistent MCP server processes defined in `mcp_config.json`.
19
+ - **Iteration Engine (Data-Driven)**: Processes data arrays or external CSV/JSON files, injecting variables in real-time.
20
+ - **Interpolator**: Replaces `{{variable}}` syntax before sending commands to the server.
21
+ - **Evidence Generator**: Performs a deep scan of responses to extract Base64 images and inject them into the report.
22
+
23
+ ### 2. Data Layer (Universal Data Bridge)
24
+ The engine supports three levels of data:
25
+ - **Hardcoded**: Literal actions.
26
+ - **Internal Data**: Arrays defined within the Test Case.
27
+ - **External Data**: Paths to external `.csv` or `.json` files.
28
+
29
+ ### 3. Hook System (Lifecycle)
30
+ The framework manages the lifecycle at hierarchical levels:
31
+ 1. **Suite Level**: `beforeSuite` (Global Setup) and `afterSuite` (Global Teardown).
32
+ 2. **Case Level**: `beforeCase` and `afterCase`.
33
+ 3. **Step Level**: `afterStep` (Ideal for automatic screenshots).
34
+
35
+ ## ๐Ÿ“Š Reporting Dashboard (Glassmorphism)
36
+ The generated report is a **self-contained** HTML file:
37
+ - **Portable**: No external files needed (images are Base64).
38
+ - **Encapsulated**: Technical JSON logs are hidden under collapsible menus to prioritize action readability and visual evidence.
39
+ - **Premium Design**: Modern aesthetics with 'Outfit' fonts and a neon/dark color palette.
@@ -0,0 +1,47 @@
1
+ # ๐Ÿ“Š Data-Driven Master Guide
2
+
3
+ The SMS V11.2 engine allows for massive test execution by decoupling data from logic.
4
+
5
+ ## 1. Internal Data (JSON)
6
+ Ideal for quick tests. Define the `data` array within the Test Case `.json` file.
7
+
8
+ ```json
9
+ "data": [
10
+ { "user": "admin", "pass": "123" },
11
+ { "user": "guest", "pass": "abc" }
12
+ ]
13
+ ```
14
+
15
+ ## 2. External Data (CSV)
16
+ The professional and scalable approach. Create a `.csv` file and link to it from the Test Case.
17
+
18
+ ### The CSV File (`users.csv`):
19
+ The first row **MUST** be the header.
20
+ ```csv
21
+ user,pass,comment
22
+ admin,secret,"Admin test"
23
+ tester,test,"Base user test"
24
+ ```
25
+
26
+ ### The Test Case:
27
+ ```json
28
+ {
29
+ "name": "TC_Massive_Login",
30
+ "data": "users.csv",
31
+ "steps": [
32
+ {
33
+ "name": "Login",
34
+ "actions": [
35
+ "mcp:wdio-mcp/set_value { \"selector\": \"#u\", \"value\": \"{{user}}\" }",
36
+ "mcp:wdio-mcp/set_value { \"selector\": \"#p\", \"value\": \"{{pass}}\" }"
37
+ ]
38
+ }
39
+ ]
40
+ }
41
+ ```
42
+
43
+ ## ๐Ÿง  Golden Rules for Data-Driven
44
+ 1. **Interpolation**: Always use `{{column}}` to reference data.
45
+ 2. **Iteration**: The engine will execute **ALL** test case steps once for each row in the CSV.
46
+ 3. **Persistence**: If you start the session in `beforeSuite`, all iterations will occur in the same browser window (much faster).
47
+ 4. **Cleanup**: The engine automatically strips extra quotes from CSVs to prevent errors in command JSON strings.
@@ -0,0 +1,36 @@
1
+ # ๐Ÿ”ฌ Master Test Plan - SMS Universal V12.3
2
+
3
+ This plan verifies the full spectrum of the MCP framework's capabilities, ensuring that all V12.3 architectural changes are stable and production-ready.
4
+
5
+ ## ๐Ÿ Test Matrix
6
+
7
+ | Feature | Platform | Test Strategy | Goal |
8
+ | :--- | :--- | :--- | :--- |
9
+ | **Pure MCP Orchestration** | Universal | Persistent Session Test | Verify that MCP connections stay open across multiple Test Cases. |
10
+ | **Multi-Platform (Web)** | Web (Chrome) | Navigation & Interactivity | Validate standard web automation via `wdio-mcp`. |
11
+ | **Multi-Platform (Mobile)** | Android (Perfecto) | Cloud Session & Cloud APK | Validate cloud device allocation and mobile interactions. |
12
+ | **Hybrid Orchestration** | Web + System | `sh:` and `mcp:` mix | Verify that system commands (API/Files) can coexist with UI actions. |
13
+ | **Data-Driven (CSV)** | Universal | Variable Interpolation | Ensure `{{variable}}` mapping works from external files. |
14
+ | **Multi-Map Support** | Universal | Isolated map switching | Verify that different suites use different files in `/maps`. |
15
+ | **Pre-flight Integrity** | Universal | Negative & Positive Validation | Ensure the engine blocks execution if the map is missing or invalid. |
16
+ | **Symmetrical Hooks** | Universal | Step-level lifecycle | Validate that `beforeStep` and `afterStep` execute correctly. |
17
+
18
+ ---
19
+
20
+ ## ๐Ÿงช Master Smoke Suite (Hybrid Scenario)
21
+
22
+ To validate everything in a single "Stress Run", we will execute a hybrid suite:
23
+ 1. **Pre-condition**: Integrity check validates `maps/web_map.json`.
24
+ 2. **Step 1 (API)**: Use `sh:curl` to verify an endpoint.
25
+ 3. **Step 2 (Web)**: Open a browser and use a CSV to perform a search.
26
+ 4. **Step 3 (Evidence)**: Capture screenshots after every step via `afterStep`.
27
+ 5. **Post-condition**: Close session.
28
+
29
+ ## ๐Ÿ“Š Acceptance Criteria
30
+ - 100% of the integrity checks must pass for valid maps.
31
+ - Visual evidence (Base64) must be present for all UI steps in the final report.
32
+ - The report must show the correct "Iteration X" label for Data-Driven tests.
33
+ - System logs must show the "V12.3 Integrity Check" header.
34
+
35
+ ---
36
+ *Verified by SMS Orchestrator V12.3*
@@ -0,0 +1,34 @@
1
+ # ๐Ÿ“ฑ Mobile Automation (Appium)
2
+
3
+ The SMS engine supports testing on real devices and emulators using the `wdio-mcp` server configured for Appium.
4
+
5
+ ## ๐Ÿ› ๏ธ Mobile Session Setup
6
+ Unlike Web, Mobile requires a robust set of **Capabilities**. These are defined in the `beforeSuite` hook.
7
+
8
+ ### Example: Android Emulator
9
+ ```json
10
+ "beforeSuite": [
11
+ "mcp:wdio-mcp/start_session {
12
+ \"platform\": \"android\",
13
+ \"deviceName\": \"emulator-5554\",
14
+ \"automationName\": \"UiAutomator2\",
15
+ \"app\": \"C:/path/my_app.apk\",
16
+ \"capabilities\": {
17
+ \"noReset\": true,
18
+ \"autoGrantPermissions\": true
19
+ }
20
+ }"
21
+ ]
22
+ ```
23
+
24
+ ## ๐Ÿ–๏ธ Mobile-Specific Actions
25
+ When the session platform is `android` or `ios`, you can use exclusive tools:
26
+
27
+ - **Tap**: `mcp:wdio-mcp/tap_element { \"selector\": \"~login_button\" }`
28
+ - **Swipe**: `mcp:wdio-mcp/swipe { \"direction\": \"up\", \"percent\": 0.8 }`
29
+ - **Hide Keyboard**: `mcp:wdio-mcp/hide_keyboard {}`
30
+
31
+ ## ๐Ÿงช Mobile Testing Strategy
32
+ 1. **Accessibility IDs**: Always use `~ID` in your selectors for maximum stability.
33
+ 2. **Context Switching**: If your app is hybrid (Webview), use `mcp:wdio-mcp/switch_context { \"context\": \"WEBVIEW_...\" }`.
34
+ 3. **Evidence Capture**: The `mcp:wdio-mcp/get_screenshot {}` command works the same as in Web and is vital for the report.
@@ -0,0 +1,45 @@
1
+ # ๐Ÿ“ฑ Perfecto Mobile Cloud Setup
2
+
3
+ This guide details how to connect the SMS Framework to the **Perfecto Mobile** cloud using the modular `wdio-mcp` provider.
4
+
5
+ ## ๐Ÿ”— Connection Configuration
6
+ The framework is optimized to use the `perfecto` provider directly, which simplifies the configuration.
7
+
8
+ ### Recommended Suite Configuration
9
+
10
+ ```json
11
+ {
12
+ "name": "Suite_Mobile_Perfecto",
13
+ "state_map": "your_map.json",
14
+ "beforeSuite": [
15
+ "mcp:wdio-mcp/start_session {
16
+ \"platform\": \"android\",
17
+ \"provider\": \"perfecto\",
18
+ \"app\": \"PRIVATE:Advantage+demo+3.3.apk\",
19
+ \"automationName\": \"UiAutomator2\",
20
+ \"capabilities\": {
21
+ \"appium:useVirtualDevice\": true,
22
+ \"appium:waitForAvailableLicense\": true,
23
+ \"perfecto:options\": {
24
+ \"securityToken\": \"YOUR_TOKEN_HERE\"
25
+ }
26
+ }
27
+ }"
28
+ ],
29
+ "afterSuite": [
30
+ "mcp:wdio-mcp/close_session {}"
31
+ ]
32
+ }
33
+ ```
34
+
35
+ ## ๐Ÿ›ก๏ธ Security & Privacy
36
+ - **Automatic Masking**: The SMS server automatically detects the `securityToken` in your suite files and masks it in the HTML/JSON reports as `********`.
37
+ - **Environment Variables**: For production environments, it is highly recommended to set the `PERFECTO_TOKEN` as an environment variable instead of hardcoding it in the JSON file.
38
+
39
+ ## ๐Ÿง  Best Practices
40
+ 1. **Selector Stability**: Use **Accessibility IDs** (`~User Name`) whenever possible. They are the most stable across cloud latency.
41
+ 2. **Pre-flight Checks**: The server performs an integrity check on your state map before execution to catch path errors early.
42
+ 3. **Session Management**: Always include `close_session` in your `afterSuite` to free up cloud licenses immediately.
43
+
44
+ ---
45
+ *V1.1 - Updated for Pure-MCP Standard*
@@ -0,0 +1,78 @@
1
+ # ๐Ÿ“– Configuration and Command Reference
2
+
3
+ This guide details how to write Test Cases, Suites, and Commands for the SMS engine.
4
+
5
+ ## ๐Ÿ“‹ Test Case Structure (`/test_cases`)
6
+
7
+ A test case is a JSON file with the following structure:
8
+
9
+ ```json
10
+ {
11
+ "name": "TC_Unique_Name",
12
+ "data": "path/to/file.csv", // Optional: For Data-Driven
13
+ "steps": [
14
+ {
15
+ "name": "Step Name",
16
+ "actions": [
17
+ "sh:echo 'System Command'",
18
+ "mcp:server/tool { \"arg\": \"value\" }"
19
+ ]
20
+ }
21
+ ]
22
+ }
23
+ ```
24
+
25
+ ## ๐Ÿ“‹ Suite Structure (`/suites`)
26
+
27
+ The suite groups cases and defines the global lifecycle:
28
+
29
+ ```json
30
+ {
31
+ "name": "Suite_Name",
32
+ "state_map": "project_map.json", // Optional: Separate state machine file
33
+ "tests": ["TC_1", "TC_2"],
34
+ "beforeSuite": ["mcp:wdio-mcp/start_session {...}"],
35
+ "afterSuite": ["mcp:wdio-mcp/close_session {}"],
36
+ "beforeCase": [],
37
+ "afterCase": [],
38
+ "beforeStep": [],
39
+ "afterStep": ["mcp:wdio-mcp/get_screenshot {}"]
40
+ }
41
+ ```
42
+
43
+ ## โšก Action Syntax
44
+
45
+ ### 1. System Commands (`sh:`)
46
+ Executes commands directly on the host's terminal.
47
+ - Example: `sh:npm run build`
48
+ - Example: `sh:echo 'Hello {{user}}'`
49
+
50
+ ### 2. MCP Commands (`mcp:`)
51
+ Calls a tool from a configured MCP server.
52
+ - Syntax: `mcp:server-name/tool-name { "json": "args" }`
53
+ - Example: `mcp:wdio-mcp/navigate { "url": "https://google.com" }`
54
+
55
+ ## ๐Ÿ“‚ Project Structure Standard
56
+ - `/test_cases`: Test logic (Steps).
57
+ - `/suites`: Execution flow and lifecycle hooks.
58
+ - `/maps`: **[NEW]** Isolated state machine database files (.json).
59
+ - `/reports`: Self-contained visual execution results.
60
+
61
+ ## ๐Ÿ”— MCP Servers (`mcp_config.json`)
62
+
63
+ Configure your persistent servers here:
64
+
65
+ ```json
66
+ {
67
+ "mcpServers": {
68
+ "wdio-mcp": {
69
+ "command": "node",
70
+ "args": ["C:/path/to/server/index.js"]
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## ๐Ÿ”„ Variables and Placeholders
77
+ The engine automatically replaces any string enclosed in `{{ }}` using the column names from your CSV or keys from your data JSON.
78
+ - Example: `{{search_query}}`, `{{password}}`.
@@ -0,0 +1,73 @@
1
+ # ๐Ÿค– AI Environment Configuration Guide
2
+
3
+ The SMS engine uses the MCP (Model Context Protocol) standard. Here we explain how to connect your servers (`index.js` and `wdio-mcp`) to different AI tools.
4
+
5
+ ## ๐Ÿ“ฆ Required Dependencies
6
+ To ensure the engine and servers run smoothly, make sure you have installed:
7
+ - **Node.js**: v18 or higher.
8
+ - **MCP SDK**: `@modelcontextprotocol/sdk`.
9
+ - **Zod**: For schema validation.
10
+
11
+ Quick installation:
12
+ ```bash
13
+ npm install @modelcontextprotocol/sdk zod
14
+ ```
15
+
16
+ ---
17
+
18
+ ## ๐ŸŒฉ๏ธ 1. Antigravity (Google Gemini) - *Current Environment*
19
+ In this environment, the agent (me) interacts directly with the SMS orchestrator.
20
+ - **Connection**: Performed via terminal commands (`node index.js`).
21
+ - **Advantage**: Allows for dynamic orchestration and real-time report visualization within the workspace.
22
+
23
+ ---
24
+
25
+ ## ๐ŸŽญ 2. Claude Desktop
26
+ Claude can "see" your automation tools if you edit its configuration file.
27
+
28
+ ### Configuration (`claude_desktop_config.json`):
29
+ Locate the file at `%APPDATA%\Claude\claude_desktop_config.json` and add:
30
+
31
+ ```json
32
+ {
33
+ "mcpServers": {
34
+ "sms-orchestrator": {
35
+ "command": "node",
36
+ "args": ["C:/path/to/your/project/index.js"]
37
+ },
38
+ "wdio-automation": {
39
+ "command": "node",
40
+ "args": ["C:/path/to/wdio-mcp/index.js"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ## ๐Ÿ’ป 3. VS Code (Copilot / Cline / Roo Code)
49
+ VS Code does not yet support MCP natively in Copilot, but powerful extensions do.
50
+
51
+ ### For Cline (Claude Dev) or Roo Code:
52
+ 1. Open the extension settings.
53
+ 2. Look for the **"MCP Servers"** section.
54
+ 3. Add a configuration similar to Claude Desktop.
55
+ 4. The AI can now create and run your test suites directly from the VS Code chat!
56
+
57
+ ---
58
+
59
+ ## ๐Ÿš€ 4. Cursor IDE
60
+ Cursor is one of the most advanced IDEs with MCP support.
61
+
62
+ 1. Go to **Settings > Features > MCP**.
63
+ 2. Click **"+ Add Server"**.
64
+ 3. **Name**: `SMS-Engine`.
65
+ 4. **Type**: `command`.
66
+ 5. **Command**: `node C:/path/to/index.js`.
67
+ 6. This will enable `execute_suite` and other tools within the Cursor "Composer".
68
+
69
+ ---
70
+
71
+ ## ๐Ÿง  Security Notes
72
+ - Always use **absolute paths** in IDE configurations.
73
+ - Ensure the server has no interactive prompts (keyboard inputs), as the AI won't be able to respond to them via MCP (always use `--yes` or similar flags).
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # ๐Ÿš€ MCP State Machine Test Framework (SMS)
2
+
3
+ The high-fidelity, autonomous orchestration engine for Next-Generation E2E Testing.
4
+
5
+ ## ๐ŸŒŸ Key Features
6
+ - **Pure-MCP Architecture**: 100% compliant with the Model Context Protocol for seamless AI agent integration.
7
+ - **Universal**: Supports Web, Mobile, and API testing through modular MCP tool aggregation.
8
+ - **Secure by Design**: Automatic masking of sensitive data (tokens, JWTs) in reports and logs.
9
+ - **Premium Reporting**: Generates high-fidelity HTML/JSON dashboards with glassmorphism aesthetics.
10
+ - **Data-Driven Core**: Native support for complex testing scenarios via CSV and JSON data injection.
11
+
12
+ ## ๐Ÿ› ๏ธ Quick Installation
13
+
14
+ You can install it as a dependency in your project:
15
+
16
+ ```bash
17
+ npm install mcp-state-machine-test-framework
18
+ ```
19
+
20
+ Or run it directly via MCP in your configuration:
21
+
22
+ ```json
23
+ "mcp-sms": {
24
+ "command": "npx",
25
+ "args": ["mcp-state-machine-test-framework"]
26
+ }
27
+ ```
28
+
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.
34
+
35
+ ## ๐Ÿ“š Technical Documentation
36
+ - [๐Ÿง  Technical Protocol & History](./SMS_Protocol.md)
37
+ - [๐Ÿ“ฑ Perfecto Mobile Cloud Setup](./DOCUMENTATION/Perfecto_Setup.md)
38
+ - [๐Ÿค– Agent Guidelines](./agent_protocol.md)
39
+
40
+ ---
41
+ *Developed with โค๏ธ for maximum automation efficiency.*
@@ -0,0 +1,41 @@
1
+ # ๐Ÿง  Historial Tรฉcnico y Protocolo: SMS MCP Server
2
+
3
+ Este documento registra el aprendizaje evolutivo sobre el uso del **State Machine Server (SMS)** y su integraciรณn con otros servidores MCP (ej. `wdio-mcp`). **No contiene lรณgica de negocio**, sino maestrรญa tรฉcnica.
4
+
5
+ ## ๐Ÿ› ๏ธ Integraciรณn con WDIO-MCP (Aprendizajes Clave)
6
+
7
+ ### 1. Gestiรณn de Capacidades (Mobile Cloud)
8
+ - **Timeouts**: En entornos cloud (Perfecto), las sesiones tardan en inicializarse. Es vital usar `timeout: 30000` en acciones crรญticas de navegaciรณn.
9
+ - **Capabilities Estables**: Para Android en Perfecto, el set mรญnimo estable es:
10
+ - `automationName: "UiAutomator2"`
11
+ - `useVirtualDevice: true`
12
+ - `app: "PRIVATE:NombreDeLaApp.apk"`
13
+ - **Ciclo de Sesiรณn**: Es preferible cerrar y abrir sesiones entre suites independientes para evitar colisiones de estado en el driver.
14
+
15
+ ### 2. Estrategias de Selectores en MCP
16
+ - **Prioridad de Accesibilidad**: Siempre intentar primero `~ID_Accesibilidad`. Es el mรฉtodo mรกs rรกpido y compatible con MCP.
17
+ - **XPaths Robustos**: Cuando no hay IDs, usar `//*[@resource-id='com.id.app:id/elemento']//android.widget.EditText`. La doble barra `//` ayuda a mitigar cambios menores en la jerarquรญa.
18
+ - **Scroll Automรกtico**: Para elementos fuera de vista, usar el selector nativo de Android: `android=new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().resourceId("..."))`.
19
+
20
+ ### 3. Autenticaciรณn y Credenciales
21
+ - **Estrategia de Descubrimiento**: Si las variables de entorno estรกn vacรญas, rastrear archivos de "scratch" o scripts de verificaciรณn (`verify_*.js`) donde el usuario suele dejar tokens hardcodeados para pruebas rรกpidas.
22
+ - **Formato Perfecto**: El token debe inyectarse en `perfecto:options` -> `securityToken`.
23
+
24
+ ## โš™๏ธ Arquitectura del Servidor SMS
25
+
26
+ ### 1. Protocolo de Comunicaciรณn "Pure MCP"
27
+ - **STDOUT Protegido**: Nunca usar `console.log` para depuraciรณn. Cualquier salida no-JSON rompe la conexiรณn. Usar `process.stderr.write` o volcar a un archivo de log externo.
28
+ - **Resoluciรณn de Rutas**: Los servidores MCP se lanzan desde contextos variables. Siempre usar `path.join(__dirname, ...)` para localizar mapas, casos y suites.
29
+
30
+ ### 2. Motor de Reportes y Evidencia
31
+ - **Agregaciรณn de Resultados**: El servidor SMS itera sobre el array `content` de las respuestas MCP. Captura automรกticamente:
32
+ - `type: "image"` -> Se convierte en miniatura visual.
33
+ - `type: "text"` -> Se guarda como log tรฉcnico expandible.
34
+ - **Interpolaciรณn Dinรกmica**: Soporte para `{{variable}}` en las acciones, permitiendo inyectar datos de archivos CSV/JSON en tiempo de ejecuciรณn.
35
+
36
+ ## ๐Ÿ”„ Formatos de Ejecuciรณn
37
+ - **Tool Principal**: `execute_suite({ "name": "Nombre_Suite" })`.
38
+ - **Estructura Modular**: Suite -> Test Cases -> Steps -> Actions. Esta jerarquรญa garantiza que los reportes sean legibles y la evidencia se asocie correctamente a cada paso lรณgico.
39
+
40
+ ---
41
+ *Documento en evoluciรณn constante. Agregue nuevos aprendizajes tรฉcnicos aquรญ.*
@@ -0,0 +1,24 @@
1
+ # ๐Ÿค– SMS Agent Protocol V12.3 (Multi-Project Edition)
2
+
3
+ This document defines the rules for AI Agents. The framework now supports multiple isolated state maps.
4
+
5
+ ## ๐ŸŽฏ Your Mission
6
+ Act as a Senior Automation Architect. You are responsible for maintaining the integrity of multiple test environments (Web, Mobile, API).
7
+
8
+ ## ๐Ÿ—๏ธ Multi-Project Laws
9
+
10
+ ### 1. Map Isolation
11
+ - **Context Awareness**: Before modeling or creating nodes, you MUST identify which map you are working on (e.g., `maps/web_map.json` or `maps/mobile_map.json`).
12
+ - **Prefixing**: Although maps are separate files, continue using prefixes (`web_`, `mob_`) for absolute clarity in logs and reports.
13
+ - **Project Switching**: If you change from a Web task to a Mobile task, explicitly state: *"Switching context to [Map Name]"*.
14
+
15
+ ### 2. Pre-flight Integrity
16
+ - **Validation First**: Before suggesting a suite execution, verify that the `state_map` property is defined in the Suite JSON and that the file exists in the `maps/` directory.
17
+ - **Mismatch Prevention**: Never mix nodes from different maps in the same Suite unless it is a documented Hybrid flow.
18
+
19
+ ## ๐Ÿ“ก Technical Governance
20
+ - **Orchestrator Integrity**: `index.js` V12.3 includes a validation layer. If an "Integrity Check Failed" error occurs, analyze the mismatch between the Suite and the Map file.
21
+ - **Path Standard**: All state maps MUST reside in the `maps/` directory.
22
+
23
+ ---
24
+ *The SMS framework is now a multi-tenant orchestration engine. Handle each project with its own context.*
package/config.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "activeEnv": "QA",
3
+ "environments": {
4
+ "QA": {
5
+ "baseUrl": "https://www.advantageonlineshopping.com",
6
+ "apiUrl": "https://www.advantageonlineshopping.com/catalog/api/v1"
7
+ },
8
+ "PROD": {
9
+ "baseUrl": "https://www.advantageonlineshopping.com",
10
+ "apiUrl": "https://www.advantageonlineshopping.com/catalog/api/v1"
11
+ }
12
+ },
13
+ "globalRetries": 1
14
+ }
@@ -0,0 +1,2 @@
1
+ user,pass
2
+ admin,Password123
package/index.js ADDED
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
5
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
6
+ import { z } from "zod";
7
+ import fs from "fs/promises";
8
+ import fsSync from "fs";
9
+ import path from "path";
10
+ import { fileURLToPath } from "url";
11
+ import { exec } from "child_process";
12
+ import { promisify } from "util";
13
+
14
+ const execAsync = promisify(exec);
15
+
16
+ async function log(msg) {
17
+ const time = new Date().toISOString();
18
+ const line = `[${time}] ${msg}\n`;
19
+ await fs.appendFile("sms_server.log", line);
20
+ }
21
+
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const __dirname = path.dirname(__filename);
24
+ const DB_FILE = path.join(__dirname, "maquina_db.json");
25
+ const SUITES_DIR = path.join(__dirname, "suites");
26
+ const CASOS_DIR = path.join(__dirname, "test_cases");
27
+ const REPORTS_ROOT = path.join(__dirname, "reports");
28
+ const CONFIG_FILE = path.join(__dirname, "mcp_config.json");
29
+
30
+ export class MaquinaDeEstados {
31
+ constructor() {
32
+ this.nodos = new Map();
33
+ this.casosPrueba = new Map();
34
+ this.suites = new Map();
35
+ this.mcpClients = new Map();
36
+ }
37
+
38
+ async cargar() {
39
+ for (const dir of [CASOS_DIR, SUITES_DIR]) {
40
+ if (!fsSync.existsSync(dir)) await fs.mkdir(dir, { recursive: true });
41
+ const files = await fs.readdir(dir);
42
+ for (const f of files) {
43
+ if (f.endsWith(".json")) {
44
+ const content = JSON.parse(await fs.readFile(path.join(dir, f), "utf-8"));
45
+ if (dir === CASOS_DIR) this.casosPrueba.set(content.name, content);
46
+ else this.suites.set(content.name, content);
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ async getMcpClient(serverName) {
53
+ if (this.mcpClients.has(serverName)) return this.mcpClients.get(serverName);
54
+ const configRaw = await fs.readFile(CONFIG_FILE, "utf-8");
55
+ const config = JSON.parse(configRaw).mcpServers[serverName];
56
+ if (!config) throw new Error(`Configuraciรณn no encontrada para ${serverName}`);
57
+ const transport = new StdioClientTransport({
58
+ command: config.command,
59
+ args: config.args || [],
60
+ env: { ...process.env, ...config.env }
61
+ });
62
+ const client = new Client({ name: "SMS-Client", version: "11.1.0" }, { capabilities: {} });
63
+ await client.connect(transport);
64
+ const data = { client, transport };
65
+ this.mcpClients.set(serverName, data);
66
+ return data;
67
+ }
68
+
69
+ interpolate(text, data) {
70
+ if (!data) return text;
71
+ let result = text;
72
+ for (const key in data) {
73
+ result = result.replace(new RegExp(`{{${key}}}`, 'g'), data[key]);
74
+ }
75
+ return result;
76
+ }
77
+
78
+ async loadExternalData(dataPath) {
79
+ const fullPath = path.isAbsolute(dataPath) ? dataPath : path.join(__dirname, dataPath);
80
+ if (!fsSync.existsSync(fullPath)) return null;
81
+
82
+ const content = await fs.readFile(fullPath, "utf-8");
83
+ if (fullPath.endsWith(".json")) return JSON.parse(content);
84
+
85
+ if (fullPath.endsWith(".csv")) {
86
+ const lines = content.split("\n").map(l => l.trim()).filter(l => l.length > 0);
87
+ if (lines.length < 2) return [];
88
+ const headers = lines[0].split(",").map(h => h.trim().replace(/^["']|["']$/g, ""));
89
+ return lines.slice(1).map(line => {
90
+ const values = line.split(",");
91
+ const obj = {};
92
+ headers.forEach((h, i) => {
93
+ let val = values[i] ? values[i].trim() : "";
94
+ // Quitar comillas si existen
95
+ val = val.replace(/^["']|["']$/g, "");
96
+ obj[h] = val;
97
+ });
98
+ return obj;
99
+ });
100
+ }
101
+ return null;
102
+ }
103
+
104
+ async ejecutarSuite(suiteName) {
105
+ const suite = this.suites.get(suiteName);
106
+ if (!suite) throw new Error(`Suite '${suiteName}' no encontrada.`);
107
+
108
+ if (suite.state_map) {
109
+ process.stderr.write(`\n๐Ÿ“ [V12.3] Pre-flight Integrity Check for: ${suite.state_map}\n`);
110
+ // Simplified validation: Ensure the file exists and has the required structure
111
+ const mapPath = path.join(__dirname, 'maps', suite.state_map);
112
+ try {
113
+ await fs.access(mapPath);
114
+ const mapData = JSON.parse(await fs.readFile(mapPath, 'utf8'));
115
+ const nodes = mapData.nodos || mapData;
116
+ process.stderr.write(`โœ… Map validated (${Object.keys(nodes).length} nodes found).\n`);
117
+ } catch (e) {
118
+ process.stderr.write(`โŒ CRITICAL: State Map '${suite.state_map}' not found or invalid!\n`);
119
+ throw new Error(`Integrity Check Failed: ${e.message}`);
120
+ }
121
+ }
122
+
123
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
124
+ const reportDir = path.join(REPORTS_ROOT, `exec_${suiteName}_${timestamp}`);
125
+ await fs.mkdir(reportDir, { recursive: true });
126
+ const results = { suiteName, timestamp, cases: [], hooks: { beforeSuite: [], afterSuite: [] } };
127
+
128
+ const maskSecrets = (text) => {
129
+ if (typeof text !== "string") return text;
130
+ // Enmascarar tokens de seguridad y JWTs
131
+ return text.replace(/("securityToken"\s*:\s*")[^"]+(")/g, '$1********$2')
132
+ .replace(/("token"\s*:\s*")[^"]+(")/g, '$1********$2')
133
+ .replace(/eyJhbGciOi[A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]*/g, '********[JWT_MASKED]********');
134
+ };
135
+
136
+ const runActions = async (actions, targetRes, data) => {
137
+ if (!actions) return;
138
+ for (const action of actions) {
139
+ const maskedAction = maskSecrets(action);
140
+ process.stderr.write(`[EXEC] Running action: ${maskedAction}\n`);
141
+ const actionRes = { action: maskedAction, status: "passed" };
142
+ targetRes.push(actionRes);
143
+ try {
144
+ let lastResult = null;
145
+ const executeAction = async (act) => {
146
+ const subRes = (act === action) ? actionRes : { action: act, status: "passed" };
147
+ if (act !== action) targetRes.push(subRes);
148
+
149
+ let finalAction = act;
150
+ try {
151
+ if (act.startsWith("transicion:")) {
152
+ const transName = act.replace("transicion:", "").trim();
153
+ const mapPath = path.join(__dirname, 'maps', suite.state_map);
154
+ const mapData = JSON.parse(await fs.readFile(mapPath, 'utf8'));
155
+ const nodes = mapData.nodos || mapData;
156
+
157
+ let foundAction = null;
158
+ for (const node of Object.values(nodes)) {
159
+ if (node.transiciones && node.transiciones[transName]) {
160
+ foundAction = node.transiciones[transName].accion;
161
+ break;
162
+ }
163
+ }
164
+ if (!foundAction) throw new Error(`Transiciรณn '${transName}' no encontrada en el mapa '${suite.state_map}'`);
165
+ finalAction = foundAction;
166
+ }
167
+
168
+ if (Array.isArray(finalAction)) {
169
+ for (const subAct of finalAction) {
170
+ await executeAction(subAct);
171
+ }
172
+ return;
173
+ }
174
+
175
+ const interpolatedAction = this.interpolate(finalAction, data);
176
+
177
+ if (interpolatedAction.startsWith("sh:")) {
178
+ subRes.output = (await execAsync(interpolatedAction.replace("sh:", ""))).stdout;
179
+ } else if (interpolatedAction.startsWith("mcp:")) {
180
+ const raw = interpolatedAction.replace("mcp:", "").trim();
181
+ const slashIndex = raw.indexOf("/");
182
+ const serverName = raw.substring(0, slashIndex);
183
+ const rest = raw.substring(slashIndex + 1);
184
+ const spaceIndex = rest.indexOf(" ");
185
+ const toolName = spaceIndex === -1 ? rest : rest.substring(0, spaceIndex);
186
+ const toolArgs = JSON.parse(spaceIndex === -1 ? "{}" : rest.substring(spaceIndex + 1));
187
+
188
+ const { client } = await this.getMcpClient(serverName);
189
+ lastResult = await client.callTool({ name: toolName, arguments: toolArgs }, undefined, { timeout: 600000 });
190
+
191
+ if (lastResult.isError && toolName !== "close_session") {
192
+ throw new Error(lastResult.content?.[0]?.text || "Error desconocido en herramienta MCP");
193
+ }
194
+
195
+ if (lastResult.content) {
196
+ for (const item of lastResult.content) {
197
+ if (item.text) {
198
+ subRes.output = (subRes.output || "") + "\n" + maskSecrets(item.text);
199
+ }
200
+ }
201
+ }
202
+
203
+ if (lastResult.content) {
204
+ let base64 = "";
205
+ for (const item of lastResult.content) {
206
+ if (item.type === "image") base64 = item.data;
207
+ else if (item.text && (item.text.startsWith("iVBOR") || item.text.length > 5000)) base64 = item.text;
208
+ }
209
+ if (base64) {
210
+ subRes.image = base64.startsWith("data:") ? base64 : `data:image/png;base64,${base64}`;
211
+ subRes.output = (subRes.output || "") + "\nEvidencia visual capturada.";
212
+ }
213
+ }
214
+ }
215
+ } catch (e) {
216
+ subRes.status = "failed";
217
+ subRes.error = e.message;
218
+ throw e;
219
+ }
220
+ };
221
+
222
+ await executeAction(action);
223
+ } catch (e) {
224
+ actionRes.status = "failed";
225
+ actionRes.error = e.message;
226
+ throw e;
227
+ }
228
+ }
229
+ };
230
+
231
+ try {
232
+ await runActions(suite.beforeSuite, results.hooks.beforeSuite);
233
+ for (const caseName of suite.tests) {
234
+ const testCase = this.casosPrueba.get(caseName);
235
+ if (!testCase) continue;
236
+
237
+ let dataRows = [null];
238
+ if (testCase.data) {
239
+ if (Array.isArray(testCase.data)) dataRows = testCase.data;
240
+ else if (typeof testCase.data === "string") dataRows = await this.loadExternalData(testCase.data) || [null];
241
+ }
242
+
243
+ for (let i = 0; i < dataRows.length; i++) {
244
+ const row = dataRows[i];
245
+ const suffix = row ? ` [Iteration ${i+1}]` : "";
246
+ const caseRes = { name: testCase.name + suffix, steps: [], status: "passed", hooks: { beforeCase: [], afterCase: [] } };
247
+ results.cases.push(caseRes);
248
+
249
+ try {
250
+ await runActions(suite.beforeCase, caseRes.hooks.beforeCase, row);
251
+ for (const step of testCase.steps) {
252
+ const stepRes = { name: step.name, actions: [], status: "passed" };
253
+ caseRes.steps.push(stepRes);
254
+ try {
255
+ await runActions(suite.beforeStep, stepRes.actions, row);
256
+ await runActions(step.actions, stepRes.actions, row);
257
+ } catch (e) {
258
+ stepRes.status = "failed";
259
+ stepRes.error = e.message;
260
+ throw e;
261
+ } finally {
262
+ await runActions(suite.afterStep, stepRes.actions, row);
263
+ }
264
+ }
265
+ await runActions(suite.afterCase, caseRes.hooks.afterCase, row);
266
+ } catch (e) { caseRes.status = "failed"; caseRes.error = e.message; }
267
+ }
268
+ }
269
+ await runActions(suite.afterSuite, results.hooks.afterSuite);
270
+ } finally {
271
+ for (const { transport } of this.mcpClients.values()) await transport.close();
272
+ this.mcpClients.clear();
273
+ await this.finalizarReporte(reportDir, results);
274
+ }
275
+ return reportDir;
276
+ }
277
+
278
+ async finalizarReporte(dir, data) {
279
+ const renderActions = (actions) => actions.map(a => `
280
+ <div style="margin-top:15px; border-bottom: 1px solid #1e293b; padding-bottom: 15px;">
281
+ <div style="display:flex; justify-content:space-between; align-items:center;">
282
+ <code style="color: #60a5fa; font-weight:bold;">${a.action}</code>
283
+ <span style="font-size:0.7em; padding:2px 8px; border-radius:10px; background:${a.status === 'passed' ? '#065f46' : '#991b1b'}; color:white;">${a.status}</span>
284
+ </div>
285
+ ${a.image ? `<div style="margin-top:15px; text-align:center;"><img src="${a.image}" style="max-width:100%; border-radius:12px; box-shadow: 0 10px 30px rgba(0,0,0,0.5); border: 1px solid #334155;"></div>` : ""}
286
+ ${a.output ? `
287
+ <details style="margin-top:10px;">
288
+ <summary style="cursor:pointer; color:#64748b; font-size:0.8em; outline:none;">View technical logs</summary>
289
+ <pre style="background:#020617; padding:15px; border-radius:8px; font-size:0.85em; color:#10b981; margin-top:10px; border:1px solid #1e293b; overflow-x:auto;">${a.output}</pre>
290
+ </details>` : ""}
291
+ ${a.error ? `<pre style="background:#450a0a; padding:15px; border-radius:8px; font-size:0.85em; color:#fca5a5; margin-top:10px;">${a.error}</pre>` : ""}
292
+ </div>
293
+ `).join("");
294
+
295
+ const html = `<!DOCTYPE html><html lang="es"><head><meta charset="UTF-8"><title>SMS Dashboard V11.1 CSV-Ready</title>
296
+ <style>
297
+ @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap');
298
+ body { font-family: 'Outfit', sans-serif; background: #020617; color: #f8fafc; padding: 60px; line-height: 1.6; }
299
+ .container { max-width: 1000px; margin: 0 auto; }
300
+ .card { background: #0f172a; border: 1px solid #1e293b; border-radius: 20px; padding: 40px; margin-bottom: 30px; box-shadow: 0 20px 50px rgba(0,0,0,0.3); }
301
+ .hook-label { background: #3b82f6; color: white; font-size: 0.7em; font-weight: 800; padding: 5px 12px; border-radius: 50px; text-transform: uppercase; margin-bottom: 15px; display: inline-block; letter-spacing: 1px; }
302
+ h1 { font-size: 3.5em; font-weight: 800; background: linear-gradient(to right, #60a5fa, #a855f7); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 0.2em; }
303
+ .step { border-left: 3px solid #3b82f6; padding-left: 30px; margin: 40px 0; }
304
+ h2 { font-size: 2em; margin-bottom: 1em; color: #f1f5f9; }
305
+ h3 { color: #94a3b8; font-weight: 400; text-transform: uppercase; font-size: 0.9em; letter-spacing: 2px; }
306
+ </style></head>
307
+ <body><div class="container">
308
+ <h1>SMS Dashboard</h1>
309
+ <p style="color: #64748b; font-size: 1.1em; margin-bottom: 40px;">Universal Data-Driven Framework โ€ข V11.2</p>
310
+ ${data.hooks.beforeSuite.length ? `<div class="card"><span class="hook-label">Global Setup</span>${renderActions(data.hooks.beforeSuite)}</div>` : ""}
311
+ ${data.cases.map(c => `
312
+ <div class="card">
313
+ <h3 style="color:#60a5fa; margin-bottom:10px;">TEST CASE EXECUTION</h3>
314
+ <h2>${c.name}</h2>
315
+ ${c.hooks.beforeCase.length ? `<div><span class="hook-label">Pre-Condition</span>${renderActions(c.hooks.beforeCase)}</div>` : ""}
316
+ ${c.steps.map(s => `<div class="step"><h3>STEP: ${s.name}</h3>${renderActions(s.actions)}</div>`).join("")}
317
+ ${c.hooks.afterCase.length ? `<div><span class="hook-label">Post-Condition</span>${renderActions(c.hooks.afterCase)}</div>` : ""}
318
+ </div>
319
+ `).join("")}
320
+ ${data.hooks.afterSuite.length ? `<div class="card"><span class="hook-label">Global Teardown</span>${renderActions(data.hooks.afterSuite)}</div>` : ""}
321
+ </div></body></html>`;
322
+ await fs.writeFile(path.join(dir, "index.html"), html);
323
+ await fs.writeFile(path.join(dir, "results.json"), JSON.stringify(data, null, 2));
324
+ }
325
+ }
326
+
327
+ const maquina = new MaquinaDeEstados();
328
+ const server = new McpServer({ name: "demo-state-machine", version: "11.1.0" });
329
+ server.tool("execute_suite", "Ejecuciรณn External Data", { name: z.string() }, async ({ name }) => {
330
+ await maquina.cargar();
331
+ const dir = await maquina.ejecutarSuite(name);
332
+ return { content: [{ type: "text", text: `Reporte: ${dir}` }] };
333
+ });
334
+ async function main() { await maquina.cargar(); const transport = new StdioServerTransport(); await server.connect(transport); }
335
+ main().catch(console.error);
@@ -0,0 +1 @@
1
+ {"nodoActual": "home_page", "nodos": {}}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "mcp-state-machine-test-framework",
3
+ "version": "1.0.0",
4
+ "description": "High-fidelity State Machine MCP Server for autonomous E2E testing orchestration.",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "mcp-sms": "index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js"
12
+ },
13
+ "keywords": [
14
+ "mcp",
15
+ "state-machine",
16
+ "testing",
17
+ "automation",
18
+ "webdriverio",
19
+ "agentic-ai"
20
+ ],
21
+ "author": "SDET Cloud Team",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "@modelcontextprotocol/sdk": "^1.29.0",
25
+ "zod": "^3.22.4",
26
+ "webdriverio": "^8.32.0"
27
+ }
28
+ }
@@ -0,0 +1 @@
1
+ '{"status": "online"}'