@qualitas-id/mcp 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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +262 -0
  3. package/dist/constants.d.ts +18 -0
  4. package/dist/constants.d.ts.map +1 -0
  5. package/dist/constants.js +18 -0
  6. package/dist/constants.js.map +1 -0
  7. package/dist/index.d.ts +10 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +82 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/schemas/common.d.ts +20 -0
  12. package/dist/schemas/common.d.ts.map +1 -0
  13. package/dist/schemas/common.js +10 -0
  14. package/dist/schemas/common.js.map +1 -0
  15. package/dist/schemas/flow.d.ts +358 -0
  16. package/dist/schemas/flow.d.ts.map +1 -0
  17. package/dist/schemas/flow.js +72 -0
  18. package/dist/schemas/flow.js.map +1 -0
  19. package/dist/schemas/project.d.ts +70 -0
  20. package/dist/schemas/project.d.ts.map +1 -0
  21. package/dist/schemas/project.js +26 -0
  22. package/dist/schemas/project.js.map +1 -0
  23. package/dist/schemas/run.d.ts +54 -0
  24. package/dist/schemas/run.d.ts.map +1 -0
  25. package/dist/schemas/run.js +20 -0
  26. package/dist/schemas/run.js.map +1 -0
  27. package/dist/schemas/variable.d.ts +91 -0
  28. package/dist/schemas/variable.d.ts.map +1 -0
  29. package/dist/schemas/variable.js +30 -0
  30. package/dist/schemas/variable.js.map +1 -0
  31. package/dist/services/api-client.d.ts +101 -0
  32. package/dist/services/api-client.d.ts.map +1 -0
  33. package/dist/services/api-client.js +184 -0
  34. package/dist/services/api-client.js.map +1 -0
  35. package/dist/services/flow-generator.d.ts +31 -0
  36. package/dist/services/flow-generator.d.ts.map +1 -0
  37. package/dist/services/flow-generator.js +638 -0
  38. package/dist/services/flow-generator.js.map +1 -0
  39. package/dist/shared-types.d.ts +579 -0
  40. package/dist/shared-types.d.ts.map +1 -0
  41. package/dist/shared-types.js +12 -0
  42. package/dist/shared-types.js.map +1 -0
  43. package/dist/tools/flows.d.ts +13 -0
  44. package/dist/tools/flows.d.ts.map +1 -0
  45. package/dist/tools/flows.js +458 -0
  46. package/dist/tools/flows.js.map +1 -0
  47. package/dist/tools/projects.d.ts +13 -0
  48. package/dist/tools/projects.d.ts.map +1 -0
  49. package/dist/tools/projects.js +381 -0
  50. package/dist/tools/projects.js.map +1 -0
  51. package/dist/tools/runs.d.ts +9 -0
  52. package/dist/tools/runs.d.ts.map +1 -0
  53. package/dist/tools/runs.js +342 -0
  54. package/dist/tools/runs.js.map +1 -0
  55. package/dist/tools/utils.d.ts +12 -0
  56. package/dist/tools/utils.d.ts.map +1 -0
  57. package/dist/tools/utils.js +144 -0
  58. package/dist/tools/utils.js.map +1 -0
  59. package/dist/tools/variables.d.ts +9 -0
  60. package/dist/tools/variables.d.ts.map +1 -0
  61. package/dist/tools/variables.js +316 -0
  62. package/dist/tools/variables.js.map +1 -0
  63. package/dist/types.d.ts +117 -0
  64. package/dist/types.d.ts.map +1 -0
  65. package/dist/types.js +8 -0
  66. package/dist/types.js.map +1 -0
  67. package/dist/utils/flow-layout.d.ts +34 -0
  68. package/dist/utils/flow-layout.d.ts.map +1 -0
  69. package/dist/utils/flow-layout.js +109 -0
  70. package/dist/utils/flow-layout.js.map +1 -0
  71. package/dist/utils/flow-validation.d.ts +74 -0
  72. package/dist/utils/flow-validation.d.ts.map +1 -0
  73. package/dist/utils/flow-validation.js +386 -0
  74. package/dist/utils/flow-validation.js.map +1 -0
  75. package/dist/utils/ocr.d.ts +25 -0
  76. package/dist/utils/ocr.d.ts.map +1 -0
  77. package/dist/utils/ocr.js +88 -0
  78. package/dist/utils/ocr.js.map +1 -0
  79. package/package.json +65 -0
  80. package/skills/qualitas.md +253 -0
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@qualitas-id/mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Qualitas - AI-powered test automation platform",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "qualitas-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "!dist/**/*.test.*",
13
+ "skills",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "scripts": {
18
+ "start": "node dist/index.js",
19
+ "dev": "tsx watch src/index.ts",
20
+ "build": "tsc",
21
+ "clean": "rm -rf dist",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "test:coverage": "vitest run --coverage",
25
+ "docker:build": "docker build -t qualitas-mcp .",
26
+ "docker:run": "docker run -it --rm -e QUALITAS_API_KEY=${QUALITAS_API_KEY} qualitas-mcp",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "engines": {
30
+ "node": ">=18"
31
+ },
32
+ "keywords": [
33
+ "mcp",
34
+ "model-context-protocol",
35
+ "qualitas",
36
+ "test-automation",
37
+ "playwright",
38
+ "flow-builder",
39
+ "ai-agent",
40
+ "e2e-testing"
41
+ ],
42
+ "author": "Qualitas <support@qualitas.id>",
43
+ "license": "MIT",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "https://github.com/firdyfirdy/qualitas-mcp-server.git"
47
+ },
48
+ "homepage": "https://qualitas.id",
49
+ "bugs": {
50
+ "url": "https://github.com/firdyfirdy/qualitas-mcp-server/issues"
51
+ },
52
+ "dependencies": {
53
+ "@modelcontextprotocol/sdk": "^1.6.1",
54
+ "axios": "^1.7.9",
55
+ "tesseract.js": "^7.0.0",
56
+ "zod": "^3.23.8"
57
+ },
58
+ "devDependencies": {
59
+ "@qualitas-id/shared-types": "^1.2.0",
60
+ "@types/node": "^22.19.19",
61
+ "tsx": "^4.19.2",
62
+ "typescript": "^5.7.2",
63
+ "vitest": "^1.6.0"
64
+ }
65
+ }
@@ -0,0 +1,253 @@
1
+ ---
2
+ name: qualitas
3
+ description: Guide for Qualitas MCP server - test automation with flow builder, fragments, validation, OCR, and executeScript tools
4
+ license: MIT
5
+ metadata:
6
+ audience: developers
7
+ workflow: test-automation
8
+ ---
9
+
10
+ # Qualitas MCP - Skills & Best Practices
11
+
12
+ Use this guide when working with the Qualitas MCP server for test automation.
13
+
14
+ ## MCP Tools Reference (20 tools)
15
+
16
+ ### Projects
17
+ | Tool | Description |
18
+ |------|-------------|
19
+ | `list_projects` | List all projects with search/filter/sort |
20
+ | `get_project` | Get project details with stats |
21
+ | `create_project` | Create a new project |
22
+ | `update_project` | Update project settings |
23
+
24
+ ### Flows
25
+ | Tool | Description |
26
+ |------|-------------|
27
+ | `list_flows` | List flows in a project |
28
+ | `get_flow` | Get flow details (nodes, edges, variables) |
29
+ | `create_flow` | Create a flow with explicit nodes/edges |
30
+ | `update_flow` | Update flow nodes/edges/variables |
31
+ | `delete_flow` | Delete a flow permanently |
32
+ | `publish_flow` | Publish flow/fragment (draft → active) |
33
+ | `generate_flow_from_scenario` | Generate a flow from natural language |
34
+
35
+ ### Runs
36
+ | Tool | Description |
37
+ |------|-------------|
38
+ | `run_flow` | Trigger flow execution |
39
+ | `get_run_status` | Get run status and step details |
40
+ | `list_runs` | List run history for a flow |
41
+
42
+ ### Variables
43
+ | Tool | Description |
44
+ |------|-------------|
45
+ | `list_variables` | List project environment variables |
46
+ | `create_variable` | Create environment variable |
47
+ | `update_variable` | Update environment variable |
48
+ | `delete_variable` | Delete environment variable |
49
+
50
+ ### Utility
51
+ | Tool | Description |
52
+ |------|-------------|
53
+ | `extract_screenshot_text` | OCR from image URL, extract text and error messages |
54
+ | `get_run_screenshot` | Get screenshot URLs for failed steps in a run |
55
+
56
+ ## Fragment Rules
57
+
58
+ ### CRITICAL: No Start Node in Fragments
59
+ Fragments MUST NOT have a `start` node. When expanded, fragment nodes are inlined into the calling flow. A start node creates duplicate start nodes → 0 steps executed.
60
+
61
+ **Wrong:**
62
+ ```json
63
+ {
64
+ "nodes": [
65
+ {"id": "start", "type": "start", ...},
66
+ {"id": "nav_login", "type": "navigate", ...}
67
+ ]
68
+ }
69
+ ```
70
+
71
+ **Correct:**
72
+ ```json
73
+ {
74
+ "nodes": [
75
+ {"id": "nav_login", "type": "navigate", ...},
76
+ {"id": "fill_email", "type": "fill", ...}
77
+ ]
78
+ }
79
+ ```
80
+
81
+ ### When to Create a Fragment
82
+ Any flow that is repetitive across multiple flows:
83
+ - Login steps (customer or admin)
84
+ - Common setup steps (navigate + wait + assert)
85
+ - Reusable verification patterns
86
+ - Multi-step form submissions
87
+
88
+ ## Variable Usage
89
+
90
+ ### Use BASE_URL for Repeated URLs
91
+ ```json
92
+ // Create variable
93
+ { "name": "BASE_URL", "values": { "dev": "https://dev.example.com", "staging": "...", "prod": "..." } }
94
+
95
+ // Reference in flows
96
+ {"url": "{{project.BASE_URL}}/auth/login"}
97
+ {"url": "{{project.BASE_URL}}/app/dashboard"}
98
+ ```
99
+
100
+ ## Node Type Selection
101
+
102
+ ### File Upload: 4 Modes
103
+ The `upload` node supports 4 file source modes:
104
+
105
+ ```json
106
+ // 1. Local file (uploaded to B2 storage at design time)
107
+ {"type": "upload", "data": {"selector": "input[type=file]", "filePath": "https://b2.example.com/file.pdf"}}
108
+
109
+ // 2. URL (downloaded at runtime)
110
+ {"type": "upload", "data": {"selector": "input[type=file]", "filePath": "https://example.com/file.pdf"}}
111
+
112
+ // 3. Base64 (decoded at runtime, max 10MB)
113
+ {"type": "upload", "data": {"selector": "input[type=file]", "filePath": "data:application/pdf;base64,JVBERi0x..."}}
114
+
115
+ // 4. Variable reference
116
+ {"type": "upload", "data": {"selector": "input[type=file]", "filePath": "{{project.TEST_FILE}}"}}
117
+ ```
118
+
119
+ **Detection logic:** Runner auto-detects source from `filePath` value:
120
+ - `data:` prefix → base64 decode to temp file
121
+ - `http://` or `https://` → download to temp file
122
+ - Otherwise → local file path
123
+
124
+ ### Execute Script: Run Custom JavaScript
125
+ The `executeScript` node runs custom JavaScript in the browser context.
126
+
127
+ ```json
128
+ {"type": "executeScript", "data": {"script": "return document.title", "description": "Get page title"}}
129
+ ```
130
+
131
+ **Use cases:**
132
+ - Extract dynamic values from page
133
+ - Check computed styles
134
+ - Evaluate complex conditions
135
+ - Get data from JavaScript variables
136
+
137
+ **Security restrictions (sandboxed):**
138
+ - ❌ `fetch()`, `XMLHttpRequest`, `WebSocket` (no network access)
139
+ - ❌ `eval()`, `Function()` (no code execution)
140
+ - ❌ `window.location =` (no navigation)
141
+ - ❌ `document.write()`, `innerHTML =` (no DOM mutation)
142
+ - ❌ `document.cookie`, `localStorage`, `sessionStorage` (no storage access)
143
+ - ❌ `import()`, `require()` (no dynamic imports)
144
+ - ✅ `document.querySelector()`, `element.textContent` (read DOM)
145
+ - ✅ `return` statement (output value)
146
+ - ⏱️ 10s timeout (prevents infinite loops)
147
+
148
+ **Examples:**
149
+ ```json
150
+ // Get page title
151
+ {"type": "executeScript", "data": {"script": "return document.title"}}
152
+
153
+ // Get element count
154
+ {"type": "executeScript", "data": {"script": "return document.querySelectorAll('button').length"}}
155
+
156
+ // Check if element exists
157
+ {"type": "executeScript", "data": {"script": "return !!document.querySelector('.error-message')"}}
158
+
159
+ // Get input value
160
+ {"type": "executeScript", "data": {"script": "return document.querySelector('#email').value"}}
161
+ ```
162
+
163
+ ### Form Submission: Use `press` Enter, NOT `click`
164
+ ```json
165
+ // WRONG - unreliable on SPA
166
+ {"type": "click", "data": {"selector": "button:has-text('Sign In')"}}
167
+
168
+ // CORRECT - more reliable
169
+ {"type": "press", "data": {"key": "Enter", "description": "Press Enter to submit"}}
170
+ ```
171
+
172
+ ### Page Assertions: Be Specific
173
+ ```json
174
+ {"type": "assertVisible", "data": {"selector": "h1:has-text('Register')", "state": "visible"}}
175
+ {"type": "assertVisible", "data": {"selector": "text=Halo", "state": "visible"}}
176
+ ```
177
+
178
+ ### URL Assertions: Use HTTPS
179
+ ```json
180
+ // WRONG - HTTP redirects cause timeout
181
+ {"type": "assertUrl", "data": {"expectedUrl": "http://dev.example.com/app/dashboard"}}
182
+
183
+ // CORRECT
184
+ {"type": "assertUrl", "data": {"expectedUrl": "https://dev.example.com/app/dashboard"}}
185
+ ```
186
+
187
+ ## Wait Strategy
188
+
189
+ ### After Navigation
190
+ ```json
191
+ {"type": "navigate", "data": {"url": "{{project.BASE_URL}}/auth/login", "waitUntil": "load"}},
192
+ {"type": "wait", "data": {"duration": 3000, "description": "Wait for page to fully load"}}
193
+ ```
194
+
195
+ ### After Form Submission
196
+ ```json
197
+ {"type": "press", "data": {"key": "Enter"}},
198
+ {"type": "wait", "data": {"duration": 3000, "description": "Wait for API response and redirect"}},
199
+ {"type": "assertUrl", "data": {"expectedUrl": "{{project.BASE_URL}}/app/dashboard"}}
200
+ ```
201
+
202
+ ### Use `waitUntil: "load"` NOT `"networkidle"`
203
+ `networkidle` can timeout on SPA pages with persistent connections.
204
+
205
+ ## Flow Structure Templates
206
+
207
+ ### Standard Flow with Login Fragment
208
+ ```
209
+ start → callFragment (Login Fragment) → navigate → wait → assertVisible → ... test steps ...
210
+ ```
211
+
212
+ ### Registration Flow
213
+ ```
214
+ start → navigate → assertVisible → fill fields → press Enter → wait → assertUrl
215
+ ```
216
+
217
+ ## Debugging Failed Runs
218
+
219
+ ### Get Step Details
220
+ ```
221
+ get_run_status({ project_id: "...", flow_id: "...", run_id: "..." })
222
+ ```
223
+
224
+ ### Get Screenshot from Failed Step
225
+ ```
226
+ get_run_screenshot({ run_id: "...", node_id: "assert_page" })
227
+ ```
228
+
229
+ ### Extract Text from Screenshot (OCR)
230
+ ```
231
+ extract_screenshot_text({ image_url: "https://..." })
232
+ ```
233
+
234
+ ## Common Mistakes
235
+
236
+ | Mistake | Fix |
237
+ |---------|-----|
238
+ | Fragment with start node | Remove start node from fragments |
239
+ | Using `click` for form submit | Use `press Enter` |
240
+ | HTTP URLs | Use HTTPS |
241
+ | `networkidle` waitUntil | Use `load` |
242
+ | No wait after API call | Add 3s wait |
243
+ | Hardcoded domain URLs | Use `{{project.BASE_URL}}` variable |
244
+ | executeScript with `fetch()` | Use `apiCall` node instead |
245
+ | executeScript with `async/await` | Use synchronous code only |
246
+ | executeScript without `return` | Add `return` to get output |
247
+
248
+ ## Notes
249
+
250
+ - Flows are `draft` by default. Call `publish_flow` before running.
251
+ - Each test run needs a unique email. Use timestamp: `testuser+$(date +%s)@example.com`
252
+ - Step details come from `/runs/:runId/steps` endpoint.
253
+ - API client has auto-retry (3 attempts, exponential backoff).