mcp-maestro-mobile-ai 1.1.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/CHANGELOG.md +114 -0
- package/CONTRIBUTING.md +417 -0
- package/LICENSE +22 -0
- package/README.md +719 -0
- package/ROADMAP.md +239 -0
- package/docs/ENTERPRISE_READINESS.md +545 -0
- package/docs/MCP_SETUP.md +180 -0
- package/docs/PRIVACY.md +198 -0
- package/docs/REACT_NATIVE_AUTOMATION_GUIDELINES.md +584 -0
- package/docs/SECURITY.md +573 -0
- package/package.json +69 -0
- package/prompts/example-login-tests.txt +9 -0
- package/prompts/example-youtube-tests.txt +8 -0
- package/src/mcp-server/index.js +625 -0
- package/src/mcp-server/tools/contextTools.js +194 -0
- package/src/mcp-server/tools/promptTools.js +191 -0
- package/src/mcp-server/tools/runTools.js +357 -0
- package/src/mcp-server/tools/utilityTools.js +721 -0
- package/src/mcp-server/tools/validateTools.js +220 -0
- package/src/mcp-server/utils/appContext.js +295 -0
- package/src/mcp-server/utils/logger.js +52 -0
- package/src/mcp-server/utils/maestro.js +508 -0
- package/templates/mcp-config-claude-desktop.json +15 -0
- package/templates/mcp-config-cursor.json +15 -0
- package/templates/mcp-config-vscode.json +13 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Maestro MCP Server
|
|
5
|
+
* AI-Assisted Mobile Automation using Model Context Protocol
|
|
6
|
+
*
|
|
7
|
+
* This server exposes tools for running Maestro mobile tests
|
|
8
|
+
* that can be called by AI clients (Cursor, Claude Desktop, VS Code Copilot)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
12
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
+
import {
|
|
14
|
+
CallToolRequestSchema,
|
|
15
|
+
ListToolsRequestSchema,
|
|
16
|
+
ListResourcesRequestSchema,
|
|
17
|
+
ReadResourceRequestSchema,
|
|
18
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
19
|
+
import { config } from "dotenv";
|
|
20
|
+
import { fileURLToPath } from "url";
|
|
21
|
+
import { dirname, join } from "path";
|
|
22
|
+
|
|
23
|
+
// Import tools
|
|
24
|
+
import { readPromptFile, listPromptFiles } from "./tools/promptTools.js";
|
|
25
|
+
import { validateMaestroYaml } from "./tools/validateTools.js";
|
|
26
|
+
import { runTest, runTestSuite } from "./tools/runTools.js";
|
|
27
|
+
import {
|
|
28
|
+
getAppConfig,
|
|
29
|
+
getTestResults,
|
|
30
|
+
takeScreenshot,
|
|
31
|
+
checkDevice,
|
|
32
|
+
checkApp,
|
|
33
|
+
cleanupResults,
|
|
34
|
+
listDevices,
|
|
35
|
+
selectDevice,
|
|
36
|
+
clearDevice,
|
|
37
|
+
} from "./tools/utilityTools.js";
|
|
38
|
+
import {
|
|
39
|
+
registerAppElements,
|
|
40
|
+
registerAppScreen,
|
|
41
|
+
saveFlow,
|
|
42
|
+
getFlows,
|
|
43
|
+
removeFlow,
|
|
44
|
+
getAIContext,
|
|
45
|
+
getAppContext,
|
|
46
|
+
clearContext,
|
|
47
|
+
listContexts,
|
|
48
|
+
} from "./tools/contextTools.js";
|
|
49
|
+
import { logger } from "./utils/logger.js";
|
|
50
|
+
|
|
51
|
+
// Load environment variables
|
|
52
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
53
|
+
const __dirname = dirname(__filename);
|
|
54
|
+
config({ path: join(__dirname, "../../.env") });
|
|
55
|
+
|
|
56
|
+
// Create MCP Server
|
|
57
|
+
const server = new Server(
|
|
58
|
+
{
|
|
59
|
+
name: "mcp-maestro-mobile-ai",
|
|
60
|
+
version: "1.1.0",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
capabilities: {
|
|
64
|
+
tools: {},
|
|
65
|
+
resources: {},
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// ============================================
|
|
71
|
+
// TOOL DEFINITIONS
|
|
72
|
+
// ============================================
|
|
73
|
+
|
|
74
|
+
const TOOLS = [
|
|
75
|
+
// === Prompt File Tools ===
|
|
76
|
+
{
|
|
77
|
+
name: "read_prompt_file",
|
|
78
|
+
description:
|
|
79
|
+
"Read test prompts from a .txt or .md file. Each line in the file is treated as a separate test case prompt.",
|
|
80
|
+
inputSchema: {
|
|
81
|
+
type: "object",
|
|
82
|
+
properties: {
|
|
83
|
+
file: {
|
|
84
|
+
type: "string",
|
|
85
|
+
description:
|
|
86
|
+
'Path to the prompt file (e.g., "prompts/login-tests.txt")',
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
required: ["file"],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "list_prompt_files",
|
|
94
|
+
description: "List all available prompt files in the prompts directory.",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
type: "object",
|
|
97
|
+
properties: {
|
|
98
|
+
directory: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description:
|
|
101
|
+
'Directory to search for prompt files (default: "prompts")',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// === Device Management Tools ===
|
|
108
|
+
{
|
|
109
|
+
name: "list_devices",
|
|
110
|
+
description:
|
|
111
|
+
"List all connected Android devices and emulators. Shows device ID, type (emulator/physical), and model name. Use this to see available devices before selecting one.",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "select_device",
|
|
119
|
+
description:
|
|
120
|
+
"Select a specific device to run tests on. All subsequent tests will run on this device until changed. Use list_devices first to see available device IDs.",
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
deviceId: {
|
|
125
|
+
type: "string",
|
|
126
|
+
description:
|
|
127
|
+
'Device ID to select (e.g., "emulator-5554" or "RF8M12345XY" for physical device)',
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
required: ["deviceId"],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "clear_device",
|
|
135
|
+
description:
|
|
136
|
+
"Clear the device selection. Tests will run on the first available device (default behavior).",
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: "object",
|
|
139
|
+
properties: {},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: "check_device",
|
|
144
|
+
description:
|
|
145
|
+
"Check if an Android emulator or device is connected. Shows connection status and which device is selected.",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "check_app",
|
|
153
|
+
description:
|
|
154
|
+
"Check if the target app is installed on the connected device. Verifies the app is ready for testing.",
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: "object",
|
|
157
|
+
properties: {
|
|
158
|
+
appId: {
|
|
159
|
+
type: "string",
|
|
160
|
+
description:
|
|
161
|
+
"Optional: App package ID to check. Uses configured APP_ID if not provided.",
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
// === Configuration Tools ===
|
|
168
|
+
{
|
|
169
|
+
name: "get_app_config",
|
|
170
|
+
description:
|
|
171
|
+
"Get the current app configuration including appId, platform, selected device, and other settings.",
|
|
172
|
+
inputSchema: {
|
|
173
|
+
type: "object",
|
|
174
|
+
properties: {},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
// === Validation Tools ===
|
|
179
|
+
{
|
|
180
|
+
name: "validate_maestro_yaml",
|
|
181
|
+
description:
|
|
182
|
+
"Validate a Maestro YAML flow for syntax errors before running. Returns validation result with any errors found.",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
type: "object",
|
|
185
|
+
properties: {
|
|
186
|
+
yaml: {
|
|
187
|
+
type: "string",
|
|
188
|
+
description: "The Maestro YAML content to validate",
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
required: ["yaml"],
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
// === Test Execution Tools ===
|
|
196
|
+
{
|
|
197
|
+
name: "run_test",
|
|
198
|
+
description:
|
|
199
|
+
"Run a single Maestro test. Provide the Maestro YAML content directly. The test will be executed on the selected device (or first available if none selected). Includes pre-flight checks and auto-retry.",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
yaml: {
|
|
204
|
+
type: "string",
|
|
205
|
+
description: "The Maestro YAML flow content to run",
|
|
206
|
+
},
|
|
207
|
+
name: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "Name for this test (used for reporting and screenshots)",
|
|
210
|
+
},
|
|
211
|
+
retries: {
|
|
212
|
+
type: "number",
|
|
213
|
+
description:
|
|
214
|
+
"Optional: Number of retries if test fails (default: from config or 0)",
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
required: ["yaml", "name"],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "run_test_suite",
|
|
222
|
+
description:
|
|
223
|
+
"Run multiple Maestro tests in sequence. Provide an array of test objects with yaml and name properties. Tests run on selected device.",
|
|
224
|
+
inputSchema: {
|
|
225
|
+
type: "object",
|
|
226
|
+
properties: {
|
|
227
|
+
tests: {
|
|
228
|
+
type: "array",
|
|
229
|
+
items: {
|
|
230
|
+
type: "object",
|
|
231
|
+
properties: {
|
|
232
|
+
yaml: {
|
|
233
|
+
type: "string",
|
|
234
|
+
description: "The Maestro YAML flow content",
|
|
235
|
+
},
|
|
236
|
+
name: {
|
|
237
|
+
type: "string",
|
|
238
|
+
description: "Name for this test",
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
required: ["yaml", "name"],
|
|
242
|
+
},
|
|
243
|
+
description: "Array of tests to run",
|
|
244
|
+
},
|
|
245
|
+
retries: {
|
|
246
|
+
type: "number",
|
|
247
|
+
description:
|
|
248
|
+
"Optional: Number of retries for failed tests (default: from config or 0)",
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
required: ["tests"],
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
|
|
255
|
+
// === Results & Utility Tools ===
|
|
256
|
+
{
|
|
257
|
+
name: "get_test_results",
|
|
258
|
+
description: "Get the results from the last test run or a specific run by ID.",
|
|
259
|
+
inputSchema: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
runId: {
|
|
263
|
+
type: "string",
|
|
264
|
+
description: "Optional: Specific run ID to get results for",
|
|
265
|
+
},
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "take_screenshot",
|
|
271
|
+
description:
|
|
272
|
+
"Take a screenshot of the current device screen. Useful for debugging or verification.",
|
|
273
|
+
inputSchema: {
|
|
274
|
+
type: "object",
|
|
275
|
+
properties: {
|
|
276
|
+
name: {
|
|
277
|
+
type: "string",
|
|
278
|
+
description: "Name for the screenshot file",
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
required: ["name"],
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: "cleanup_results",
|
|
286
|
+
description:
|
|
287
|
+
"Clean up old test results and screenshots to free up disk space. Keeps the most recent results.",
|
|
288
|
+
inputSchema: {
|
|
289
|
+
type: "object",
|
|
290
|
+
properties: {
|
|
291
|
+
keepLast: {
|
|
292
|
+
type: "number",
|
|
293
|
+
description: "Number of recent results to keep (default: 50)",
|
|
294
|
+
},
|
|
295
|
+
deleteScreenshots: {
|
|
296
|
+
type: "boolean",
|
|
297
|
+
description: "Whether to delete old screenshots (default: true)",
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
// === App Context/Training Tools ===
|
|
304
|
+
{
|
|
305
|
+
name: "register_elements",
|
|
306
|
+
description:
|
|
307
|
+
"Register UI elements for an app to help AI generate better YAML. Provide testIDs, accessibilityLabels, and text values for app elements. This teaches the AI about your app's UI structure.",
|
|
308
|
+
inputSchema: {
|
|
309
|
+
type: "object",
|
|
310
|
+
properties: {
|
|
311
|
+
appId: {
|
|
312
|
+
type: "string",
|
|
313
|
+
description: "App package ID (e.g., 'com.myapp')",
|
|
314
|
+
},
|
|
315
|
+
elements: {
|
|
316
|
+
type: "object",
|
|
317
|
+
description:
|
|
318
|
+
"Object containing element definitions. Each key is the element name, value contains: testId, accessibilityLabel, text, type, description",
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
required: ["appId", "elements"],
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: "register_screen",
|
|
326
|
+
description:
|
|
327
|
+
"Register a screen structure for an app. Define what elements and actions are available on each screen.",
|
|
328
|
+
inputSchema: {
|
|
329
|
+
type: "object",
|
|
330
|
+
properties: {
|
|
331
|
+
appId: {
|
|
332
|
+
type: "string",
|
|
333
|
+
description: "App package ID",
|
|
334
|
+
},
|
|
335
|
+
screenName: {
|
|
336
|
+
type: "string",
|
|
337
|
+
description: "Name of the screen (e.g., 'LoginScreen', 'Dashboard')",
|
|
338
|
+
},
|
|
339
|
+
screenData: {
|
|
340
|
+
type: "object",
|
|
341
|
+
description:
|
|
342
|
+
"Screen data including: description, elements (array of element names), actions (array of possible actions)",
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
required: ["appId", "screenName", "screenData"],
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: "save_successful_flow",
|
|
350
|
+
description:
|
|
351
|
+
"Save a successful test flow as a pattern for future reference. Call this after a test passes to help AI learn from successful patterns.",
|
|
352
|
+
inputSchema: {
|
|
353
|
+
type: "object",
|
|
354
|
+
properties: {
|
|
355
|
+
appId: {
|
|
356
|
+
type: "string",
|
|
357
|
+
description: "App package ID",
|
|
358
|
+
},
|
|
359
|
+
flowName: {
|
|
360
|
+
type: "string",
|
|
361
|
+
description: "Name for this flow pattern",
|
|
362
|
+
},
|
|
363
|
+
yamlContent: {
|
|
364
|
+
type: "string",
|
|
365
|
+
description: "The successful Maestro YAML content",
|
|
366
|
+
},
|
|
367
|
+
description: {
|
|
368
|
+
type: "string",
|
|
369
|
+
description: "Optional: Description of what this flow does",
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
required: ["appId", "flowName", "yamlContent"],
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: "get_saved_flows",
|
|
377
|
+
description:
|
|
378
|
+
"Get all saved successful flows for an app. Use these as patterns when generating new tests.",
|
|
379
|
+
inputSchema: {
|
|
380
|
+
type: "object",
|
|
381
|
+
properties: {
|
|
382
|
+
appId: {
|
|
383
|
+
type: "string",
|
|
384
|
+
description: "App package ID",
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
required: ["appId"],
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
name: "delete_flow",
|
|
392
|
+
description: "Delete a saved flow pattern.",
|
|
393
|
+
inputSchema: {
|
|
394
|
+
type: "object",
|
|
395
|
+
properties: {
|
|
396
|
+
appId: {
|
|
397
|
+
type: "string",
|
|
398
|
+
description: "App package ID",
|
|
399
|
+
},
|
|
400
|
+
flowName: {
|
|
401
|
+
type: "string",
|
|
402
|
+
description: "Name of the flow to delete",
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
required: ["appId", "flowName"],
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: "get_ai_context",
|
|
410
|
+
description:
|
|
411
|
+
"Get the formatted AI context for an app. This returns all registered elements, screens, and example flows in a format optimized for AI consumption. ALWAYS call this before generating Maestro YAML to get app-specific information.",
|
|
412
|
+
inputSchema: {
|
|
413
|
+
type: "object",
|
|
414
|
+
properties: {
|
|
415
|
+
appId: {
|
|
416
|
+
type: "string",
|
|
417
|
+
description: "App package ID",
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
required: ["appId"],
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: "get_full_context",
|
|
425
|
+
description:
|
|
426
|
+
"Get the complete raw app context including all elements, screens, and flows.",
|
|
427
|
+
inputSchema: {
|
|
428
|
+
type: "object",
|
|
429
|
+
properties: {
|
|
430
|
+
appId: {
|
|
431
|
+
type: "string",
|
|
432
|
+
description: "App package ID",
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
required: ["appId"],
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
name: "clear_app_context",
|
|
440
|
+
description: "Clear all saved context for an app (elements, screens, flows).",
|
|
441
|
+
inputSchema: {
|
|
442
|
+
type: "object",
|
|
443
|
+
properties: {
|
|
444
|
+
appId: {
|
|
445
|
+
type: "string",
|
|
446
|
+
description: "App package ID",
|
|
447
|
+
},
|
|
448
|
+
},
|
|
449
|
+
required: ["appId"],
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: "list_app_contexts",
|
|
454
|
+
description: "List all apps that have saved context data.",
|
|
455
|
+
inputSchema: {
|
|
456
|
+
type: "object",
|
|
457
|
+
properties: {},
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
];
|
|
461
|
+
|
|
462
|
+
// ============================================
|
|
463
|
+
// HANDLERS
|
|
464
|
+
// ============================================
|
|
465
|
+
|
|
466
|
+
// List available tools
|
|
467
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
468
|
+
logger.info("Listing available tools");
|
|
469
|
+
return { tools: TOOLS };
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// Handle tool calls
|
|
473
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
474
|
+
const { name, arguments: args } = request.params;
|
|
475
|
+
logger.info(`Tool called: ${name}`, { args });
|
|
476
|
+
|
|
477
|
+
try {
|
|
478
|
+
switch (name) {
|
|
479
|
+
// Prompt tools
|
|
480
|
+
case "read_prompt_file":
|
|
481
|
+
return await readPromptFile(args.file);
|
|
482
|
+
|
|
483
|
+
case "list_prompt_files":
|
|
484
|
+
return await listPromptFiles(args.directory);
|
|
485
|
+
|
|
486
|
+
// Device management tools
|
|
487
|
+
case "list_devices":
|
|
488
|
+
return await listDevices();
|
|
489
|
+
|
|
490
|
+
case "select_device":
|
|
491
|
+
return await selectDevice(args.deviceId);
|
|
492
|
+
|
|
493
|
+
case "clear_device":
|
|
494
|
+
return await clearDevice();
|
|
495
|
+
|
|
496
|
+
case "check_device":
|
|
497
|
+
return await checkDevice();
|
|
498
|
+
|
|
499
|
+
case "check_app":
|
|
500
|
+
return await checkApp(args.appId);
|
|
501
|
+
|
|
502
|
+
// Config tools
|
|
503
|
+
case "get_app_config":
|
|
504
|
+
return await getAppConfig();
|
|
505
|
+
|
|
506
|
+
// Validation tools
|
|
507
|
+
case "validate_maestro_yaml":
|
|
508
|
+
return await validateMaestroYaml(args.yaml);
|
|
509
|
+
|
|
510
|
+
// Execution tools
|
|
511
|
+
case "run_test":
|
|
512
|
+
return await runTest(args.yaml, args.name, { retries: args.retries });
|
|
513
|
+
|
|
514
|
+
case "run_test_suite":
|
|
515
|
+
return await runTestSuite(args.tests, { retries: args.retries });
|
|
516
|
+
|
|
517
|
+
// Results & utility tools
|
|
518
|
+
case "get_test_results":
|
|
519
|
+
return await getTestResults(args.runId);
|
|
520
|
+
|
|
521
|
+
case "take_screenshot":
|
|
522
|
+
return await takeScreenshot(args.name);
|
|
523
|
+
|
|
524
|
+
case "cleanup_results":
|
|
525
|
+
return await cleanupResults({
|
|
526
|
+
keepLast: args.keepLast,
|
|
527
|
+
deleteScreenshots: args.deleteScreenshots,
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// App context/training tools
|
|
531
|
+
case "register_elements":
|
|
532
|
+
return await registerAppElements(args.appId, args.elements);
|
|
533
|
+
|
|
534
|
+
case "register_screen":
|
|
535
|
+
return await registerAppScreen(args.appId, args.screenName, args.screenData);
|
|
536
|
+
|
|
537
|
+
case "save_successful_flow":
|
|
538
|
+
return await saveFlow(args.appId, args.flowName, args.yamlContent, args.description);
|
|
539
|
+
|
|
540
|
+
case "get_saved_flows":
|
|
541
|
+
return await getFlows(args.appId);
|
|
542
|
+
|
|
543
|
+
case "delete_flow":
|
|
544
|
+
return await removeFlow(args.appId, args.flowName);
|
|
545
|
+
|
|
546
|
+
case "get_ai_context":
|
|
547
|
+
return await getAIContext(args.appId);
|
|
548
|
+
|
|
549
|
+
case "get_full_context":
|
|
550
|
+
return await getAppContext(args.appId);
|
|
551
|
+
|
|
552
|
+
case "clear_app_context":
|
|
553
|
+
return await clearContext(args.appId);
|
|
554
|
+
|
|
555
|
+
case "list_app_contexts":
|
|
556
|
+
return await listContexts();
|
|
557
|
+
|
|
558
|
+
default:
|
|
559
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
560
|
+
}
|
|
561
|
+
} catch (error) {
|
|
562
|
+
logger.error(`Tool error: ${name}`, { error: error.message });
|
|
563
|
+
return {
|
|
564
|
+
content: [
|
|
565
|
+
{
|
|
566
|
+
type: "text",
|
|
567
|
+
text: JSON.stringify({
|
|
568
|
+
success: false,
|
|
569
|
+
error: error.message,
|
|
570
|
+
}),
|
|
571
|
+
},
|
|
572
|
+
],
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
// List available resources (prompt files)
|
|
578
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
579
|
+
const result = await listPromptFiles("prompts");
|
|
580
|
+
const files = JSON.parse(result.content[0].text).files || [];
|
|
581
|
+
|
|
582
|
+
return {
|
|
583
|
+
resources: files.map((file) => ({
|
|
584
|
+
uri: `prompts://${file}`,
|
|
585
|
+
name: file,
|
|
586
|
+
mimeType: "text/plain",
|
|
587
|
+
description: `Prompt file: ${file}`,
|
|
588
|
+
})),
|
|
589
|
+
};
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// Read a resource
|
|
593
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
594
|
+
const uri = request.params.uri;
|
|
595
|
+
const file = uri.replace("prompts://", "");
|
|
596
|
+
const result = await readPromptFile(`prompts/${file}`);
|
|
597
|
+
|
|
598
|
+
return {
|
|
599
|
+
contents: [
|
|
600
|
+
{
|
|
601
|
+
uri,
|
|
602
|
+
mimeType: "text/plain",
|
|
603
|
+
text: result.content[0].text,
|
|
604
|
+
},
|
|
605
|
+
],
|
|
606
|
+
};
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// ============================================
|
|
610
|
+
// START SERVER
|
|
611
|
+
// ============================================
|
|
612
|
+
|
|
613
|
+
async function main() {
|
|
614
|
+
logger.info("Starting MCP Maestro Mobile AI v1.1.0...");
|
|
615
|
+
|
|
616
|
+
const transport = new StdioServerTransport();
|
|
617
|
+
await server.connect(transport);
|
|
618
|
+
|
|
619
|
+
logger.info("MCP Maestro Mobile AI server running on stdio");
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
main().catch((error) => {
|
|
623
|
+
logger.error("Failed to start server", { error: error.message });
|
|
624
|
+
process.exit(1);
|
|
625
|
+
});
|