mcp-maestro-mobile-ai 1.1.1 → 1.3.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/CHANGELOG.md +32 -0
- package/package.json +1 -1
- package/src/mcp-server/index.js +200 -11
- package/src/mcp-server/tools/contextTools.js +123 -0
- package/src/mcp-server/tools/runTools.js +227 -4
- package/src/mcp-server/utils/reportGenerator.js +455 -0
- package/src/mcp-server/utils/yamlTemplate.js +559 -0
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,38 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
15
15
|
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
+
## [1.3.1] - 2025-01-06
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **MCP Schema Fix**: Fixed `generate_report` tool array parameter missing `items` definition
|
|
22
|
+
- This was causing "tool parameters array type must have items" validation error
|
|
23
|
+
- Now properly defines the structure of test result objects
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## [1.2.0] - 2025-01-06
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
- **YAML Generation Instructions System**: Ensures consistent YAML generation across different environments
|
|
31
|
+
- `get_yaml_instructions` - AI MUST call this before generating YAML (provides exact rules)
|
|
32
|
+
- `validate_yaml_structure` - Validates YAML for common issues (like missing tapOn before inputText)
|
|
33
|
+
- `get_test_pattern` - Get standard patterns for login, search, navigation, form tests
|
|
34
|
+
- **Critical Fix**: Input text pattern now enforced - prevents password going to username field issue
|
|
35
|
+
- Standard test patterns for common scenarios (login, search, navigation, form)
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
- YAML generation inconsistency between different environments
|
|
39
|
+
- Text input going to wrong fields due to missing tapOn commands
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## [1.1.1] - 2025-01-06
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
- Version bump for npm publish
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
18
50
|
## [1.1.0] - 2025-01-06
|
|
19
51
|
|
|
20
52
|
### Changed
|
package/package.json
CHANGED
package/src/mcp-server/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { dirname, join } from "path";
|
|
|
23
23
|
// Import tools
|
|
24
24
|
import { readPromptFile, listPromptFiles } from "./tools/promptTools.js";
|
|
25
25
|
import { validateMaestroYaml } from "./tools/validateTools.js";
|
|
26
|
-
import { runTest, runTestSuite } from "./tools/runTools.js";
|
|
26
|
+
import { runTest, runTestSuite, generateTestReport, listTestReports, runTestSuiteWithReport } from "./tools/runTools.js";
|
|
27
27
|
import {
|
|
28
28
|
getAppConfig,
|
|
29
29
|
getTestResults,
|
|
@@ -45,6 +45,10 @@ import {
|
|
|
45
45
|
getAppContext,
|
|
46
46
|
clearContext,
|
|
47
47
|
listContexts,
|
|
48
|
+
getYamlInstructions,
|
|
49
|
+
validateYamlBeforeRun,
|
|
50
|
+
getTestPattern,
|
|
51
|
+
getScreenAnalysis,
|
|
48
52
|
} from "./tools/contextTools.js";
|
|
49
53
|
import { logger } from "./utils/logger.js";
|
|
50
54
|
import { validatePrerequisites } from "./utils/prerequisites.js";
|
|
@@ -58,7 +62,7 @@ config({ path: join(__dirname, "../../.env") });
|
|
|
58
62
|
const server = new Server(
|
|
59
63
|
{
|
|
60
64
|
name: "mcp-maestro-mobile-ai",
|
|
61
|
-
version: "1.
|
|
65
|
+
version: "1.2.0",
|
|
62
66
|
},
|
|
63
67
|
{
|
|
64
68
|
capabilities: {
|
|
@@ -180,7 +184,7 @@ const TOOLS = [
|
|
|
180
184
|
{
|
|
181
185
|
name: "validate_maestro_yaml",
|
|
182
186
|
description:
|
|
183
|
-
"Validate
|
|
187
|
+
"Validate Maestro YAML for syntax AND structure errors. Checks for: missing appId, missing clearState/launchApp, inputText without tapOn (which causes text to go to wrong fields). Always validate before running!",
|
|
184
188
|
inputSchema: {
|
|
185
189
|
type: "object",
|
|
186
190
|
properties: {
|
|
@@ -196,14 +200,31 @@ const TOOLS = [
|
|
|
196
200
|
// === Test Execution Tools ===
|
|
197
201
|
{
|
|
198
202
|
name: "run_test",
|
|
199
|
-
description:
|
|
200
|
-
|
|
203
|
+
description: `Run a single Maestro test. IMPORTANT: The YAML MUST follow these rules or it will be REJECTED:
|
|
204
|
+
|
|
205
|
+
1. STRUCTURE: Must start with appId, then clearState, then launchApp
|
|
206
|
+
2. TEXT INPUT: ALWAYS use tapOn BEFORE inputText (or text goes to wrong field!)
|
|
207
|
+
CORRECT: - tapOn: "Username" then - inputText: "value"
|
|
208
|
+
WRONG: - inputText: "value" (missing tapOn!)
|
|
209
|
+
3. Use visible text labels for elements when testIDs are unknown
|
|
210
|
+
|
|
211
|
+
Example valid YAML:
|
|
212
|
+
appId: com.example.app
|
|
213
|
+
---
|
|
214
|
+
- clearState
|
|
215
|
+
- launchApp
|
|
216
|
+
- tapOn: "Username"
|
|
217
|
+
- inputText: "user@example.com"
|
|
218
|
+
- tapOn: "Password"
|
|
219
|
+
- inputText: "password123"
|
|
220
|
+
- tapOn: "Sign In"
|
|
221
|
+
- assertVisible: "Welcome"`,
|
|
201
222
|
inputSchema: {
|
|
202
223
|
type: "object",
|
|
203
224
|
properties: {
|
|
204
225
|
yaml: {
|
|
205
226
|
type: "string",
|
|
206
|
-
description: "The Maestro YAML flow content
|
|
227
|
+
description: "The Maestro YAML flow content. MUST use tapOn before inputText for each field!",
|
|
207
228
|
},
|
|
208
229
|
name: {
|
|
209
230
|
type: "string",
|
|
@@ -221,7 +242,7 @@ const TOOLS = [
|
|
|
221
242
|
{
|
|
222
243
|
name: "run_test_suite",
|
|
223
244
|
description:
|
|
224
|
-
"Run multiple Maestro tests in sequence.
|
|
245
|
+
"Run multiple Maestro tests in sequence. Each YAML must follow the rules: appId at top, clearState, launchApp, and ALWAYS tapOn before inputText!",
|
|
225
246
|
inputSchema: {
|
|
226
247
|
type: "object",
|
|
227
248
|
properties: {
|
|
@@ -232,7 +253,7 @@ const TOOLS = [
|
|
|
232
253
|
properties: {
|
|
233
254
|
yaml: {
|
|
234
255
|
type: "string",
|
|
235
|
-
description: "The Maestro YAML
|
|
256
|
+
description: "The Maestro YAML. MUST use tapOn before inputText!",
|
|
236
257
|
},
|
|
237
258
|
name: {
|
|
238
259
|
type: "string",
|
|
@@ -253,7 +274,90 @@ const TOOLS = [
|
|
|
253
274
|
},
|
|
254
275
|
},
|
|
255
276
|
|
|
256
|
-
// === Results &
|
|
277
|
+
// === Results & Reporting Tools ===
|
|
278
|
+
{
|
|
279
|
+
name: "run_tests_with_report",
|
|
280
|
+
description:
|
|
281
|
+
"Run multiple tests and automatically generate HTML + JSON report. Use this when running tests from a prompt file. Returns report path that can be opened in browser.",
|
|
282
|
+
inputSchema: {
|
|
283
|
+
type: "object",
|
|
284
|
+
properties: {
|
|
285
|
+
tests: {
|
|
286
|
+
type: "array",
|
|
287
|
+
items: {
|
|
288
|
+
type: "object",
|
|
289
|
+
properties: {
|
|
290
|
+
yaml: {
|
|
291
|
+
type: "string",
|
|
292
|
+
description: "The Maestro YAML. MUST use tapOn before inputText!",
|
|
293
|
+
},
|
|
294
|
+
name: {
|
|
295
|
+
type: "string",
|
|
296
|
+
description: "Name for this test",
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
required: ["yaml", "name"],
|
|
300
|
+
},
|
|
301
|
+
description: "Array of tests to run",
|
|
302
|
+
},
|
|
303
|
+
promptFile: {
|
|
304
|
+
type: "string",
|
|
305
|
+
description: "Name of the prompt file (for report metadata)",
|
|
306
|
+
},
|
|
307
|
+
appId: {
|
|
308
|
+
type: "string",
|
|
309
|
+
description: "App ID (for report metadata)",
|
|
310
|
+
},
|
|
311
|
+
retries: {
|
|
312
|
+
type: "number",
|
|
313
|
+
description: "Number of retries for failed tests",
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
required: ["tests"],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: "generate_report",
|
|
321
|
+
description:
|
|
322
|
+
"Generate HTML and JSON report from test results. Call this after running tests to create a visual summary report.",
|
|
323
|
+
inputSchema: {
|
|
324
|
+
type: "object",
|
|
325
|
+
properties: {
|
|
326
|
+
results: {
|
|
327
|
+
type: "array",
|
|
328
|
+
items: {
|
|
329
|
+
type: "object",
|
|
330
|
+
properties: {
|
|
331
|
+
name: { type: "string", description: "Test name" },
|
|
332
|
+
success: { type: "boolean", description: "Whether the test passed" },
|
|
333
|
+
duration: { type: "number", description: "Test duration in milliseconds" },
|
|
334
|
+
error: { type: "string", description: "Error message if test failed" },
|
|
335
|
+
},
|
|
336
|
+
required: ["name", "success"],
|
|
337
|
+
},
|
|
338
|
+
description: "Array of test results with name, success, duration, error fields",
|
|
339
|
+
},
|
|
340
|
+
promptFile: {
|
|
341
|
+
type: "string",
|
|
342
|
+
description: "Name of the prompt file",
|
|
343
|
+
},
|
|
344
|
+
appId: {
|
|
345
|
+
type: "string",
|
|
346
|
+
description: "App ID",
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
required: ["results"],
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: "list_reports",
|
|
354
|
+
description:
|
|
355
|
+
"List all generated test reports. Returns paths to HTML and JSON report files.",
|
|
356
|
+
inputSchema: {
|
|
357
|
+
type: "object",
|
|
358
|
+
properties: {},
|
|
359
|
+
},
|
|
360
|
+
},
|
|
257
361
|
{
|
|
258
362
|
name: "get_test_results",
|
|
259
363
|
description: "Get the results from the last test run or a specific run by ID.",
|
|
@@ -458,6 +562,62 @@ const TOOLS = [
|
|
|
458
562
|
properties: {},
|
|
459
563
|
},
|
|
460
564
|
},
|
|
565
|
+
|
|
566
|
+
// === YAML Generation Tools (CRITICAL) ===
|
|
567
|
+
{
|
|
568
|
+
name: "get_yaml_instructions",
|
|
569
|
+
description:
|
|
570
|
+
"CRITICAL: Call this BEFORE generating any Maestro YAML. Returns the exact rules and patterns for generating valid YAML that works consistently. Includes app-specific context if available.",
|
|
571
|
+
inputSchema: {
|
|
572
|
+
type: "object",
|
|
573
|
+
properties: {
|
|
574
|
+
appId: {
|
|
575
|
+
type: "string",
|
|
576
|
+
description: "App package ID to get app-specific context",
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
name: "validate_yaml_structure",
|
|
583
|
+
description:
|
|
584
|
+
"Validate YAML structure before running a test. Checks for common issues like missing 'tapOn' before 'inputText' which causes text to go to wrong fields.",
|
|
585
|
+
inputSchema: {
|
|
586
|
+
type: "object",
|
|
587
|
+
properties: {
|
|
588
|
+
yamlContent: {
|
|
589
|
+
type: "string",
|
|
590
|
+
description: "The Maestro YAML content to validate",
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
required: ["yamlContent"],
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
name: "get_test_pattern",
|
|
598
|
+
description:
|
|
599
|
+
"Get a standard test pattern template. Available: login, form, search, navigation, list, settings, logout. Use these as starting points.",
|
|
600
|
+
inputSchema: {
|
|
601
|
+
type: "object",
|
|
602
|
+
properties: {
|
|
603
|
+
patternName: {
|
|
604
|
+
type: "string",
|
|
605
|
+
description: "Pattern name: login, form, search, navigation, list, settings, or logout",
|
|
606
|
+
enum: ["login", "form", "search", "navigation", "list", "settings", "logout"],
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
required: ["patternName"],
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
name: "get_screen_analysis_help",
|
|
614
|
+
description:
|
|
615
|
+
"Get instructions on how to gather UI element information from the user. Call this when you don't know the exact element names/labels on a screen. Returns questions to ask the user.",
|
|
616
|
+
inputSchema: {
|
|
617
|
+
type: "object",
|
|
618
|
+
properties: {},
|
|
619
|
+
},
|
|
620
|
+
},
|
|
461
621
|
];
|
|
462
622
|
|
|
463
623
|
// ============================================
|
|
@@ -515,7 +675,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
515
675
|
case "run_test_suite":
|
|
516
676
|
return await runTestSuite(args.tests, { retries: args.retries });
|
|
517
677
|
|
|
518
|
-
// Results &
|
|
678
|
+
// Results & reporting tools
|
|
679
|
+
case "run_tests_with_report":
|
|
680
|
+
return await runTestSuiteWithReport(args.tests, {
|
|
681
|
+
promptFile: args.promptFile,
|
|
682
|
+
appId: args.appId,
|
|
683
|
+
retries: args.retries,
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
case "generate_report":
|
|
687
|
+
return await generateTestReport(args.results, {
|
|
688
|
+
promptFile: args.promptFile,
|
|
689
|
+
appId: args.appId,
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
case "list_reports":
|
|
693
|
+
return await listTestReports();
|
|
694
|
+
|
|
519
695
|
case "get_test_results":
|
|
520
696
|
return await getTestResults(args.runId);
|
|
521
697
|
|
|
@@ -556,6 +732,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
556
732
|
case "list_app_contexts":
|
|
557
733
|
return await listContexts();
|
|
558
734
|
|
|
735
|
+
// YAML generation tools
|
|
736
|
+
case "get_yaml_instructions":
|
|
737
|
+
return await getYamlInstructions(args.appId);
|
|
738
|
+
|
|
739
|
+
case "validate_yaml_structure":
|
|
740
|
+
return await validateYamlBeforeRun(args.yamlContent);
|
|
741
|
+
|
|
742
|
+
case "get_test_pattern":
|
|
743
|
+
return await getTestPattern(args.patternName);
|
|
744
|
+
|
|
745
|
+
case "get_screen_analysis_help":
|
|
746
|
+
return await getScreenAnalysis();
|
|
747
|
+
|
|
559
748
|
default:
|
|
560
749
|
throw new Error(`Unknown tool: ${name}`);
|
|
561
750
|
}
|
|
@@ -612,7 +801,7 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
612
801
|
// ============================================
|
|
613
802
|
|
|
614
803
|
async function main() {
|
|
615
|
-
logger.info("Starting MCP Maestro Mobile AI v1.
|
|
804
|
+
logger.info("Starting MCP Maestro Mobile AI v1.2.0...");
|
|
616
805
|
logger.info("");
|
|
617
806
|
|
|
618
807
|
// Validate prerequisites before starting
|
|
@@ -14,6 +14,13 @@ import {
|
|
|
14
14
|
clearAppContext,
|
|
15
15
|
listAppContexts,
|
|
16
16
|
} from "../utils/appContext.js";
|
|
17
|
+
import {
|
|
18
|
+
YAML_GENERATION_INSTRUCTIONS,
|
|
19
|
+
getYamlGenerationContext,
|
|
20
|
+
TEST_PATTERNS,
|
|
21
|
+
validateYamlStructure,
|
|
22
|
+
getScreenAnalysisInstructions,
|
|
23
|
+
} from "../utils/yamlTemplate.js";
|
|
17
24
|
|
|
18
25
|
/**
|
|
19
26
|
* Format result as MCP response
|
|
@@ -180,6 +187,118 @@ export async function listContexts() {
|
|
|
180
187
|
return formatResponse(result);
|
|
181
188
|
}
|
|
182
189
|
|
|
190
|
+
/**
|
|
191
|
+
* Get YAML generation instructions
|
|
192
|
+
* AI MUST call this before generating any Maestro YAML
|
|
193
|
+
*/
|
|
194
|
+
export async function getYamlInstructions(appId) {
|
|
195
|
+
if (!appId) {
|
|
196
|
+
// Return generic instructions without app context
|
|
197
|
+
return formatResponse({
|
|
198
|
+
success: true,
|
|
199
|
+
instructions: YAML_GENERATION_INSTRUCTIONS,
|
|
200
|
+
patterns: TEST_PATTERNS,
|
|
201
|
+
message: "Use these instructions when generating Maestro YAML. Always follow the tapOn → inputText pattern for text input.",
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Get app-specific context
|
|
206
|
+
const appContext = await loadAppContext(appId);
|
|
207
|
+
const instructions = getYamlGenerationContext(appId, appContext);
|
|
208
|
+
|
|
209
|
+
return formatResponse({
|
|
210
|
+
success: true,
|
|
211
|
+
appId,
|
|
212
|
+
instructions,
|
|
213
|
+
patterns: TEST_PATTERNS,
|
|
214
|
+
hasAppContext: Object.keys(appContext.elements || {}).length > 0,
|
|
215
|
+
message: "IMPORTANT: Follow these instructions EXACTLY when generating YAML. Always use tapOn before inputText.",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Validate YAML before running
|
|
221
|
+
* Checks for common issues like missing tapOn before inputText
|
|
222
|
+
*/
|
|
223
|
+
export async function validateYamlBeforeRun(yamlContent) {
|
|
224
|
+
if (!yamlContent) {
|
|
225
|
+
return formatResponse({
|
|
226
|
+
success: false,
|
|
227
|
+
error: "yamlContent is required",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const validation = validateYamlStructure(yamlContent);
|
|
232
|
+
|
|
233
|
+
if (!validation.valid) {
|
|
234
|
+
return formatResponse({
|
|
235
|
+
success: false,
|
|
236
|
+
valid: false,
|
|
237
|
+
issues: validation.issues,
|
|
238
|
+
message: "YAML has structural issues that may cause test failures. Please fix before running.",
|
|
239
|
+
fixedYaml: null, // Could auto-fix in future
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return formatResponse({
|
|
244
|
+
success: true,
|
|
245
|
+
valid: true,
|
|
246
|
+
message: "YAML structure is valid.",
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get a test pattern template
|
|
252
|
+
*/
|
|
253
|
+
export async function getTestPattern(patternName) {
|
|
254
|
+
const patterns = {
|
|
255
|
+
login: TEST_PATTERNS.login,
|
|
256
|
+
search: TEST_PATTERNS.search,
|
|
257
|
+
navigation: TEST_PATTERNS.navigation,
|
|
258
|
+
form: TEST_PATTERNS.form,
|
|
259
|
+
list: TEST_PATTERNS.list,
|
|
260
|
+
settings: TEST_PATTERNS.settings,
|
|
261
|
+
logout: TEST_PATTERNS.logout,
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const pattern = patterns[patternName?.toLowerCase()];
|
|
265
|
+
|
|
266
|
+
if (!pattern) {
|
|
267
|
+
return formatResponse({
|
|
268
|
+
success: false,
|
|
269
|
+
error: `Unknown pattern: ${patternName}`,
|
|
270
|
+
availablePatterns: Object.keys(patterns),
|
|
271
|
+
hint: "Use: login, form, search, navigation, list, settings, or logout",
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return formatResponse({
|
|
276
|
+
success: true,
|
|
277
|
+
pattern: patternName,
|
|
278
|
+
template: pattern,
|
|
279
|
+
message: "Replace placeholders in {} with actual values. REMEMBER: Always use tapOn before inputText!",
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get screen analysis instructions
|
|
285
|
+
* Helps AI understand how to gather UI element information
|
|
286
|
+
*/
|
|
287
|
+
export async function getScreenAnalysis() {
|
|
288
|
+
const instructions = getScreenAnalysisInstructions();
|
|
289
|
+
|
|
290
|
+
return formatResponse({
|
|
291
|
+
success: true,
|
|
292
|
+
instructions,
|
|
293
|
+
message: "Use these instructions to gather UI element information before generating YAML.",
|
|
294
|
+
requiredInfo: [
|
|
295
|
+
"Field labels/placeholders for text inputs",
|
|
296
|
+
"Button text for actions",
|
|
297
|
+
"Success/error indicators to verify results",
|
|
298
|
+
],
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
183
302
|
export default {
|
|
184
303
|
registerAppElements,
|
|
185
304
|
registerAppScreen,
|
|
@@ -190,5 +309,9 @@ export default {
|
|
|
190
309
|
getAppContext,
|
|
191
310
|
clearContext,
|
|
192
311
|
listContexts,
|
|
312
|
+
getYamlInstructions,
|
|
313
|
+
validateYamlBeforeRun,
|
|
314
|
+
getTestPattern,
|
|
315
|
+
getScreenAnalysis,
|
|
193
316
|
};
|
|
194
317
|
|