@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.
- package/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/constants.d.ts +18 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +18 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +82 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/common.d.ts +20 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +10 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/flow.d.ts +358 -0
- package/dist/schemas/flow.d.ts.map +1 -0
- package/dist/schemas/flow.js +72 -0
- package/dist/schemas/flow.js.map +1 -0
- package/dist/schemas/project.d.ts +70 -0
- package/dist/schemas/project.d.ts.map +1 -0
- package/dist/schemas/project.js +26 -0
- package/dist/schemas/project.js.map +1 -0
- package/dist/schemas/run.d.ts +54 -0
- package/dist/schemas/run.d.ts.map +1 -0
- package/dist/schemas/run.js +20 -0
- package/dist/schemas/run.js.map +1 -0
- package/dist/schemas/variable.d.ts +91 -0
- package/dist/schemas/variable.d.ts.map +1 -0
- package/dist/schemas/variable.js +30 -0
- package/dist/schemas/variable.js.map +1 -0
- package/dist/services/api-client.d.ts +101 -0
- package/dist/services/api-client.d.ts.map +1 -0
- package/dist/services/api-client.js +184 -0
- package/dist/services/api-client.js.map +1 -0
- package/dist/services/flow-generator.d.ts +31 -0
- package/dist/services/flow-generator.d.ts.map +1 -0
- package/dist/services/flow-generator.js +638 -0
- package/dist/services/flow-generator.js.map +1 -0
- package/dist/shared-types.d.ts +579 -0
- package/dist/shared-types.d.ts.map +1 -0
- package/dist/shared-types.js +12 -0
- package/dist/shared-types.js.map +1 -0
- package/dist/tools/flows.d.ts +13 -0
- package/dist/tools/flows.d.ts.map +1 -0
- package/dist/tools/flows.js +458 -0
- package/dist/tools/flows.js.map +1 -0
- package/dist/tools/projects.d.ts +13 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +381 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/runs.d.ts +9 -0
- package/dist/tools/runs.d.ts.map +1 -0
- package/dist/tools/runs.js +342 -0
- package/dist/tools/runs.js.map +1 -0
- package/dist/tools/utils.d.ts +12 -0
- package/dist/tools/utils.d.ts.map +1 -0
- package/dist/tools/utils.js +144 -0
- package/dist/tools/utils.js.map +1 -0
- package/dist/tools/variables.d.ts +9 -0
- package/dist/tools/variables.d.ts.map +1 -0
- package/dist/tools/variables.js +316 -0
- package/dist/tools/variables.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/flow-layout.d.ts +34 -0
- package/dist/utils/flow-layout.d.ts.map +1 -0
- package/dist/utils/flow-layout.js +109 -0
- package/dist/utils/flow-layout.js.map +1 -0
- package/dist/utils/flow-validation.d.ts +74 -0
- package/dist/utils/flow-validation.d.ts.map +1 -0
- package/dist/utils/flow-validation.js +386 -0
- package/dist/utils/flow-validation.js.map +1 -0
- package/dist/utils/ocr.d.ts +25 -0
- package/dist/utils/ocr.d.ts.map +1 -0
- package/dist/utils/ocr.js +88 -0
- package/dist/utils/ocr.js.map +1 -0
- package/package.json +65 -0
- 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).
|