Skip to content

Commit 9538614

Browse files
committed
Centralize tool schema definitions and sync across MCP
Introduces src/vs/workbench/contrib/roopik/electron-main/mcp/toolSchemas.ts as the single source of truth for MCP tool schemas using Zod. Refactors browserTools.ts and mcpRequestRouter.ts to use these shared definitions, updates network request and viewport tools for more flexible filtering and override clearing, and adds a sync-schemas script to copy schemas to the STDIO binary. Updates build scripts to ensure schemas are synced before building binaries, ensuring DRY and consistent tool definitions across all MCP interfaces.
1 parent e7aee4c commit 9538614

6 files changed

Lines changed: 515 additions & 405 deletions

File tree

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Roopik. All rights reserved.
3+
* Licensed under the MIT License.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
/**
7+
* Tool Schemas - Single Source of Truth
8+
*
9+
* All MCP tool definitions using Zod v4.
10+
* This file is the canonical source for tool schemas across:
11+
* - WebSocket MCP (mcpRequestRouter.ts) - converts to JSON Schema via .toJsonSchema()
12+
* - HTTP MCP (browserTools.ts) - uses Zod directly with MCP SDK
13+
* - STDIO Binary (tools.ts) - copies/imports these definitions
14+
*
15+
* DRY Principle: Define once, use everywhere.
16+
*/
17+
18+
import { z } from 'zod';
19+
20+
// ============================================================================
21+
// Browser Tool Schemas (14)
22+
// ============================================================================
23+
24+
export const browserOpenSchema = z.object({
25+
url: z.string().optional().describe('Optional URL to navigate to')
26+
});
27+
28+
export const browserNavigateSchema = z.object({
29+
url: z.string().describe('URL to navigate to')
30+
});
31+
32+
export const browserReloadSchema = z.object({
33+
ignoreCache: z.boolean().optional().describe('Whether to ignore cache when reloading')
34+
});
35+
36+
export const browserActionInputSchema = z.object({
37+
action: z.enum(['click', 'right_click', 'double_click', 'hover', 'drag', 'type', 'press', 'scroll'])
38+
.describe('The action to perform'),
39+
coordinate: z.string().optional().describe('Coordinates in "x,y" format for click/hover actions'),
40+
text: z.string().optional().describe('Text to type (for type action)'),
41+
key: z.string().optional().describe('Key to press (for press action)'),
42+
modifiers: z.array(z.string()).optional().describe('Modifier keys (ctrl, alt, shift, meta)'),
43+
deltaX: z.number().optional().describe('Horizontal scroll delta'),
44+
deltaY: z.number().optional().describe('Vertical scroll delta')
45+
});
46+
47+
export const browserExecuteScriptSchema = z.object({
48+
script: z.string().describe('JavaScript code to execute')
49+
});
50+
51+
export const browserInspectElementSchema = z.object({
52+
selector: z.string().describe('CSS selector for the element'),
53+
includeInherited: z.boolean().optional().describe('Include inherited styles')
54+
});
55+
56+
export const browserGetErrorsSchema = z.object({
57+
limit: z.number().optional().describe('Maximum number of errors to return')
58+
});
59+
60+
export const browserGetConsoleLogsSchema = z.object({
61+
types: z.array(z.string()).optional().describe('Filter by log types (log, warn, error, etc.)'),
62+
since: z.number().optional().describe('Get logs since timestamp'),
63+
limit: z.number().optional().describe('Maximum number of logs'),
64+
clear: z.boolean().optional().describe('Clear logs after getting')
65+
});
66+
67+
export const browserSetViewportSchema = z.object({
68+
width: z.number().optional().describe('Viewport width in pixels. Omit to clear override.'),
69+
height: z.number().optional().describe('Viewport height in pixels. Omit to clear override.'),
70+
deviceScaleFactor: z.number().optional().describe('Device scale factor (default: 1)'),
71+
mobile: z.boolean().optional().describe('Emulate mobile device (default: false)')
72+
});
73+
74+
export const browserGetNetworkRequestsSchema = z.object({
75+
urlFilter: z.string().optional().describe('Filter requests by URL substring'),
76+
method: z.string().optional().describe('Filter by HTTP method (GET, POST, etc.)'),
77+
statusFilter: z.enum(['success', 'error', 'all']).optional()
78+
.describe('Filter by status: success (2xx-3xx), error (4xx-5xx or failed), all'),
79+
limit: z.number().optional().describe('Maximum number of requests to return (default: 100, max: 500)')
80+
});
81+
82+
// Empty schemas for tools with no parameters
83+
export const emptySchema = z.object({});
84+
85+
// ============================================================================
86+
// Canvas Tool Schemas (3)
87+
// ============================================================================
88+
89+
export const canvasListSchema = z.object({
90+
nameFilter: z.string().optional().describe('Filter by name'),
91+
sortBy: z.enum(['name', 'updatedAt', 'createdAt', 'componentCount']).optional().describe('Sort field'),
92+
sortDirection: z.enum(['asc', 'desc']).optional().describe('Sort direction')
93+
});
94+
95+
export const canvasCreateSchema = z.object({
96+
name: z.string().describe('Canvas name')
97+
});
98+
99+
// ============================================================================
100+
// Component Tool Schemas (6)
101+
// ============================================================================
102+
103+
export const componentAddSchema = z.object({
104+
canvasId: z.string().optional().describe('Target canvas ID (uses active if not provided)'),
105+
folderPath: z.string().describe('Path to component folder'),
106+
name: z.string().optional().describe('Component name (auto-detected if not provided)'),
107+
entryFile: z.string().optional().describe('Entry file (auto-detected if not provided)'),
108+
framework: z.enum(['react', 'vue', 'svelte', 'solid', 'preact', 'html']).optional()
109+
.describe('Framework (auto-detected if not provided)')
110+
});
111+
112+
export const componentAddBatchSchema = z.object({
113+
components: z.array(z.object({
114+
canvasId: z.string().optional(),
115+
folderPath: z.string(),
116+
name: z.string().optional(),
117+
entryFile: z.string().optional(),
118+
framework: z.enum(['react', 'vue', 'svelte', 'solid', 'preact', 'html']).optional()
119+
})).describe('Array of components to add')
120+
});
121+
122+
export const componentRemoveSchema = z.object({
123+
componentId: z.string().describe('Component ID'),
124+
deleteSourceCode: z.boolean().optional().describe('Also delete source files')
125+
});
126+
127+
export const componentGetInfoSchema = z.object({
128+
componentId: z.string().describe('Component ID')
129+
});
130+
131+
export const componentListSchema = z.object({
132+
canvasId: z.string().describe('Canvas ID')
133+
});
134+
135+
export const componentRebuildSchema = z.object({
136+
componentId: z.string().describe('Component ID')
137+
});
138+
139+
// ============================================================================
140+
// Project Tool Schemas (3)
141+
// ============================================================================
142+
143+
export const projectStartSchema = z.object({
144+
projectPath: z.string().describe('Path to project (relative or absolute)'),
145+
port: z.number().optional().describe('Optional port number')
146+
});
147+
148+
// ============================================================================
149+
// Tool Definitions Interface
150+
// ============================================================================
151+
152+
export interface ToolDefinition {
153+
name: string;
154+
description: string;
155+
schema: z.ZodType;
156+
}
157+
158+
// ============================================================================
159+
// Complete Tool Definitions Array
160+
// ============================================================================
161+
162+
export const TOOL_DEFINITIONS: ToolDefinition[] = [
163+
// ========== Browser Tools (14) ==========
164+
{
165+
name: 'browser_open',
166+
description: 'Open the browser view. Optionally navigate to a URL.',
167+
schema: browserOpenSchema
168+
},
169+
{
170+
name: 'browser_close',
171+
description: 'Close the browser view.',
172+
schema: emptySchema
173+
},
174+
{
175+
name: 'browser_screenshot',
176+
description: 'Take a screenshot of the current browser view. Returns base64 PNG image.',
177+
schema: emptySchema
178+
},
179+
{
180+
name: 'browser_navigate',
181+
description: 'Navigate to a URL in the browser.',
182+
schema: browserNavigateSchema
183+
},
184+
{
185+
name: 'browser_reload',
186+
description: 'Reload the current page.',
187+
schema: browserReloadSchema
188+
},
189+
{
190+
name: 'browser_action_input',
191+
description: 'Perform browser input actions (click, type, scroll, etc.).',
192+
schema: browserActionInputSchema
193+
},
194+
{
195+
name: 'browser_execute_script',
196+
description: 'Execute JavaScript in the browser context.',
197+
schema: browserExecuteScriptSchema
198+
},
199+
{
200+
name: 'browser_inspect_element',
201+
description: 'Inspect CSS styles of an element with source file resolution. THE MOAT capability - returns exact file:line:column where styles are defined.',
202+
schema: browserInspectElementSchema
203+
},
204+
{
205+
name: 'browser_get_errors',
206+
description: 'Get combined console errors and network failures.',
207+
schema: browserGetErrorsSchema
208+
},
209+
{
210+
name: 'browser_get_console_logs',
211+
description: 'Get browser console logs.',
212+
schema: browserGetConsoleLogsSchema
213+
},
214+
{
215+
name: 'browser_get_performance',
216+
description: 'Get browser performance metrics (Web Vitals: LCP, CLS, FCP, TTFB and runtime metrics).',
217+
schema: emptySchema
218+
},
219+
{
220+
name: 'browser_get_state',
221+
description: 'Get browser state information (open/closed, current URL, title).',
222+
schema: emptySchema
223+
},
224+
{
225+
name: 'browser_set_viewport',
226+
description: 'Set or clear browser viewport override. Provide width/height to set a specific size (e.g., mobile 375x812). Call with NO parameters to clear override and restore natural browser size.',
227+
schema: browserSetViewportSchema
228+
},
229+
{
230+
name: 'browser_get_network_requests',
231+
description: 'Get captured network requests and responses. Requires CDP monitoring.',
232+
schema: browserGetNetworkRequestsSchema
233+
},
234+
235+
// ========== Canvas Tools (3) ==========
236+
{
237+
name: 'canvas_list',
238+
description: 'List all canvases.',
239+
schema: canvasListSchema
240+
},
241+
{
242+
name: 'canvas_get_active',
243+
description: 'Get the currently active/focused canvas.',
244+
schema: emptySchema
245+
},
246+
{
247+
name: 'canvas_create',
248+
description: 'Create a new canvas or get existing one with same name.',
249+
schema: canvasCreateSchema
250+
},
251+
252+
// ========== Component Tools (6) ==========
253+
{
254+
name: 'component_add',
255+
description: 'Add a component to a canvas for live preview.',
256+
schema: componentAddSchema
257+
},
258+
{
259+
name: 'component_add_batch',
260+
description: 'Add multiple components at once.',
261+
schema: componentAddBatchSchema
262+
},
263+
{
264+
name: 'component_remove',
265+
description: 'Remove a component from canvas.',
266+
schema: componentRemoveSchema
267+
},
268+
{
269+
name: 'component_get_info',
270+
description: 'Get detailed component information including build state.',
271+
schema: componentGetInfoSchema
272+
},
273+
{
274+
name: 'component_list',
275+
description: 'List all components in a canvas.',
276+
schema: componentListSchema
277+
},
278+
{
279+
name: 'component_rebuild',
280+
description: 'Trigger rebuild of a component.',
281+
schema: componentRebuildSchema
282+
},
283+
284+
// ========== Project Tools (3) ==========
285+
{
286+
name: 'project_get_active',
287+
description: 'Check if a dev server is running and get its info.',
288+
schema: emptySchema
289+
},
290+
{
291+
name: 'project_start',
292+
description: 'Start a development server for a project.',
293+
schema: projectStartSchema
294+
},
295+
{
296+
name: 'project_stop',
297+
description: 'Stop the running development server.',
298+
schema: emptySchema
299+
}
300+
];
301+
302+
// ============================================================================
303+
// Helper: Convert to JSON Schema format for MCP protocol
304+
// Uses Zod v4 native .toJSONSchema() method
305+
// ============================================================================
306+
307+
export function getToolDefinitionsAsJsonSchema(): Array<{
308+
name: string;
309+
description: string;
310+
inputSchema: Record<string, unknown>;
311+
}> {
312+
return TOOL_DEFINITIONS.map(tool => ({
313+
name: tool.name,
314+
description: tool.description,
315+
inputSchema: tool.schema.toJSONSchema() as Record<string, unknown>
316+
}));
317+
}
318+
319+
// Total: 26 Tools

0 commit comments

Comments
 (0)