auto-feedback 0.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.
Files changed (90) hide show
  1. package/README.md +180 -0
  2. package/build/capture/console-collector.d.ts +16 -0
  3. package/build/capture/console-collector.js +43 -0
  4. package/build/capture/error-collector.d.ts +15 -0
  5. package/build/capture/error-collector.js +47 -0
  6. package/build/capture/network-collector.d.ts +16 -0
  7. package/build/capture/network-collector.js +76 -0
  8. package/build/capture/process-collector.d.ts +16 -0
  9. package/build/capture/process-collector.js +48 -0
  10. package/build/capture/types.d.ts +61 -0
  11. package/build/capture/types.js +5 -0
  12. package/build/index.d.ts +6 -0
  13. package/build/index.js +41 -0
  14. package/build/interaction/selectors.d.ts +26 -0
  15. package/build/interaction/selectors.js +84 -0
  16. package/build/interaction/types.d.ts +56 -0
  17. package/build/interaction/types.js +5 -0
  18. package/build/process/cleanup.d.ts +23 -0
  19. package/build/process/cleanup.js +50 -0
  20. package/build/process/launcher.d.ts +22 -0
  21. package/build/process/launcher.js +54 -0
  22. package/build/process/monitor.d.ts +14 -0
  23. package/build/process/monitor.js +67 -0
  24. package/build/process/types.d.ts +84 -0
  25. package/build/process/types.js +5 -0
  26. package/build/screenshot/auto-capture.d.ts +14 -0
  27. package/build/screenshot/auto-capture.js +38 -0
  28. package/build/screenshot/capture.d.ts +21 -0
  29. package/build/screenshot/capture.js +48 -0
  30. package/build/screenshot/optimize.d.ts +19 -0
  31. package/build/screenshot/optimize.js +28 -0
  32. package/build/screenshot/types.d.ts +43 -0
  33. package/build/screenshot/types.js +4 -0
  34. package/build/server.d.ts +10 -0
  35. package/build/server.js +18 -0
  36. package/build/session-manager.d.ts +119 -0
  37. package/build/session-manager.js +284 -0
  38. package/build/tools/check-port.d.ts +10 -0
  39. package/build/tools/check-port.js +40 -0
  40. package/build/tools/click-element.d.ts +13 -0
  41. package/build/tools/click-element.js +118 -0
  42. package/build/tools/get-console-logs.d.ts +7 -0
  43. package/build/tools/get-console-logs.js +55 -0
  44. package/build/tools/get-element-state.d.ts +14 -0
  45. package/build/tools/get-element-state.js +116 -0
  46. package/build/tools/get-errors.d.ts +7 -0
  47. package/build/tools/get-errors.js +40 -0
  48. package/build/tools/get-network-logs.d.ts +7 -0
  49. package/build/tools/get-network-logs.js +58 -0
  50. package/build/tools/get-process-output.d.ts +7 -0
  51. package/build/tools/get-process-output.js +55 -0
  52. package/build/tools/get-screenshot.d.ts +7 -0
  53. package/build/tools/get-screenshot.js +32 -0
  54. package/build/tools/index.d.ts +9 -0
  55. package/build/tools/index.js +117 -0
  56. package/build/tools/launch-electron.d.ts +13 -0
  57. package/build/tools/launch-electron.js +97 -0
  58. package/build/tools/launch-web-server.d.ts +13 -0
  59. package/build/tools/launch-web-server.js +88 -0
  60. package/build/tools/launch-windows-exe.d.ts +13 -0
  61. package/build/tools/launch-windows-exe.js +81 -0
  62. package/build/tools/navigate.d.ts +13 -0
  63. package/build/tools/navigate.js +137 -0
  64. package/build/tools/run-workflow.d.ts +14 -0
  65. package/build/tools/run-workflow.js +207 -0
  66. package/build/tools/screenshot-desktop.d.ts +13 -0
  67. package/build/tools/screenshot-desktop.js +80 -0
  68. package/build/tools/screenshot-electron.d.ts +13 -0
  69. package/build/tools/screenshot-electron.js +72 -0
  70. package/build/tools/screenshot-web.d.ts +13 -0
  71. package/build/tools/screenshot-web.js +129 -0
  72. package/build/tools/stop-process.d.ts +14 -0
  73. package/build/tools/stop-process.js +41 -0
  74. package/build/tools/type-text.d.ts +13 -0
  75. package/build/tools/type-text.js +137 -0
  76. package/build/tools/wait-for-element.d.ts +14 -0
  77. package/build/tools/wait-for-element.js +93 -0
  78. package/build/types/index.d.ts +31 -0
  79. package/build/types/index.js +4 -0
  80. package/build/utils/errors.d.ts +26 -0
  81. package/build/utils/errors.js +62 -0
  82. package/build/utils/shutdown.d.ts +16 -0
  83. package/build/utils/shutdown.js +34 -0
  84. package/build/workflow/assertions.d.ts +25 -0
  85. package/build/workflow/assertions.js +326 -0
  86. package/build/workflow/executor.d.ts +34 -0
  87. package/build/workflow/executor.js +269 -0
  88. package/build/workflow/types.d.ts +95 -0
  89. package/build/workflow/types.js +6 -0
  90. package/package.json +36 -0
@@ -0,0 +1,55 @@
1
+ /**
2
+ * MCP Tool 19: get_console_logs
3
+ * Retrieves browser console logs captured during a session.
4
+ */
5
+ import { z } from "zod";
6
+ import { createToolError, createToolResult } from "../utils/errors.js";
7
+ export function registerGetConsoleLogsTool(server, sessionManager) {
8
+ server.tool("get_console_logs", "Get browser console logs captured during the session. Use to check for warnings, errors, or debug output from web apps.", {
9
+ sessionId: z.string().describe("Session ID to get logs from"),
10
+ level: z
11
+ .enum(["all", "log", "error", "warning", "info", "debug"])
12
+ .optional()
13
+ .describe("Filter by log level (default: all)"),
14
+ limit: z
15
+ .number()
16
+ .int()
17
+ .min(1)
18
+ .max(1000)
19
+ .optional()
20
+ .describe("Max entries to return, most recent first (default: 100)"),
21
+ }, async ({ sessionId, level, limit }) => {
22
+ const session = sessionManager.get(sessionId);
23
+ if (!session) {
24
+ return createToolError(`Session not found: ${sessionId}`, "The session may have already been ended or never existed", "Create a session first with create_session.");
25
+ }
26
+ const collectors = sessionManager.getConsoleCollectors(sessionId);
27
+ if (collectors.length === 0) {
28
+ return createToolResult({
29
+ count: 0,
30
+ truncated: false,
31
+ entries: [],
32
+ note: "No console logs captured yet — ensure a page exists in this session",
33
+ });
34
+ }
35
+ // Aggregate entries from all collectors
36
+ let entries = collectors.flatMap((c) => [...c.getEntries()]);
37
+ // Filter by level if specified
38
+ if (level && level !== "all") {
39
+ entries = entries.filter((entry) => entry.level === level);
40
+ }
41
+ // Sort by timestamp descending (newest first)
42
+ entries.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
43
+ // Apply limit
44
+ const maxEntries = limit ?? 100;
45
+ const truncated = entries.length > maxEntries;
46
+ const totalCount = entries.length;
47
+ entries = entries.slice(0, maxEntries);
48
+ return createToolResult({
49
+ count: entries.length,
50
+ totalAvailable: totalCount,
51
+ truncated,
52
+ entries,
53
+ });
54
+ });
55
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * get_element_state MCP tool
3
+ * Reads comprehensive UI element state in a single call: text, visibility,
4
+ * enabled/editable status, attributes, and bounding box.
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { SessionManager } from "../session-manager.js";
8
+ /**
9
+ * Register the get_element_state tool with the MCP server
10
+ *
11
+ * @param server - MCP server instance
12
+ * @param sessionManager - Session manager for resource tracking
13
+ */
14
+ export declare function registerGetElementStateTool(server: McpServer, sessionManager: SessionManager): void;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * get_element_state MCP tool
3
+ * Reads comprehensive UI element state in a single call: text, visibility,
4
+ * enabled/editable status, attributes, and bounding box.
5
+ */
6
+ import { z } from "zod";
7
+ import { createToolError, createToolResult } from "../utils/errors.js";
8
+ import { resolveSelector, getActivePage } from "../interaction/selectors.js";
9
+ /**
10
+ * Register the get_element_state tool with the MCP server
11
+ *
12
+ * @param server - MCP server instance
13
+ * @param sessionManager - Session manager for resource tracking
14
+ */
15
+ export function registerGetElementStateTool(server, sessionManager) {
16
+ server.tool("get_element_state", "Read element state: text, visibility, enabled, attributes. Use to verify UI state without taking a screenshot.", {
17
+ sessionId: z
18
+ .string()
19
+ .describe("Session ID from create_session"),
20
+ selector: z
21
+ .string()
22
+ .describe("Element selector. CSS: #id, .class, div > span. Text: text=Click me. Role: role=button[name='Submit']. Test ID: testid=my-btn"),
23
+ pageIdentifier: z
24
+ .string()
25
+ .optional()
26
+ .describe("URL or 'electron' to target a specific page. Omit if session has only one page."),
27
+ attributes: z
28
+ .array(z.string())
29
+ .optional()
30
+ .describe("Attribute names to read (e.g., ['href', 'class', 'aria-label']). Omit to skip attribute reading."),
31
+ timeout: z
32
+ .number()
33
+ .int()
34
+ .min(0)
35
+ .optional()
36
+ .describe("Max wait time in ms (default: 30000)"),
37
+ }, async ({ sessionId, selector, pageIdentifier, attributes, timeout }) => {
38
+ try {
39
+ // Validate session exists
40
+ const session = sessionManager.get(sessionId);
41
+ if (!session) {
42
+ const availableSessions = sessionManager.list();
43
+ return createToolError(`Session not found: ${sessionId}`, "The session may have already been ended", availableSessions.length > 0
44
+ ? `Available sessions: ${availableSessions.join(", ")}`
45
+ : "Create a session first with create_session.");
46
+ }
47
+ // Find the active page
48
+ const pageResult = getActivePage(sessionManager, sessionId, pageIdentifier);
49
+ if (!pageResult.success) {
50
+ return createToolError(pageResult.error, `Session: ${sessionId}`, pageResult.availablePages
51
+ ? `Available pages: ${pageResult.availablePages.join(", ")}`
52
+ : undefined);
53
+ }
54
+ const { page } = pageResult;
55
+ const effectiveTimeout = timeout ?? 30000;
56
+ // Resolve selector to Playwright Locator
57
+ const locator = resolveSelector(page, selector);
58
+ // Auto-wait gate: ensure element is in DOM before reading state
59
+ await locator.waitFor({
60
+ state: "attached",
61
+ timeout: effectiveTimeout,
62
+ });
63
+ // Read core state properties in parallel
64
+ const [text, innerTextVal, visible, enabled, editable] = await Promise.all([
65
+ locator.textContent({ timeout: effectiveTimeout }),
66
+ locator.innerText({ timeout: effectiveTimeout }),
67
+ locator.isVisible(),
68
+ locator.isEnabled({ timeout: effectiveTimeout }),
69
+ locator.isEditable({ timeout: effectiveTimeout }).catch(() => false),
70
+ ]);
71
+ // Read optional properties (return null for non-applicable elements)
72
+ const [inputVal, checked] = await Promise.all([
73
+ locator.inputValue({ timeout: effectiveTimeout }).catch(() => null),
74
+ locator.isChecked({ timeout: effectiveTimeout }).catch(() => null),
75
+ ]);
76
+ // Read bounding box (null if element not visible)
77
+ const box = await locator.boundingBox();
78
+ // Read requested attributes
79
+ const attrs = {};
80
+ if (attributes) {
81
+ for (const name of attributes) {
82
+ attrs[name] = await locator.getAttribute(name, {
83
+ timeout: effectiveTimeout,
84
+ });
85
+ }
86
+ }
87
+ // Return structured state (text-only, no screenshot)
88
+ return createToolResult({
89
+ selector,
90
+ visible,
91
+ enabled,
92
+ editable,
93
+ checked,
94
+ textContent: text,
95
+ innerText: innerTextVal,
96
+ inputValue: inputVal,
97
+ attributes: attrs,
98
+ boundingBox: box,
99
+ });
100
+ }
101
+ catch (error) {
102
+ const message = error instanceof Error ? error.message : String(error);
103
+ // Strict mode violation: selector matched multiple elements
104
+ if (message.includes("strict mode violation")) {
105
+ return createToolError("Selector matched multiple elements", `Selector "${selector}" matched more than one element (strict mode violation)`, "Use a more specific selector or add :nth-child(), :first-of-type, or similar to target a single element.");
106
+ }
107
+ // Timeout: element not found or not attached within timeout
108
+ if (message.includes("Timeout") ||
109
+ message.includes("timeout")) {
110
+ return createToolError("Element not found within timeout", `Selector "${selector}" did not match any element within ${timeout ?? 30000}ms`, "Check the selector is correct, the element exists in the DOM, or increase the timeout. Take a screenshot first to verify the page state.");
111
+ }
112
+ // Default error
113
+ return createToolError("Failed to read element state", `Selector: "${selector}" — ${message}`, "Take a screenshot to verify the element exists and is visible on the page.");
114
+ }
115
+ });
116
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * MCP Tool 20: get_errors
3
+ * Retrieves runtime errors and page crashes captured during a session.
4
+ */
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { SessionManager } from "../session-manager.js";
7
+ export declare function registerGetErrorsTool(server: McpServer, sessionManager: SessionManager): void;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * MCP Tool 20: get_errors
3
+ * Retrieves runtime errors and page crashes captured during a session.
4
+ */
5
+ import { z } from "zod";
6
+ import { createToolError, createToolResult } from "../utils/errors.js";
7
+ export function registerGetErrorsTool(server, sessionManager) {
8
+ server.tool("get_errors", "Get runtime errors and page crashes captured during the session. Use to diagnose uncaught exceptions or page crashes in web apps.", {
9
+ sessionId: z.string().describe("Session ID to get errors from"),
10
+ type: z
11
+ .enum(["all", "uncaught-exception", "page-crash"])
12
+ .optional()
13
+ .describe("Filter by error type (default: all)"),
14
+ }, async ({ sessionId, type }) => {
15
+ const session = sessionManager.get(sessionId);
16
+ if (!session) {
17
+ return createToolError(`Session not found: ${sessionId}`, "The session may have already been ended or never existed", "Create a session first with create_session.");
18
+ }
19
+ const collectors = sessionManager.getErrorCollectors(sessionId);
20
+ if (collectors.length === 0) {
21
+ return createToolResult({
22
+ count: 0,
23
+ entries: [],
24
+ note: "No errors captured yet — ensure a page exists in this session",
25
+ });
26
+ }
27
+ // Aggregate entries from all collectors
28
+ let entries = collectors.flatMap((c) => [...c.getEntries()]);
29
+ // Filter by type if specified
30
+ if (type && type !== "all") {
31
+ entries = entries.filter((entry) => entry.type === type);
32
+ }
33
+ // Sort by timestamp descending (newest first)
34
+ entries.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
35
+ return createToolResult({
36
+ count: entries.length,
37
+ entries,
38
+ });
39
+ });
40
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * MCP Tool 21: get_network_logs
3
+ * Retrieves HTTP request/response logs captured during a session.
4
+ */
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { SessionManager } from "../session-manager.js";
7
+ export declare function registerGetNetworkLogsTool(server: McpServer, sessionManager: SessionManager): void;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * MCP Tool 21: get_network_logs
3
+ * Retrieves HTTP request/response logs captured during a session.
4
+ */
5
+ import { z } from "zod";
6
+ import { createToolError, createToolResult } from "../utils/errors.js";
7
+ export function registerGetNetworkLogsTool(server, sessionManager) {
8
+ server.tool("get_network_logs", "Get HTTP request/response logs captured during the session. Use to inspect API calls, failed requests, or slow responses in web apps.", {
9
+ sessionId: z.string().describe("Session ID to get network logs from"),
10
+ filter: z
11
+ .enum(["all", "failed", "errors"])
12
+ .optional()
13
+ .describe("Filter: 'failed' = status 0 (network errors), 'errors' = status >= 400 or 0 (default: all)"),
14
+ limit: z
15
+ .number()
16
+ .int()
17
+ .min(1)
18
+ .max(500)
19
+ .optional()
20
+ .describe("Max entries to return, most recent first (default: 100)"),
21
+ }, async ({ sessionId, filter, limit }) => {
22
+ const session = sessionManager.get(sessionId);
23
+ if (!session) {
24
+ return createToolError(`Session not found: ${sessionId}`, "The session may have already been ended or never existed", "Create a session first with create_session.");
25
+ }
26
+ const collectors = sessionManager.getNetworkCollectors(sessionId);
27
+ if (collectors.length === 0) {
28
+ return createToolResult({
29
+ count: 0,
30
+ truncated: false,
31
+ entries: [],
32
+ note: "No network logs captured yet — ensure a page exists in this session",
33
+ });
34
+ }
35
+ // Aggregate entries from all collectors
36
+ let entries = collectors.flatMap((c) => [...c.getEntries()]);
37
+ // Apply filter
38
+ if (filter === "failed") {
39
+ entries = entries.filter((entry) => entry.status === 0);
40
+ }
41
+ else if (filter === "errors") {
42
+ entries = entries.filter((entry) => entry.status === 0 || entry.status >= 400);
43
+ }
44
+ // Sort by timestamp descending (newest first)
45
+ entries.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
46
+ // Apply limit
47
+ const maxEntries = limit ?? 100;
48
+ const truncated = entries.length > maxEntries;
49
+ const totalCount = entries.length;
50
+ entries = entries.slice(0, maxEntries);
51
+ return createToolResult({
52
+ count: entries.length,
53
+ totalAvailable: totalCount,
54
+ truncated,
55
+ entries,
56
+ });
57
+ });
58
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * MCP Tool 22: get_process_output
3
+ * Retrieves stdout/stderr output from spawned processes in a session.
4
+ */
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { SessionManager } from "../session-manager.js";
7
+ export declare function registerGetProcessOutputTool(server: McpServer, sessionManager: SessionManager): void;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * MCP Tool 22: get_process_output
3
+ * Retrieves stdout/stderr output from spawned processes in a session.
4
+ */
5
+ import { z } from "zod";
6
+ import { createToolError, createToolResult } from "../utils/errors.js";
7
+ export function registerGetProcessOutputTool(server, sessionManager) {
8
+ server.tool("get_process_output", "Get stdout/stderr output from spawned processes in the session. Use to check dev server logs, build errors, or runtime output.", {
9
+ sessionId: z.string().describe("Session ID to get output from"),
10
+ stream: z
11
+ .enum(["all", "stdout", "stderr"])
12
+ .optional()
13
+ .describe("Filter by output stream (default: all)"),
14
+ limit: z
15
+ .number()
16
+ .int()
17
+ .min(1)
18
+ .max(5000)
19
+ .optional()
20
+ .describe("Max lines to return, most recent first (default: 200)"),
21
+ }, async ({ sessionId, stream, limit }) => {
22
+ const session = sessionManager.get(sessionId);
23
+ if (!session) {
24
+ return createToolError(`Session not found: ${sessionId}`, "The session may have already been ended or never existed", "Create a session first with create_session.");
25
+ }
26
+ const collectors = sessionManager.getProcessCollectors(sessionId);
27
+ if (collectors.length === 0) {
28
+ return createToolResult({
29
+ count: 0,
30
+ truncated: false,
31
+ entries: [],
32
+ note: "No process output captured yet — ensure a process exists in this session",
33
+ });
34
+ }
35
+ // Aggregate entries from all collectors
36
+ let entries = collectors.flatMap((c) => [...c.getEntries()]);
37
+ // Filter by stream if specified
38
+ if (stream && stream !== "all") {
39
+ entries = entries.filter((entry) => entry.stream === stream);
40
+ }
41
+ // Sort by timestamp descending (newest first)
42
+ entries.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
43
+ // Apply limit
44
+ const maxEntries = limit ?? 200;
45
+ const truncated = entries.length > maxEntries;
46
+ const totalCount = entries.length;
47
+ entries = entries.slice(0, maxEntries);
48
+ return createToolResult({
49
+ count: entries.length,
50
+ totalAvailable: totalCount,
51
+ truncated,
52
+ entries,
53
+ });
54
+ });
55
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * get_screenshot MCP tool
3
+ * Retrieves the most recent auto-captured screenshot for a session
4
+ */
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { SessionManager } from "../session-manager.js";
7
+ export declare function registerGetScreenshotTool(server: McpServer, sessionManager: SessionManager): void;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * get_screenshot MCP tool
3
+ * Retrieves the most recent auto-captured screenshot for a session
4
+ */
5
+ import { z } from "zod";
6
+ import { createToolError, createScreenshotResult } from "../utils/errors.js";
7
+ export function registerGetScreenshotTool(server, sessionManager) {
8
+ server.tool("get_screenshot", "Get the most recent auto-captured screenshot for a session. Use to check current visual state without triggering a new capture.", {
9
+ sessionId: z.string().describe("Session ID to get the screenshot for"),
10
+ }, async ({ sessionId }) => {
11
+ try {
12
+ const session = sessionManager.get(sessionId);
13
+ if (!session) {
14
+ return createToolError(`Session not found: ${sessionId}`, "The session may have already been ended", "Create a session first with create_session.");
15
+ }
16
+ const autoCapture = sessionManager.getAutoCapture(sessionId);
17
+ if (!autoCapture) {
18
+ return createToolError("No auto-captured screenshot available", `Session ${sessionId} has no auto-capture yet. Auto-captures are taken after page navigations in web and Electron apps.`, "Use screenshot_web, screenshot_electron, or screenshot_desktop for an on-demand capture. Or navigate the app to trigger an auto-capture.");
19
+ }
20
+ return createScreenshotResult({
21
+ sessionId,
22
+ source: "auto-capture",
23
+ url: autoCapture.url,
24
+ capturedAt: autoCapture.capturedAt.toISOString(),
25
+ }, autoCapture.imageBase64, autoCapture.mimeType);
26
+ }
27
+ catch (error) {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ return createToolError("Failed to retrieve screenshot", message, "Check the session ID is valid.");
30
+ }
31
+ });
32
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * MCP tool registration
3
+ */
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { SessionManager } from "../session-manager.js";
6
+ /**
7
+ * Register all MCP tools with the server
8
+ */
9
+ export declare function registerTools(server: McpServer, sessionManager: SessionManager): void;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * MCP tool registration
3
+ */
4
+ import { z } from "zod";
5
+ import { createToolError, createToolResult } from "../utils/errors.js";
6
+ import { registerCheckPortTool } from "./check-port.js";
7
+ import { registerLaunchWebServerTool } from "./launch-web-server.js";
8
+ import { registerLaunchElectronTool } from "./launch-electron.js";
9
+ import { registerLaunchWindowsExeTool } from "./launch-windows-exe.js";
10
+ import { registerStopProcessTool } from "./stop-process.js";
11
+ import { registerScreenshotWebTool } from "./screenshot-web.js";
12
+ import { registerScreenshotElectronTool } from "./screenshot-electron.js";
13
+ import { registerScreenshotDesktopTool } from "./screenshot-desktop.js";
14
+ import { registerGetScreenshotTool } from "./get-screenshot.js";
15
+ import { registerClickElementTool } from "./click-element.js";
16
+ import { registerTypeTextTool } from "./type-text.js";
17
+ import { registerNavigateTool } from "./navigate.js";
18
+ import { registerGetElementStateTool } from "./get-element-state.js";
19
+ import { registerWaitForElementTool } from "./wait-for-element.js";
20
+ import { registerGetConsoleLogsTool } from "./get-console-logs.js";
21
+ import { registerGetErrorsTool } from "./get-errors.js";
22
+ import { registerGetNetworkLogsTool } from "./get-network-logs.js";
23
+ import { registerGetProcessOutputTool } from "./get-process-output.js";
24
+ import { registerRunWorkflowTool } from "./run-workflow.js";
25
+ /**
26
+ * Register all MCP tools with the server
27
+ */
28
+ export function registerTools(server, sessionManager) {
29
+ // Tool 1: get_version
30
+ server.tool("get_version", "Get server version and capabilities. Use to verify the server is running and responsive.", {}, async () => {
31
+ return createToolResult({
32
+ name: "feedback",
33
+ version: "0.1.0",
34
+ status: "ready",
35
+ capabilities: [
36
+ "process_lifecycle",
37
+ "screenshots",
38
+ "interactions",
39
+ "error_capture",
40
+ "workflows",
41
+ "assertions",
42
+ ],
43
+ });
44
+ });
45
+ // Tool 2: create_session
46
+ server.tool("create_session", "Create a new session for tracking app resources. Returns a unique session ID used by other tools.", {}, async () => {
47
+ const sessionId = sessionManager.create();
48
+ return createToolResult({
49
+ sessionId,
50
+ created: true,
51
+ });
52
+ });
53
+ // Tool 3: list_sessions
54
+ server.tool("list_sessions", "List all active session IDs. Use to find existing sessions or check server state.", {}, async () => {
55
+ const sessions = sessionManager.list();
56
+ return createToolResult({
57
+ sessions,
58
+ count: sessions.length,
59
+ });
60
+ });
61
+ // Tool 4: end_session
62
+ server.tool("end_session", "End a session and clean up all its resources. Use when done testing an application.", {
63
+ sessionId: z.string().describe("The session ID to end"),
64
+ }, async ({ sessionId }) => {
65
+ const session = sessionManager.get(sessionId);
66
+ if (!session) {
67
+ const availableSessions = sessionManager.list();
68
+ return createToolError(`Session not found: ${sessionId}`, "The session may have already been ended or never existed", availableSessions.length > 0
69
+ ? `Available sessions: ${availableSessions.join(", ")}`
70
+ : "No active sessions. Create one with create_session first.");
71
+ }
72
+ await sessionManager.destroy(sessionId);
73
+ return createToolResult({
74
+ sessionId,
75
+ ended: true,
76
+ });
77
+ });
78
+ // Tool 5: check_port
79
+ registerCheckPortTool(server, sessionManager);
80
+ // Tool 6: launch_web_server
81
+ registerLaunchWebServerTool(server, sessionManager);
82
+ // Tool 7: launch_electron
83
+ registerLaunchElectronTool(server, sessionManager);
84
+ // Tool 8: launch_windows_exe
85
+ registerLaunchWindowsExeTool(server, sessionManager);
86
+ // Tool 9: stop_process
87
+ registerStopProcessTool(server, sessionManager);
88
+ // Tool 10: screenshot_web
89
+ registerScreenshotWebTool(server, sessionManager);
90
+ // Tool 11: screenshot_electron
91
+ registerScreenshotElectronTool(server, sessionManager);
92
+ // Tool 12: screenshot_desktop
93
+ registerScreenshotDesktopTool(server, sessionManager);
94
+ // Tool 13: get_screenshot
95
+ registerGetScreenshotTool(server, sessionManager);
96
+ // Tool 14: click_element
97
+ registerClickElementTool(server, sessionManager);
98
+ // Tool 15: type_text
99
+ registerTypeTextTool(server, sessionManager);
100
+ // Tool 16: navigate
101
+ registerNavigateTool(server, sessionManager);
102
+ // Tool 17: get_element_state
103
+ registerGetElementStateTool(server, sessionManager);
104
+ // Tool 18: wait_for_element
105
+ registerWaitForElementTool(server, sessionManager);
106
+ // Tool 19: get_console_logs
107
+ registerGetConsoleLogsTool(server, sessionManager);
108
+ // Tool 20: get_errors
109
+ registerGetErrorsTool(server, sessionManager);
110
+ // Tool 21: get_network_logs
111
+ registerGetNetworkLogsTool(server, sessionManager);
112
+ // Tool 22: get_process_output
113
+ registerGetProcessOutputTool(server, sessionManager);
114
+ // Tool 23: run_workflow
115
+ registerRunWorkflowTool(server, sessionManager);
116
+ console.error("Registered 23 MCP tools");
117
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * launch_electron MCP tool (PROC-02)
3
+ * Launches an Electron app via Playwright for automation
4
+ */
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { SessionManager } from "../session-manager.js";
7
+ /**
8
+ * Register the launch_electron tool with the MCP server
9
+ *
10
+ * @param server - MCP server instance
11
+ * @param sessionManager - Session manager for resource tracking
12
+ */
13
+ export declare function registerLaunchElectronTool(server: McpServer, sessionManager: SessionManager): void;
@@ -0,0 +1,97 @@
1
+ /**
2
+ * launch_electron MCP tool (PROC-02)
3
+ * Launches an Electron app via Playwright for automation
4
+ */
5
+ import path from "path";
6
+ import { z } from "zod";
7
+ import { _electron as electron } from "playwright";
8
+ import { createToolError, createToolResult } from "../utils/errors.js";
9
+ import { setupAutoCapture } from "../screenshot/auto-capture.js";
10
+ import { attachConsoleCollector } from "../capture/console-collector.js";
11
+ import { attachErrorCollector } from "../capture/error-collector.js";
12
+ import { attachNetworkCollector } from "../capture/network-collector.js";
13
+ /**
14
+ * Register the launch_electron tool with the MCP server
15
+ *
16
+ * @param server - MCP server instance
17
+ * @param sessionManager - Session manager for resource tracking
18
+ */
19
+ export function registerLaunchElectronTool(server, sessionManager) {
20
+ server.tool("launch_electron", "Launch an Electron app via Playwright for automation. Use to start and instrument Electron desktop applications.", {
21
+ sessionId: z.string().describe("Session ID to track this app"),
22
+ entryPath: z
23
+ .string()
24
+ .describe("Path to Electron main entry file (e.g., main.js, index.js)"),
25
+ cwd: z
26
+ .string()
27
+ .optional()
28
+ .describe("Working directory (defaults to entry file directory)"),
29
+ timeoutMs: z
30
+ .number()
31
+ .int()
32
+ .min(1000)
33
+ .max(120000)
34
+ .optional()
35
+ .describe("Launch timeout in ms (default: 30000)"),
36
+ }, async ({ sessionId, entryPath, cwd, timeoutMs }) => {
37
+ try {
38
+ // Validate session exists
39
+ const session = sessionManager.get(sessionId);
40
+ if (!session) {
41
+ return createToolError(`Session not found: ${sessionId}`, "The session may have already been ended or never existed", "Create a session first with create_session.");
42
+ }
43
+ console.error(`[launch_electron] Launching Electron: ${entryPath}`);
44
+ // Resolve paths
45
+ const resolvedEntryPath = path.resolve(entryPath);
46
+ const resolvedCwd = cwd
47
+ ? path.resolve(cwd)
48
+ : path.dirname(resolvedEntryPath);
49
+ // Launch Electron via Playwright
50
+ const electronApp = await electron.launch({
51
+ args: [resolvedEntryPath],
52
+ cwd: resolvedCwd,
53
+ timeout: timeoutMs ?? 30000,
54
+ });
55
+ // Wait for the first window to appear
56
+ const window = await electronApp.firstWindow();
57
+ console.error(`[launch_electron] Window detected, app ready`);
58
+ // Store page reference for screenshot access
59
+ sessionManager.setPageRef(sessionId, "electron", {
60
+ type: "electron",
61
+ page: window,
62
+ electronApp,
63
+ });
64
+ // Attach auto-capture on navigation events
65
+ const removeAutoCapture = setupAutoCapture(window, sessionId, sessionManager);
66
+ // Attach diagnostic collectors
67
+ const consoleCollector = attachConsoleCollector(window);
68
+ const errorCollector = attachErrorCollector(window);
69
+ const networkCollector = attachNetworkCollector(window);
70
+ sessionManager.setConsoleCollector(sessionId, "electron", consoleCollector);
71
+ sessionManager.setErrorCollector(sessionId, "electron", errorCollector);
72
+ sessionManager.setNetworkCollector(sessionId, "electron", networkCollector);
73
+ // Create a resource that cleans up the Electron app
74
+ const resource = {
75
+ cleanup: async () => {
76
+ console.error(`[launch_electron] Closing Electron app`);
77
+ removeAutoCapture();
78
+ sessionManager.removePageRef(sessionId, "electron");
79
+ await electronApp.close();
80
+ console.error(`[launch_electron] Electron app closed`);
81
+ },
82
+ };
83
+ sessionManager.addResource(sessionId, resource);
84
+ return createToolResult({
85
+ sessionId,
86
+ type: "electron",
87
+ status: "ready",
88
+ entryPath: resolvedEntryPath,
89
+ windowTitle: await window.title(),
90
+ });
91
+ }
92
+ catch (error) {
93
+ const message = error instanceof Error ? error.message : String(error);
94
+ return createToolError("Failed to launch Electron app", message, "Check the entry path points to a valid Electron main file. Ensure Electron is installed in the target project.");
95
+ }
96
+ });
97
+ }