@everydaydevopsio/pilot 0.5.2 → 0.6.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.
Files changed (306) hide show
  1. package/.mcp.json +9 -0
  2. package/AGENTS.md +9 -0
  3. package/CHANGELOG.md +64 -0
  4. package/CLAUDE.md +7 -7
  5. package/README.md +282 -69
  6. package/dist/browser/browser-manager.d.ts +60 -0
  7. package/dist/browser/browser-manager.d.ts.map +1 -0
  8. package/dist/browser/browser-manager.js +355 -0
  9. package/dist/browser/browser-manager.js.map +1 -0
  10. package/dist/browser/chrome-launcher.d.ts +31 -0
  11. package/dist/browser/chrome-launcher.d.ts.map +1 -0
  12. package/dist/browser/chrome-launcher.js +225 -0
  13. package/dist/browser/chrome-launcher.js.map +1 -0
  14. package/dist/browser/connection.d.ts +23 -0
  15. package/dist/browser/connection.d.ts.map +1 -0
  16. package/dist/browser/connection.js +53 -0
  17. package/dist/browser/connection.js.map +1 -0
  18. package/dist/browser/events.d.ts +9 -0
  19. package/dist/browser/events.d.ts.map +1 -0
  20. package/dist/browser/events.js +99 -0
  21. package/dist/browser/events.js.map +1 -0
  22. package/dist/browser/index.d.ts +4 -0
  23. package/dist/browser/index.d.ts.map +1 -0
  24. package/dist/browser/index.js +4 -0
  25. package/dist/browser/index.js.map +1 -0
  26. package/dist/browser/inspect/element-ref.d.ts +23 -0
  27. package/dist/browser/inspect/element-ref.d.ts.map +1 -0
  28. package/dist/browser/inspect/element-ref.js +52 -0
  29. package/dist/browser/inspect/element-ref.js.map +1 -0
  30. package/dist/browser/inspect/snapshot.d.ts +11 -0
  31. package/dist/browser/inspect/snapshot.d.ts.map +1 -0
  32. package/dist/browser/inspect/snapshot.js +282 -0
  33. package/dist/browser/inspect/snapshot.js.map +1 -0
  34. package/dist/browser/inspect/styles.d.ts +45 -0
  35. package/dist/browser/inspect/styles.d.ts.map +1 -0
  36. package/dist/browser/inspect/styles.js +190 -0
  37. package/dist/browser/inspect/styles.js.map +1 -0
  38. package/dist/browser/inspect/types.d.ts +35 -0
  39. package/dist/browser/inspect/types.d.ts.map +1 -0
  40. package/dist/browser/inspect/types.js +2 -0
  41. package/dist/browser/inspect/types.js.map +1 -0
  42. package/dist/browser/interaction/dialogs.d.ts +18 -0
  43. package/dist/browser/interaction/dialogs.d.ts.map +1 -0
  44. package/dist/browser/interaction/dialogs.js +35 -0
  45. package/dist/browser/interaction/dialogs.js.map +1 -0
  46. package/dist/browser/interaction/downloads.d.ts +23 -0
  47. package/dist/browser/interaction/downloads.d.ts.map +1 -0
  48. package/dist/browser/interaction/downloads.js +87 -0
  49. package/dist/browser/interaction/downloads.js.map +1 -0
  50. package/dist/browser/interaction/events.d.ts +17 -0
  51. package/dist/browser/interaction/events.d.ts.map +1 -0
  52. package/dist/browser/interaction/events.js +66 -0
  53. package/dist/browser/interaction/events.js.map +1 -0
  54. package/dist/browser/interaction/ref-resolver.d.ts +18 -0
  55. package/dist/browser/interaction/ref-resolver.d.ts.map +1 -0
  56. package/dist/browser/interaction/ref-resolver.js +32 -0
  57. package/dist/browser/interaction/ref-resolver.js.map +1 -0
  58. package/dist/browser/interaction/uploads.d.ts +4 -0
  59. package/dist/browser/interaction/uploads.d.ts.map +1 -0
  60. package/dist/browser/interaction/uploads.js +43 -0
  61. package/dist/browser/interaction/uploads.js.map +1 -0
  62. package/dist/browser/network/network-buffer.d.ts +19 -0
  63. package/dist/browser/network/network-buffer.d.ts.map +1 -0
  64. package/dist/browser/network/network-buffer.js +118 -0
  65. package/dist/browser/network/network-buffer.js.map +1 -0
  66. package/dist/browser/network/network-monitor.d.ts +8 -0
  67. package/dist/browser/network/network-monitor.d.ts.map +1 -0
  68. package/dist/browser/network/network-monitor.js +85 -0
  69. package/dist/browser/network/network-monitor.js.map +1 -0
  70. package/dist/browser/network/redaction.d.ts +3 -0
  71. package/dist/browser/network/redaction.d.ts.map +1 -0
  72. package/dist/browser/network/redaction.js +36 -0
  73. package/dist/browser/network/redaction.js.map +1 -0
  74. package/dist/browser/network/types.d.ts +45 -0
  75. package/dist/browser/network/types.d.ts.map +1 -0
  76. package/dist/browser/network/types.js +2 -0
  77. package/dist/browser/network/types.js.map +1 -0
  78. package/dist/browser/performance/analyze.d.ts +5 -0
  79. package/dist/browser/performance/analyze.d.ts.map +1 -0
  80. package/dist/browser/performance/analyze.js +230 -0
  81. package/dist/browser/performance/analyze.js.map +1 -0
  82. package/dist/browser/performance/trace.d.ts +10 -0
  83. package/dist/browser/performance/trace.d.ts.map +1 -0
  84. package/dist/browser/performance/trace.js +38 -0
  85. package/dist/browser/performance/trace.js.map +1 -0
  86. package/dist/browser/performance/types.d.ts +31 -0
  87. package/dist/browser/performance/types.d.ts.map +1 -0
  88. package/dist/browser/performance/types.js +2 -0
  89. package/dist/browser/performance/types.js.map +1 -0
  90. package/dist/browser/preflight.d.ts +20 -0
  91. package/dist/browser/preflight.d.ts.map +1 -0
  92. package/dist/browser/preflight.js +105 -0
  93. package/dist/browser/preflight.js.map +1 -0
  94. package/dist/browser/security/origins.d.ts +18 -0
  95. package/dist/browser/security/origins.d.ts.map +1 -0
  96. package/dist/browser/security/origins.js +84 -0
  97. package/dist/browser/security/origins.js.map +1 -0
  98. package/dist/browser/tabs.d.ts +15 -0
  99. package/dist/browser/tabs.d.ts.map +1 -0
  100. package/dist/browser/tabs.js +71 -0
  101. package/dist/browser/tabs.js.map +1 -0
  102. package/dist/browser/types.d.ts +32 -0
  103. package/dist/browser/types.d.ts.map +1 -0
  104. package/dist/browser/types.js +2 -0
  105. package/dist/browser/types.js.map +1 -0
  106. package/dist/browser.d.ts +2 -99
  107. package/dist/browser.d.ts.map +1 -1
  108. package/dist/browser.js +3 -653
  109. package/dist/browser.js.map +1 -1
  110. package/dist/cli/init.d.ts +3 -0
  111. package/dist/cli/init.d.ts.map +1 -1
  112. package/dist/cli/init.js +33 -48
  113. package/dist/cli/init.js.map +1 -1
  114. package/dist/cli/linux-check.d.ts +2 -0
  115. package/dist/cli/linux-check.d.ts.map +1 -0
  116. package/dist/cli/linux-check.js +20 -0
  117. package/dist/cli/linux-check.js.map +1 -0
  118. package/dist/cli/skill-template.d.ts +1 -1
  119. package/dist/cli/skill-template.d.ts.map +1 -1
  120. package/dist/cli/skill-template.js +3 -113
  121. package/dist/cli/skill-template.js.map +1 -1
  122. package/dist/commands/check.d.ts +17 -0
  123. package/dist/commands/check.d.ts.map +1 -0
  124. package/dist/commands/check.js +38 -0
  125. package/dist/commands/check.js.map +1 -0
  126. package/dist/commands/click.d.ts +3 -1
  127. package/dist/commands/click.d.ts.map +1 -1
  128. package/dist/commands/click.js +16 -4
  129. package/dist/commands/click.js.map +1 -1
  130. package/dist/commands/connect.d.ts +14 -0
  131. package/dist/commands/connect.d.ts.map +1 -0
  132. package/dist/commands/connect.js +19 -0
  133. package/dist/commands/connect.js.map +1 -0
  134. package/dist/commands/dialog.d.ts +19 -0
  135. package/dist/commands/dialog.d.ts.map +1 -0
  136. package/dist/commands/dialog.js +51 -0
  137. package/dist/commands/dialog.js.map +1 -0
  138. package/dist/commands/downloads.d.ts +30 -0
  139. package/dist/commands/downloads.d.ts.map +1 -0
  140. package/dist/commands/downloads.js +66 -0
  141. package/dist/commands/downloads.js.map +1 -0
  142. package/dist/commands/fill.d.ts +16 -0
  143. package/dist/commands/fill.d.ts.map +1 -0
  144. package/dist/commands/fill.js +15 -0
  145. package/dist/commands/fill.js.map +1 -0
  146. package/dist/commands/find.d.ts +14 -0
  147. package/dist/commands/find.d.ts.map +1 -0
  148. package/dist/commands/find.js +26 -0
  149. package/dist/commands/find.js.map +1 -0
  150. package/dist/commands/hover.d.ts +13 -0
  151. package/dist/commands/hover.d.ts.map +1 -0
  152. package/dist/commands/hover.js +24 -0
  153. package/dist/commands/hover.js.map +1 -0
  154. package/dist/commands/navigate.d.ts.map +1 -1
  155. package/dist/commands/navigate.js +13 -0
  156. package/dist/commands/navigate.js.map +1 -1
  157. package/dist/commands/network.d.ts +33 -0
  158. package/dist/commands/network.d.ts.map +1 -0
  159. package/dist/commands/network.js +84 -0
  160. package/dist/commands/network.js.map +1 -0
  161. package/dist/commands/performance.d.ts +12 -0
  162. package/dist/commands/performance.d.ts.map +1 -0
  163. package/dist/commands/performance.js +26 -0
  164. package/dist/commands/performance.js.map +1 -0
  165. package/dist/commands/press_key.d.ts +10 -0
  166. package/dist/commands/press_key.d.ts.map +1 -0
  167. package/dist/commands/press_key.js +103 -0
  168. package/dist/commands/press_key.js.map +1 -0
  169. package/dist/commands/scroll.d.ts +21 -0
  170. package/dist/commands/scroll.d.ts.map +1 -0
  171. package/dist/commands/scroll.js +54 -0
  172. package/dist/commands/scroll.js.map +1 -0
  173. package/dist/commands/select.d.ts +18 -0
  174. package/dist/commands/select.d.ts.map +1 -0
  175. package/dist/commands/select.js +53 -0
  176. package/dist/commands/select.js.map +1 -0
  177. package/dist/commands/snapshot.d.ts +9 -0
  178. package/dist/commands/snapshot.d.ts.map +1 -0
  179. package/dist/commands/snapshot.js +7 -0
  180. package/dist/commands/snapshot.js.map +1 -0
  181. package/dist/commands/styles.d.ts +13 -0
  182. package/dist/commands/styles.d.ts.map +1 -0
  183. package/dist/commands/styles.js +7 -0
  184. package/dist/commands/styles.js.map +1 -0
  185. package/dist/commands/type.d.ts +3 -1
  186. package/dist/commands/type.d.ts.map +1 -1
  187. package/dist/commands/type.js +11 -2
  188. package/dist/commands/type.js.map +1 -1
  189. package/dist/commands/upload.d.ts +12 -0
  190. package/dist/commands/upload.d.ts.map +1 -0
  191. package/dist/commands/upload.js +13 -0
  192. package/dist/commands/upload.js.map +1 -0
  193. package/dist/mcp/console-buffer.d.ts +9 -0
  194. package/dist/mcp/console-buffer.d.ts.map +1 -1
  195. package/dist/mcp/console-buffer.js.map +1 -1
  196. package/dist/mcp/index.js +20 -2
  197. package/dist/mcp/index.js.map +1 -1
  198. package/dist/mcp/server.d.ts +10 -0
  199. package/dist/mcp/server.d.ts.map +1 -1
  200. package/dist/mcp/server.js +63 -2
  201. package/dist/mcp/server.js.map +1 -1
  202. package/dist/mcp/tools/browser.d.ts.map +1 -1
  203. package/dist/mcp/tools/browser.js +109 -11
  204. package/dist/mcp/tools/browser.js.map +1 -1
  205. package/dist/mcp/tools/connect.d.ts +5 -0
  206. package/dist/mcp/tools/connect.d.ts.map +1 -0
  207. package/dist/mcp/tools/connect.js +48 -0
  208. package/dist/mcp/tools/connect.js.map +1 -0
  209. package/dist/mcp/tools/files.d.ts +4 -0
  210. package/dist/mcp/tools/files.d.ts.map +1 -0
  211. package/dist/mcp/tools/files.js +85 -0
  212. package/dist/mcp/tools/files.js.map +1 -0
  213. package/dist/mcp/tools/interaction.d.ts +4 -0
  214. package/dist/mcp/tools/interaction.d.ts.map +1 -0
  215. package/dist/mcp/tools/interaction.js +176 -0
  216. package/dist/mcp/tools/interaction.js.map +1 -0
  217. package/dist/mcp/tools/names.d.ts +1 -1
  218. package/dist/mcp/tools/names.d.ts.map +1 -1
  219. package/dist/mcp/tools/names.js +19 -1
  220. package/dist/mcp/tools/names.js.map +1 -1
  221. package/dist/mcp/tools/network.d.ts +4 -0
  222. package/dist/mcp/tools/network.d.ts.map +1 -0
  223. package/dist/mcp/tools/network.js +68 -0
  224. package/dist/mcp/tools/network.js.map +1 -0
  225. package/dist/mcp/tools/performance.d.ts +4 -0
  226. package/dist/mcp/tools/performance.d.ts.map +1 -0
  227. package/dist/mcp/tools/performance.js +26 -0
  228. package/dist/mcp/tools/performance.js.map +1 -0
  229. package/dist/mcp/tools/runtime.d.ts +4 -0
  230. package/dist/mcp/tools/runtime.d.ts.map +1 -0
  231. package/dist/mcp/tools/runtime.js +128 -0
  232. package/dist/mcp/tools/runtime.js.map +1 -0
  233. package/dist/mcp/tools/snapshot.d.ts +4 -0
  234. package/dist/mcp/tools/snapshot.d.ts.map +1 -0
  235. package/dist/mcp/tools/snapshot.js +42 -0
  236. package/dist/mcp/tools/snapshot.js.map +1 -0
  237. package/dist/mcp/tools/styles.d.ts +4 -0
  238. package/dist/mcp/tools/styles.d.ts.map +1 -0
  239. package/dist/mcp/tools/styles.js +27 -0
  240. package/dist/mcp/tools/styles.js.map +1 -0
  241. package/dist/util/config.d.ts.map +1 -1
  242. package/dist/util/config.js +3 -2
  243. package/dist/util/config.js.map +1 -1
  244. package/package.json +5 -4
  245. package/skills/pilot/SKILL.md +48 -0
  246. package/src/browser/browser-manager.ts +465 -0
  247. package/src/browser/chrome-launcher.ts +282 -0
  248. package/src/browser/connection.ts +84 -0
  249. package/src/browser/events.ts +121 -0
  250. package/src/browser/index.ts +20 -0
  251. package/src/browser/inspect/element-ref.ts +68 -0
  252. package/src/browser/inspect/snapshot.ts +348 -0
  253. package/src/browser/inspect/styles.ts +258 -0
  254. package/src/browser/inspect/types.ts +37 -0
  255. package/src/browser/interaction/dialogs.ts +54 -0
  256. package/src/browser/interaction/downloads.ts +116 -0
  257. package/src/browser/interaction/events.ts +79 -0
  258. package/src/browser/interaction/ref-resolver.ts +53 -0
  259. package/src/browser/interaction/uploads.ts +60 -0
  260. package/src/browser/network/network-buffer.ts +133 -0
  261. package/src/browser/network/network-monitor.ts +107 -0
  262. package/src/browser/network/redaction.ts +42 -0
  263. package/src/browser/network/types.ts +47 -0
  264. package/src/browser/performance/analyze.ts +298 -0
  265. package/src/browser/performance/trace.ts +60 -0
  266. package/src/browser/performance/types.ts +34 -0
  267. package/src/browser/preflight.ts +144 -0
  268. package/src/browser/security/origins.ts +102 -0
  269. package/src/browser/tabs.ts +110 -0
  270. package/src/browser/types.ts +32 -0
  271. package/src/browser.ts +22 -798
  272. package/src/cli/init.ts +40 -49
  273. package/src/cli/linux-check.ts +23 -0
  274. package/src/cli/skill-template.ts +4 -113
  275. package/src/commands/check.ts +59 -0
  276. package/src/commands/click.ts +18 -4
  277. package/src/commands/connect.ts +40 -0
  278. package/src/commands/dialog.ts +79 -0
  279. package/src/commands/downloads.ts +111 -0
  280. package/src/commands/fill.ts +34 -0
  281. package/src/commands/find.ts +52 -0
  282. package/src/commands/hover.ts +42 -0
  283. package/src/commands/navigate.ts +20 -0
  284. package/src/commands/network.ts +141 -0
  285. package/src/commands/performance.ts +51 -0
  286. package/src/commands/press_key.ts +134 -0
  287. package/src/commands/scroll.ts +79 -0
  288. package/src/commands/select.ts +78 -0
  289. package/src/commands/snapshot.ts +18 -0
  290. package/src/commands/styles.ts +24 -0
  291. package/src/commands/type.ts +12 -2
  292. package/src/commands/upload.ts +36 -0
  293. package/src/mcp/console-buffer.ts +10 -0
  294. package/src/mcp/index.ts +24 -2
  295. package/src/mcp/server.ts +74 -2
  296. package/src/mcp/tools/browser.ts +125 -9
  297. package/src/mcp/tools/connect.ts +62 -0
  298. package/src/mcp/tools/files.ts +120 -0
  299. package/src/mcp/tools/interaction.ts +232 -0
  300. package/src/mcp/tools/names.ts +19 -1
  301. package/src/mcp/tools/network.ts +86 -0
  302. package/src/mcp/tools/performance.ts +40 -0
  303. package/src/mcp/tools/runtime.ts +164 -0
  304. package/src/mcp/tools/snapshot.ts +64 -0
  305. package/src/mcp/tools/styles.ts +41 -0
  306. package/src/util/config.ts +3 -2
@@ -1,11 +1,21 @@
1
1
  export type ConsoleLevel = 'log' | 'warn' | 'error' | 'info' | 'debug';
2
2
 
3
+ export interface StackFrame {
4
+ url: string;
5
+ functionName: string;
6
+ lineNumber: number;
7
+ columnNumber: number;
8
+ }
9
+
3
10
  export interface ConsoleMessage {
4
11
  level: ConsoleLevel;
5
12
  text: string;
6
13
  url: string;
7
14
  lineNumber: number;
15
+ columnNumber?: number;
8
16
  timestamp: number;
17
+ stackFrames?: StackFrame[];
18
+ isException?: boolean;
9
19
  }
10
20
 
11
21
  export interface ConsoleFilter {
package/src/mcp/index.ts CHANGED
@@ -28,8 +28,12 @@ async function run(): Promise<void> {
28
28
  console.log('Usage: pilot [command] [options]');
29
29
  console.log('');
30
30
  console.log('Commands:');
31
+ console.log(' init [--agent claude|codex|both] [--force]');
31
32
  console.log(
32
- ' init [--force] Install the Claude Code debug-browser skill'
33
+ ' Install the Pilot skill (default: claude)'
34
+ );
35
+ console.log(
36
+ ' check Run Linux browser-launch preflight checks'
33
37
  );
34
38
  console.log(' (default) Start the MCP server on stdio');
35
39
  console.log('');
@@ -74,7 +78,25 @@ async function run(): Promise<void> {
74
78
  if (subcommand === 'init') {
75
79
  const { runInit } = await import('../cli/init.js');
76
80
  const force = args.includes('--force');
77
- await runInit({ force });
81
+ const agentArg = args.find((arg) => arg.startsWith('--agent='));
82
+ const agentIndex = args.indexOf('--agent');
83
+ const agent =
84
+ agentArg?.slice('--agent='.length) ??
85
+ (agentIndex >= 0 ? args[agentIndex + 1] : undefined) ??
86
+ 'claude';
87
+ if (!['claude', 'codex', 'both'].includes(agent)) {
88
+ throw new Error('--agent must be one of: claude, codex, both');
89
+ }
90
+ await runInit({
91
+ force,
92
+ agent: agent as 'claude' | 'codex' | 'both'
93
+ });
94
+ return;
95
+ }
96
+
97
+ if (subcommand === 'check') {
98
+ const { runCheck } = await import('../cli/linux-check.js');
99
+ await runCheck();
78
100
  return;
79
101
  }
80
102
 
package/src/mcp/server.ts CHANGED
@@ -1,9 +1,29 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { BrowserManager } from '../browser.js';
3
+ import { ElementRefMap } from '../browser/inspect/element-ref.js';
4
+ import { resetCssState } from '../browser/inspect/styles.js';
5
+ import { DialogQueue } from '../browser/interaction/dialogs.js';
6
+ import { attachDialogHandler } from '../browser/interaction/dialogs.js';
7
+ import {
8
+ DownloadTracker,
9
+ enableDownloads,
10
+ attachDownloadHandler
11
+ } from '../browser/interaction/downloads.js';
12
+ import { NetworkBuffer } from '../browser/network/network-buffer.js';
13
+ import { attachNetworkMonitor } from '../browser/network/network-monitor.js';
3
14
  import { loadConfig } from '../util/config.js';
4
15
  import { ConsoleBuffer, type ConsoleMessage } from './console-buffer.js';
5
16
  import { registerBrowserTools } from './tools/browser.js';
6
17
  import { registerErrorTools } from './tools/errors.js';
18
+ import { registerSnapshotTools } from './tools/snapshot.js';
19
+ import { registerInteractionTools } from './tools/interaction.js';
20
+ import { registerNetworkTools } from './tools/network.js';
21
+ import { registerRuntimeTools } from './tools/runtime.js';
22
+ import { registerStylesTools } from './tools/styles.js';
23
+ import { registerFilesTools } from './tools/files.js';
24
+ import { createTraceState } from '../browser/performance/trace.js';
25
+ import { registerPerformanceTools } from './tools/performance.js';
26
+ import { registerConnectTools } from './tools/connect.js';
7
27
 
8
28
  export interface McpConfig {
9
29
  bufferSize: number;
@@ -14,6 +34,11 @@ export interface McpConfig {
14
34
  export interface BrowserContext {
15
35
  manager: BrowserManager | null;
16
36
  consoleBuffer: ConsoleBuffer;
37
+ elementRefMap: ElementRefMap;
38
+ networkBuffer: NetworkBuffer;
39
+ dialogQueue: DialogQueue;
40
+ downloadTracker: DownloadTracker;
41
+ traceState: ReturnType<typeof createTraceState>;
17
42
  baseConfig: McpConfig;
18
43
  }
19
44
 
@@ -27,18 +52,57 @@ export async function createMcpServer(config: McpConfig): Promise<{
27
52
  });
28
53
 
29
54
  const consoleBuffer = new ConsoleBuffer(config.bufferSize);
55
+ const elementRefMap = new ElementRefMap();
56
+ const networkBuffer = new NetworkBuffer();
57
+ const dialogQueue = new DialogQueue();
58
+ const downloadTracker = new DownloadTracker();
59
+ const traceState = createTraceState();
30
60
 
31
61
  const context: BrowserContext = {
32
62
  manager: null,
33
63
  consoleBuffer,
64
+ elementRefMap,
65
+ networkBuffer,
66
+ dialogQueue,
67
+ downloadTracker,
68
+ traceState,
34
69
  baseConfig: config
35
70
  };
36
71
 
37
- function attachConsoleBuffer(manager: BrowserManager): void {
72
+ function attachEventHandlers(manager: BrowserManager): void {
73
+ let monitoredClient: unknown = null;
74
+
75
+ function ensureMonitors(): void {
76
+ const client = manager.getClient();
77
+ if (client && client !== monitoredClient) {
78
+ attachNetworkMonitor(client, networkBuffer);
79
+ attachDialogHandler(client, dialogQueue);
80
+ attachDownloadHandler(client, downloadTracker);
81
+ void enableDownloads(client).catch(() => {
82
+ // Download behavior setup may fail in some environments
83
+ });
84
+ monitoredClient = client;
85
+ }
86
+ }
87
+
38
88
  manager.setEventCallback((event, data) => {
39
89
  if (event === 'console_message') {
40
90
  consoleBuffer.push(data as ConsoleMessage);
41
91
  }
92
+ if (event === 'page_navigated') {
93
+ elementRefMap.invalidate();
94
+ }
95
+ if (event === 'browser_connected') {
96
+ ensureMonitors();
97
+ }
98
+ if (event === 'browser_disconnected') {
99
+ monitoredClient = null;
100
+ resetCssState();
101
+ dialogQueue.clear();
102
+ downloadTracker.clear();
103
+ }
104
+ // Reattach after tab switch (new client, no connect event)
105
+ ensureMonitors();
42
106
  });
43
107
  }
44
108
 
@@ -48,12 +112,20 @@ export async function createMcpServer(config: McpConfig): Promise<{
48
112
  cdpHost: config.cdpHost
49
113
  });
50
114
  const manager = new BrowserManager(browserConfig);
51
- attachConsoleBuffer(manager);
115
+ attachEventHandlers(manager);
52
116
  return manager;
53
117
  }
54
118
 
55
119
  registerBrowserTools(server, context, makeBrowserManager);
56
120
  registerErrorTools(server, consoleBuffer);
121
+ registerSnapshotTools(server, context);
122
+ registerInteractionTools(server, context);
123
+ registerNetworkTools(server, context);
124
+ registerRuntimeTools(server, consoleBuffer);
125
+ registerStylesTools(server, context);
126
+ registerFilesTools(server, context);
127
+ registerPerformanceTools(server, context);
128
+ registerConnectTools(server, context, makeBrowserManager);
57
129
 
58
130
  return {
59
131
  server,
@@ -57,6 +57,10 @@ const navigateShape = {
57
57
  };
58
58
 
59
59
  const clickShape = {
60
+ ref: z
61
+ .string()
62
+ .optional()
63
+ .describe('Element ref from browser_snapshot (e.g. "e3")'),
60
64
  selector: z.string().optional().describe('CSS selector to click'),
61
65
  x: z.number().optional().describe('Viewport X coordinate'),
62
66
  y: z.number().optional().describe('Viewport Y coordinate'),
@@ -80,6 +84,10 @@ const clickShape = {
80
84
 
81
85
  const typeShape = {
82
86
  text: z.string().describe('Text to type'),
87
+ ref: z
88
+ .string()
89
+ .optional()
90
+ .describe('Element ref from browser_snapshot (e.g. "e3")'),
83
91
  selector: z.string().optional().describe('CSS selector to focus first'),
84
92
  clearFirst: z
85
93
  .boolean()
@@ -128,7 +136,7 @@ const startShape = {
128
136
  .boolean()
129
137
  .optional()
130
138
  .describe(
131
- 'Run Chrome headless. When omitted, falls back to PILOT_HEADLESS (default: true).'
139
+ 'Run Chrome headless. When omitted, falls back to PILOT_HEADLESS (accepts true/1 or false/0; default: false).'
132
140
  ),
133
141
  chromePath: z
134
142
  .string()
@@ -172,6 +180,14 @@ const startShape = {
172
180
  .optional()
173
181
  .describe(
174
182
  'Responsive mode. When true, the page uses real window dimensions and reflows on resize (like a normal browser). Desktop presets default to true; mobile/tablet presets leave this unset (locked viewport). Set to false to lock the viewport with setDeviceMetricsOverride.'
183
+ ),
184
+ startupTimeoutMs: z
185
+ .number()
186
+ .int()
187
+ .positive()
188
+ .default(30000)
189
+ .describe(
190
+ 'Timeout waiting for Chrome DevTools to become ready, in milliseconds.'
175
191
  )
176
192
  };
177
193
 
@@ -190,6 +206,24 @@ const switchTabShape = {
190
206
  targetId: z.string().describe('Target ID of the tab to switch to')
191
207
  };
192
208
 
209
+ const tabsShape = {
210
+ action: z
211
+ .enum(['list', 'new', 'select', 'close'])
212
+ .describe(
213
+ 'Action: list tabs, open new tab, select/switch tab, or close tab'
214
+ ),
215
+ targetId: z
216
+ .string()
217
+ .optional()
218
+ .describe('Target ID (required for select and close actions)'),
219
+ url: z
220
+ .string()
221
+ .optional()
222
+ .describe(
223
+ 'URL to open in the new tab (new action only, opens blank tab if omitted)'
224
+ )
225
+ };
226
+
193
227
  const viewportResizeShape = {
194
228
  width: z.number().int().positive().describe('New viewport width in pixels'),
195
229
  height: z.number().int().positive().describe('New viewport height in pixels'),
@@ -237,7 +271,8 @@ export function registerBrowserTools(
237
271
  viewportWidth,
238
272
  viewportHeight,
239
273
  deviceScaleFactor,
240
- responsive
274
+ responsive,
275
+ startupTimeoutMs
241
276
  }) => {
242
277
  if (context.manager?.isConnected()) {
243
278
  return {
@@ -265,7 +300,8 @@ export function registerBrowserTools(
265
300
  viewportWidth,
266
301
  viewportHeight,
267
302
  deviceScaleFactor,
268
- responsive
303
+ responsive,
304
+ startupTimeoutMs
269
305
  });
270
306
  context.manager = manager;
271
307
 
@@ -361,10 +397,11 @@ export function registerBrowserTools(
361
397
 
362
398
  server.tool(
363
399
  'browser_click',
364
- 'Click an element by CSS selector or coordinates. Either selector or x/y coordinates required.',
400
+ 'Click an element by ref (from browser_snapshot), CSS selector, or coordinates.',
365
401
  clickShape,
366
402
  async (params) => {
367
403
  if (
404
+ params.ref === undefined &&
368
405
  params.selector === undefined &&
369
406
  (params.x === undefined || params.y === undefined)
370
407
  ) {
@@ -372,7 +409,7 @@ export function registerBrowserTools(
372
409
  content: [
373
410
  {
374
411
  type: 'text' as const,
375
- text: 'Error: Either selector or x/y coordinates are required'
412
+ text: 'Error: Either ref, selector, or x/y coordinates are required'
376
413
  }
377
414
  ],
378
415
  isError: true
@@ -380,7 +417,7 @@ export function registerBrowserTools(
380
417
  }
381
418
 
382
419
  const { client } = requireClient(context);
383
- const result = await executeClick(client, params);
420
+ const result = await executeClick(client, params, context.elementRefMap);
384
421
 
385
422
  return {
386
423
  content: [
@@ -395,17 +432,18 @@ export function registerBrowserTools(
395
432
 
396
433
  server.tool(
397
434
  'browser_type',
398
- 'Type text into the focused element or a specified selector',
435
+ 'Type text into the focused element, a ref (from browser_snapshot), or a CSS selector. Generates keystroke events (use browser_fill to replace a field value entirely).',
399
436
  typeShape,
400
437
  async (params) => {
401
438
  const { client } = requireClient(context);
402
- await executeType(client, params);
439
+ await executeType(client, params, context.elementRefMap);
403
440
 
441
+ const target = params.ref ?? params.selector;
404
442
  return {
405
443
  content: [
406
444
  {
407
445
  type: 'text' as const,
408
- text: `Typed "${params.text}"${params.selector ? ` into ${params.selector}` : ''}`
446
+ text: `Typed "${params.text}"${target ? ` into ${target}` : ''}`
409
447
  }
410
448
  ]
411
449
  };
@@ -602,4 +640,82 @@ export function registerBrowserTools(
602
640
  };
603
641
  }
604
642
  );
643
+
644
+ server.tool(
645
+ 'browser_tabs',
646
+ 'Manage browser tabs. Actions: "list" shows all tabs, "new" opens a tab (optional url), "select" switches to a tab (requires targetId), "close" closes a tab (requires targetId).',
647
+ tabsShape,
648
+ async ({ action, targetId, url }) => {
649
+ const { manager } = requireClient(context);
650
+
651
+ switch (action) {
652
+ case 'list': {
653
+ const tabs = await executeListTabs(manager);
654
+ return {
655
+ content: [
656
+ { type: 'text' as const, text: JSON.stringify(tabs, null, 2) }
657
+ ]
658
+ };
659
+ }
660
+
661
+ case 'new': {
662
+ const result = await executeNewTab(manager, url);
663
+ return {
664
+ content: [
665
+ {
666
+ type: 'text' as const,
667
+ text: `New tab opened (targetId: ${result.targetId}, url: ${result.url})`
668
+ }
669
+ ]
670
+ };
671
+ }
672
+
673
+ case 'select': {
674
+ if (!targetId) {
675
+ return {
676
+ content: [
677
+ {
678
+ type: 'text' as const,
679
+ text: 'Error: targetId is required for select action'
680
+ }
681
+ ],
682
+ isError: true
683
+ };
684
+ }
685
+ await executeSwitchTab(manager, targetId);
686
+ return {
687
+ content: [
688
+ {
689
+ type: 'text' as const,
690
+ text: `Switched to tab ${targetId}.`
691
+ }
692
+ ]
693
+ };
694
+ }
695
+
696
+ case 'close': {
697
+ if (!targetId) {
698
+ return {
699
+ content: [
700
+ {
701
+ type: 'text' as const,
702
+ text: 'Error: targetId is required for close action'
703
+ }
704
+ ],
705
+ isError: true
706
+ };
707
+ }
708
+ await executeCloseTab(manager, targetId);
709
+ return {
710
+ content: [
711
+ {
712
+ type: 'text' as const,
713
+ text: `Tab ${targetId} closed.`
714
+ }
715
+ ]
716
+ };
717
+ }
718
+ }
719
+ }
720
+ );
605
721
  }
@@ -0,0 +1,62 @@
1
+ import { z } from 'zod';
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import type { BrowserContext } from '../server.js';
4
+ import type { BrowserManager } from '../../browser/browser-manager.js';
5
+ import { executeConnect, formatConnectResult } from '../../commands/connect.js';
6
+
7
+ const connectShape = {
8
+ host: z
9
+ .string()
10
+ .default('127.0.0.1')
11
+ .describe('Chrome debugging host (default: 127.0.0.1)'),
12
+ port: z
13
+ .number()
14
+ .int()
15
+ .min(1)
16
+ .max(65535)
17
+ .default(9222)
18
+ .describe('Chrome debugging port (default: 9222)')
19
+ };
20
+
21
+ export function registerConnectTools(
22
+ server: McpServer,
23
+ context: BrowserContext,
24
+ makeBrowserManager: () => BrowserManager
25
+ ): void {
26
+ server.tool(
27
+ 'browser_connect',
28
+ 'Connect to an existing Chrome instance with remote debugging enabled. Start Chrome with --remote-debugging-port=PORT, then use this tool to connect. Lists available tabs after connecting. Use browser_switch_tab to select a specific tab.',
29
+ connectShape,
30
+ async ({ host, port }) => {
31
+ if (context.manager?.isConnected()) {
32
+ return {
33
+ content: [
34
+ {
35
+ type: 'text' as const,
36
+ text: 'Already connected to a browser. Call browser_stop to disconnect first.'
37
+ }
38
+ ]
39
+ };
40
+ }
41
+
42
+ if (context.manager) {
43
+ await context.manager.destroy();
44
+ context.manager = null;
45
+ }
46
+
47
+ const manager = makeBrowserManager();
48
+ try {
49
+ const result = await executeConnect(manager, { host, port });
50
+ context.manager = manager;
51
+ return {
52
+ content: [
53
+ { type: 'text' as const, text: formatConnectResult(result) }
54
+ ]
55
+ };
56
+ } catch (err) {
57
+ await manager.destroy();
58
+ throw err;
59
+ }
60
+ }
61
+ );
62
+ }
@@ -0,0 +1,120 @@
1
+ import { z } from 'zod';
2
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import type { BrowserContext } from '../server.js';
4
+ import { executeDialog, formatDialogResult } from '../../commands/dialog.js';
5
+ import { executeUpload } from '../../commands/upload.js';
6
+ import {
7
+ executeDownloads,
8
+ formatDownloadsResult
9
+ } from '../../commands/downloads.js';
10
+
11
+ const dialogShape = {
12
+ action: z
13
+ .enum(['list', 'accept', 'dismiss'])
14
+ .describe(
15
+ 'Action: list pending dialogs, accept (OK/Yes), or dismiss (Cancel/No)'
16
+ ),
17
+ promptText: z
18
+ .string()
19
+ .optional()
20
+ .describe('Text to enter for prompt dialogs (accept action only)')
21
+ };
22
+
23
+ const uploadShape = {
24
+ ref: z
25
+ .string()
26
+ .describe('Element ref of a file input (from browser_snapshot)'),
27
+ files: z
28
+ .array(z.string())
29
+ .describe(
30
+ 'File paths to upload. Must exist, be regular files, and be under PILOT_UPLOAD_ROOTS (default: cwd).'
31
+ )
32
+ };
33
+
34
+ const downloadsShape = {
35
+ action: z
36
+ .enum(['list', 'wait', 'clear'])
37
+ .describe(
38
+ 'Action: list tracked downloads, wait for one to complete, or clear'
39
+ ),
40
+ guid: z
41
+ .string()
42
+ .optional()
43
+ .describe('Download GUID (required for wait action)'),
44
+ timeoutMs: z
45
+ .number()
46
+ .int()
47
+ .positive()
48
+ .optional()
49
+ .describe('Timeout for wait action in milliseconds (default: 30000)')
50
+ };
51
+
52
+ function requireContext(context: BrowserContext) {
53
+ const manager = context.manager;
54
+ if (!manager || !manager.isConnected()) {
55
+ throw new Error('Browser not started. Call browser_start first.');
56
+ }
57
+ const client = manager.getClient()!;
58
+ return { client };
59
+ }
60
+
61
+ export function registerFilesTools(
62
+ server: McpServer,
63
+ context: BrowserContext
64
+ ): void {
65
+ server.tool(
66
+ 'browser_dialog',
67
+ 'Handle browser dialogs (alert, confirm, prompt). Use "list" to see pending dialogs, "accept" to click OK/Yes, or "dismiss" to click Cancel/No. For prompt dialogs, provide promptText with accept.',
68
+ dialogShape,
69
+ async ({ action, promptText }) => {
70
+ const { client } = requireContext(context);
71
+ const result = await executeDialog(client, context.dialogQueue, {
72
+ action,
73
+ promptText
74
+ });
75
+ return {
76
+ content: [{ type: 'text' as const, text: formatDialogResult(result) }]
77
+ };
78
+ }
79
+ );
80
+
81
+ server.tool(
82
+ 'browser_upload',
83
+ 'Upload files to a file input element by ref. Files must exist, be regular files, and be under allowed roots (PILOT_UPLOAD_ROOTS env, default: cwd). Use browser_snapshot to find file input refs.',
84
+ uploadShape,
85
+ async ({ ref, files }) => {
86
+ const { client } = requireContext(context);
87
+ const result = await executeUpload(client, context.elementRefMap, {
88
+ ref,
89
+ files
90
+ });
91
+ return {
92
+ content: [
93
+ {
94
+ type: 'text' as const,
95
+ text: `Uploaded ${result.files.length} file(s) to ${result.ref}`
96
+ }
97
+ ]
98
+ };
99
+ }
100
+ );
101
+
102
+ server.tool(
103
+ 'browser_downloads',
104
+ 'Track file downloads. Actions: "list" shows tracked downloads with status/progress; "wait" blocks until a download (by GUID) completes; "clear" resets the tracker.',
105
+ downloadsShape,
106
+ async ({ action, guid, timeoutMs }) => {
107
+ requireContext(context);
108
+ const result = await executeDownloads(context.downloadTracker, {
109
+ action,
110
+ guid,
111
+ timeoutMs
112
+ });
113
+ return {
114
+ content: [
115
+ { type: 'text' as const, text: formatDownloadsResult(result) }
116
+ ]
117
+ };
118
+ }
119
+ );
120
+ }