zelune 1.6.2 → 1.6.4
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 +16 -0
- package/bin/cli.mjs +13 -54
- package/dist/assets/CheckoutScreen-6IdrpKgm.js +229 -0
- package/dist/assets/HomeScreen-Dxe9L4I2.js +316 -0
- package/dist/assets/LoginScreen-Dp1awI4F.js +189 -0
- package/dist/assets/ProductDetailScreen-Wa5_nlRP.js +196 -0
- package/dist/assets/ProfileScreen-riAU6vVh.js +185 -0
- package/dist/assets/index-hEzd4JVy.js +44697 -0
- package/{index.html → dist/index.html} +3 -7
- package/mcp/server.mjs +1370 -34
- package/package.json +10 -17
- package/plugin.json +1 -1
- package/socket.yml +11 -0
- package/mcp/server.ts +0 -446
- package/plugins/viteReactNativeFallbackPlugin.ts +0 -519
- package/plugins/viteRnSourceInject.ts +0 -117
- package/plugins/viteVirtualScreensPlugin.ts +0 -216
- package/src/canvas/CanvasApp.tsx +0 -1655
- package/src/canvas/FlowArrowsLayer.tsx +0 -239
- package/src/canvas/InspectorPanel.tsx +0 -1494
- package/src/canvas/ScreenArtboard.tsx +0 -1491
- package/src/canvas/types.ts +0 -23
- package/src/main.tsx +0 -80
- package/src/runtime/MockNavigationContext.tsx +0 -1795
- package/src/runtime/ScreenErrorBoundary.tsx +0 -86
- package/src/runtime/nativewindShim.ts +0 -997
- package/src/runtime/reactNativeShim.ts +0 -240
- package/src/screens/CheckoutScreen.tsx +0 -229
- package/src/screens/HomeScreen.tsx +0 -288
- package/src/screens/LoginScreen.tsx +0 -176
- package/src/screens/ProductDetailScreen.tsx +0 -207
- package/src/screens/ProfileScreen.tsx +0 -172
- package/src/screens/index.ts +0 -1
- package/src/server/apiMiddleware.ts +0 -375
- package/src/server/astEngine.ts +0 -2553
- package/src/vite-env.d.ts +0 -7
- package/tsconfig.json +0 -23
- package/vite.config.ts +0 -340
- /package/{src/styles.css → dist/assets/index-C7nrS82N.css} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zelune",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -29,17 +29,14 @@
|
|
|
29
29
|
},
|
|
30
30
|
"files": [
|
|
31
31
|
"bin/",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"mcp/",
|
|
32
|
+
"dist/",
|
|
33
|
+
"mcp/server.mjs",
|
|
35
34
|
"skills/",
|
|
36
35
|
"rules/",
|
|
37
36
|
"assets/",
|
|
38
|
-
"index.html",
|
|
39
|
-
"vite.config.ts",
|
|
40
|
-
"tsconfig.json",
|
|
41
37
|
"plugin.json",
|
|
42
38
|
"mcp_config.json",
|
|
39
|
+
"socket.yml",
|
|
43
40
|
"PRODUCT.md",
|
|
44
41
|
"DESIGN.md",
|
|
45
42
|
"README.md",
|
|
@@ -49,7 +46,7 @@
|
|
|
49
46
|
"dev": "vite --host 127.0.0.1 --port 5173",
|
|
50
47
|
"zelune": "node ./bin/cli.mjs",
|
|
51
48
|
"canvas": "node ./bin/cli.mjs",
|
|
52
|
-
"build": "vite build",
|
|
49
|
+
"build": "vite build --mode development",
|
|
53
50
|
"preview": "vite preview --host 127.0.0.1 --port 4173",
|
|
54
51
|
"test": "vitest run",
|
|
55
52
|
"mcp": "node ./bin/cli.mjs --mcp"
|
|
@@ -57,22 +54,18 @@
|
|
|
57
54
|
"dependencies": {
|
|
58
55
|
"@babel/generator": "^7.25.0",
|
|
59
56
|
"@babel/parser": "^7.25.0",
|
|
60
|
-
"@babel/
|
|
61
|
-
"@babel/types": "^7.25.0",
|
|
62
|
-
"@vitejs/plugin-react": "^4.7.0",
|
|
63
|
-
"lucide-react": "^0.454.0",
|
|
64
|
-
"react": "^18.3.1",
|
|
65
|
-
"react-dom": "^18.3.1",
|
|
66
|
-
"react-native-web": "^0.19.12",
|
|
67
|
-
"vite": "6.4.4"
|
|
57
|
+
"@babel/types": "^7.25.0"
|
|
68
58
|
},
|
|
69
59
|
"devDependencies": {
|
|
70
60
|
"@types/babel__generator": "^7.6.8",
|
|
71
|
-
"@types/babel__traverse": "^7.20.6",
|
|
72
61
|
"@types/node": "^22.0.0",
|
|
73
62
|
"@types/react": "^18.3.11",
|
|
74
63
|
"@types/react-dom": "^18.3.1",
|
|
64
|
+
"lucide-react": "^0.454.0",
|
|
65
|
+
"react": "^18.3.1",
|
|
66
|
+
"react-dom": "^18.3.1",
|
|
75
67
|
"typescript": "^5.6.2",
|
|
68
|
+
"vite": "6.4.4",
|
|
76
69
|
"vitest": "^2.1.2"
|
|
77
70
|
}
|
|
78
71
|
}
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zelune",
|
|
3
3
|
"displayName": "Zelune — React Native Visual Canvas",
|
|
4
|
-
"version": "1.6.
|
|
4
|
+
"version": "1.6.4",
|
|
5
5
|
"description": "Zelune MCP & Figma-style multi-screen React Native visual canvas with live surgical AST patching and navigation flow visualization for Google Antigravity.",
|
|
6
6
|
"logo": "assets/logo.svg",
|
|
7
7
|
"suggestedPrompts": [
|
package/socket.yml
ADDED
package/mcp/server.ts
DELETED
|
@@ -1,446 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import readline from 'node:readline';
|
|
4
|
-
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
5
|
-
import {
|
|
6
|
-
discoverScreens,
|
|
7
|
-
inspectElement,
|
|
8
|
-
patchElement,
|
|
9
|
-
reorderElement,
|
|
10
|
-
insertElement,
|
|
11
|
-
duplicateElement,
|
|
12
|
-
deleteElement,
|
|
13
|
-
type InsertElementPreset,
|
|
14
|
-
type StyleValue,
|
|
15
|
-
} from '../src/server/astEngine.js';
|
|
16
|
-
|
|
17
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const DEFAULT_WORKSPACE_ROOT = path.resolve(__dirname, '..');
|
|
19
|
-
|
|
20
|
-
export interface JsonRpcRequest {
|
|
21
|
-
jsonrpc?: string;
|
|
22
|
-
id?: string | number | null;
|
|
23
|
-
method?: string;
|
|
24
|
-
params?: Record<string, unknown>;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface JsonRpcResponse {
|
|
28
|
-
jsonrpc: '2.0';
|
|
29
|
-
id: string | number | null;
|
|
30
|
-
result?: unknown;
|
|
31
|
-
error?: {
|
|
32
|
-
code: number;
|
|
33
|
-
message: string;
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export const MCP_TOOLS = [
|
|
38
|
-
{
|
|
39
|
-
name: 'list_canvas_screens',
|
|
40
|
-
description:
|
|
41
|
-
'Lists all discovered React Native screens on the visual canvas along with their element counts and outgoing navigation flow edges.',
|
|
42
|
-
inputSchema: {
|
|
43
|
-
type: 'object',
|
|
44
|
-
properties: {},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'inspect_screen_element',
|
|
49
|
-
description:
|
|
50
|
-
'Inspects a specific JSX element inside a React Native screen by its relative file path and AST index path.',
|
|
51
|
-
inputSchema: {
|
|
52
|
-
type: 'object',
|
|
53
|
-
properties: {
|
|
54
|
-
file: {
|
|
55
|
-
type: 'string',
|
|
56
|
-
description: 'Relative path to the screen file (e.g. src/screens/HomeScreen.tsx)',
|
|
57
|
-
},
|
|
58
|
-
astPath: {
|
|
59
|
-
type: 'string',
|
|
60
|
-
description: 'Preorder AST index of the JSX element (e.g. "0", "4")',
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
required: ['file', 'astPath'],
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
name: 'patch_screen_element',
|
|
68
|
-
description:
|
|
69
|
-
'Surgically patches the text copy or StyleSheet properties of a React Native JSX element.',
|
|
70
|
-
inputSchema: {
|
|
71
|
-
type: 'object',
|
|
72
|
-
properties: {
|
|
73
|
-
file: {
|
|
74
|
-
type: 'string',
|
|
75
|
-
description: 'Relative path to the screen file (e.g. src/screens/HomeScreen.tsx)',
|
|
76
|
-
},
|
|
77
|
-
astPath: {
|
|
78
|
-
type: 'string',
|
|
79
|
-
description: 'Preorder AST index of the JSX element',
|
|
80
|
-
},
|
|
81
|
-
text: {
|
|
82
|
-
type: 'string',
|
|
83
|
-
description: 'Optional new static text content for <Text> elements',
|
|
84
|
-
},
|
|
85
|
-
styles: {
|
|
86
|
-
type: 'object',
|
|
87
|
-
description: 'Optional map of React Native style properties to update',
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
required: ['file', 'astPath'],
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
name: 'reorder_screen_element',
|
|
95
|
-
description:
|
|
96
|
-
'Surgically reorders a React Native JSX element among its Flexbox siblings in the .tsx AST.',
|
|
97
|
-
inputSchema: {
|
|
98
|
-
type: 'object',
|
|
99
|
-
properties: {
|
|
100
|
-
file: {
|
|
101
|
-
type: 'string',
|
|
102
|
-
description: 'Relative path to the screen file (e.g. src/screens/HomeScreen.tsx)',
|
|
103
|
-
},
|
|
104
|
-
astPath: {
|
|
105
|
-
type: 'string',
|
|
106
|
-
description: 'Preorder AST index of the JSX element to move',
|
|
107
|
-
},
|
|
108
|
-
direction: {
|
|
109
|
-
type: 'string',
|
|
110
|
-
enum: ['up', 'down'],
|
|
111
|
-
description: 'Optional relative direction ("up" or "down") among siblings',
|
|
112
|
-
},
|
|
113
|
-
targetAstPath: {
|
|
114
|
-
type: 'string',
|
|
115
|
-
description: 'Optional AST index of the sibling to place before/after',
|
|
116
|
-
},
|
|
117
|
-
placement: {
|
|
118
|
-
type: 'string',
|
|
119
|
-
enum: ['before', 'after'],
|
|
120
|
-
description: 'Whether to insert before or after targetAstPath',
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
required: ['file', 'astPath'],
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
name: 'insert_screen_element',
|
|
128
|
-
description:
|
|
129
|
-
'Surgically inserts a new React Native component/section preset (button, section, row, heading, text, input, badge, divider) into a screen .tsx AST and auto-injects required react-native imports.',
|
|
130
|
-
inputSchema: {
|
|
131
|
-
type: 'object',
|
|
132
|
-
properties: {
|
|
133
|
-
file: {
|
|
134
|
-
type: 'string',
|
|
135
|
-
description: 'Relative path to the screen file (e.g. src/screens/HomeScreen.tsx)',
|
|
136
|
-
},
|
|
137
|
-
astPath: {
|
|
138
|
-
type: 'string',
|
|
139
|
-
description: 'Optional AST index of the reference JSX element',
|
|
140
|
-
},
|
|
141
|
-
placement: {
|
|
142
|
-
type: 'string',
|
|
143
|
-
enum: ['inside', 'after', 'before'],
|
|
144
|
-
description: 'Placement relative to astPath (defaults to "after" or "inside")',
|
|
145
|
-
},
|
|
146
|
-
preset: {
|
|
147
|
-
type: 'string',
|
|
148
|
-
enum: ['button', 'section', 'row', 'heading', 'text', 'input', 'badge', 'divider'],
|
|
149
|
-
description: 'Component preset to insert',
|
|
150
|
-
},
|
|
151
|
-
label: {
|
|
152
|
-
type: 'string',
|
|
153
|
-
description: 'Optional custom label/text/placeholder for the inserted component',
|
|
154
|
-
},
|
|
155
|
-
navigateTo: {
|
|
156
|
-
type: 'string',
|
|
157
|
-
description: 'Optional target screen ID if inserting a navigation button',
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
required: ['file', 'preset'],
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
name: 'duplicate_screen_element',
|
|
165
|
-
description: 'Duplicates a JSX element right after itself in the screen .tsx AST.',
|
|
166
|
-
inputSchema: {
|
|
167
|
-
type: 'object',
|
|
168
|
-
properties: {
|
|
169
|
-
file: { type: 'string' },
|
|
170
|
-
astPath: { type: 'string' },
|
|
171
|
-
},
|
|
172
|
-
required: ['file', 'astPath'],
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
name: 'delete_screen_element',
|
|
177
|
-
description: 'Deletes a non-root JSX element from the screen .tsx AST.',
|
|
178
|
-
inputSchema: {
|
|
179
|
-
type: 'object',
|
|
180
|
-
properties: {
|
|
181
|
-
file: { type: 'string' },
|
|
182
|
-
astPath: { type: 'string' },
|
|
183
|
-
},
|
|
184
|
-
required: ['file', 'astPath'],
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
];
|
|
188
|
-
|
|
189
|
-
async function executeMcpTool(
|
|
190
|
-
workspaceRoot: string,
|
|
191
|
-
name: string,
|
|
192
|
-
args: Record<string, unknown> | undefined
|
|
193
|
-
): Promise<{ content: Array<{ type: 'text'; text: string }> }> {
|
|
194
|
-
if (name === 'list_canvas_screens') {
|
|
195
|
-
const screens = await discoverScreens(workspaceRoot);
|
|
196
|
-
return {
|
|
197
|
-
content: [{ type: 'text', text: JSON.stringify(screens, null, 2) }],
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (name === 'inspect_screen_element') {
|
|
202
|
-
const file = String(args?.file ?? '');
|
|
203
|
-
const astPath = String(args?.astPath ?? '0');
|
|
204
|
-
const element = await inspectElement(workspaceRoot, { file, astPath });
|
|
205
|
-
return {
|
|
206
|
-
content: [{ type: 'text', text: JSON.stringify(element, null, 2) }],
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (name === 'patch_screen_element') {
|
|
211
|
-
const file = String(args?.file ?? '');
|
|
212
|
-
const astPath = String(args?.astPath ?? '0');
|
|
213
|
-
const text = typeof args?.text === 'string' ? args.text : undefined;
|
|
214
|
-
const styles = (args?.styles as Record<string, StyleValue> | undefined) ?? undefined;
|
|
215
|
-
|
|
216
|
-
const result = await patchElement(workspaceRoot, {
|
|
217
|
-
file,
|
|
218
|
-
astPath,
|
|
219
|
-
text,
|
|
220
|
-
styles,
|
|
221
|
-
});
|
|
222
|
-
return {
|
|
223
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
if (name === 'reorder_screen_element') {
|
|
228
|
-
const file = String(args?.file ?? '');
|
|
229
|
-
const astPath = String(args?.astPath ?? '0');
|
|
230
|
-
const direction =
|
|
231
|
-
args?.direction === 'up' || args?.direction === 'down' ? args.direction : undefined;
|
|
232
|
-
const targetAstPath =
|
|
233
|
-
typeof args?.targetAstPath === 'string' ? args.targetAstPath : undefined;
|
|
234
|
-
const placement =
|
|
235
|
-
args?.placement === 'before' || args?.placement === 'after' ? args.placement : undefined;
|
|
236
|
-
|
|
237
|
-
const result = await reorderElement(workspaceRoot, {
|
|
238
|
-
file,
|
|
239
|
-
astPath,
|
|
240
|
-
direction,
|
|
241
|
-
targetAstPath,
|
|
242
|
-
placement,
|
|
243
|
-
});
|
|
244
|
-
return {
|
|
245
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (name === 'insert_screen_element') {
|
|
250
|
-
const file = String(args?.file ?? '');
|
|
251
|
-
const preset = String(args?.preset ?? 'button') as InsertElementPreset;
|
|
252
|
-
const astPath = typeof args?.astPath === 'string' ? args.astPath : undefined;
|
|
253
|
-
const placement =
|
|
254
|
-
args?.placement === 'inside' || args?.placement === 'after' || args?.placement === 'before'
|
|
255
|
-
? args.placement
|
|
256
|
-
: undefined;
|
|
257
|
-
const label = typeof args?.label === 'string' ? args.label : undefined;
|
|
258
|
-
const navigateTo = typeof args?.navigateTo === 'string' ? args.navigateTo : undefined;
|
|
259
|
-
|
|
260
|
-
const result = await insertElement(workspaceRoot, {
|
|
261
|
-
file,
|
|
262
|
-
astPath,
|
|
263
|
-
placement,
|
|
264
|
-
preset,
|
|
265
|
-
label,
|
|
266
|
-
navigateTo,
|
|
267
|
-
});
|
|
268
|
-
return {
|
|
269
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (name === 'duplicate_screen_element') {
|
|
274
|
-
const file = String(args?.file ?? '');
|
|
275
|
-
const astPath = String(args?.astPath ?? '1');
|
|
276
|
-
const result = await duplicateElement(workspaceRoot, { file, astPath });
|
|
277
|
-
return {
|
|
278
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (name === 'delete_screen_element') {
|
|
283
|
-
const file = String(args?.file ?? '');
|
|
284
|
-
const astPath = String(args?.astPath ?? '1');
|
|
285
|
-
const result = await deleteElement(workspaceRoot, { file, astPath });
|
|
286
|
-
return {
|
|
287
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
throw new Error(`Unknown MCP tool: ${name}`);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export async function handleMcpJsonRpcRequest(
|
|
295
|
-
request: JsonRpcRequest,
|
|
296
|
-
customWorkspaceRoot?: string
|
|
297
|
-
): Promise<JsonRpcResponse | null> {
|
|
298
|
-
if (request.id === undefined) {
|
|
299
|
-
// Notification (e.g. notifications/initialized)
|
|
300
|
-
return null;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const workspaceRoot =
|
|
304
|
-
customWorkspaceRoot ||
|
|
305
|
-
process.env.ANTIGRAVITY_TARGET_PROJECT ||
|
|
306
|
-
process.env.ANTIGRAVITY_WORKSPACE_ROOT ||
|
|
307
|
-
DEFAULT_WORKSPACE_ROOT;
|
|
308
|
-
|
|
309
|
-
const method = request.method ?? '';
|
|
310
|
-
|
|
311
|
-
try {
|
|
312
|
-
if (method === 'initialize') {
|
|
313
|
-
const requestedProtocol =
|
|
314
|
-
typeof request.params?.protocolVersion === 'string'
|
|
315
|
-
? request.params.protocolVersion
|
|
316
|
-
: '2024-11-05';
|
|
317
|
-
return {
|
|
318
|
-
jsonrpc: '2.0',
|
|
319
|
-
id: request.id,
|
|
320
|
-
result: {
|
|
321
|
-
protocolVersion: requestedProtocol,
|
|
322
|
-
capabilities: {
|
|
323
|
-
tools: {},
|
|
324
|
-
},
|
|
325
|
-
serverInfo: {
|
|
326
|
-
name: 'zelune',
|
|
327
|
-
version: '1.6.1',
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (method === 'ping') {
|
|
334
|
-
return {
|
|
335
|
-
jsonrpc: '2.0',
|
|
336
|
-
id: request.id,
|
|
337
|
-
result: {},
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
if (method === 'tools/list') {
|
|
342
|
-
return {
|
|
343
|
-
jsonrpc: '2.0',
|
|
344
|
-
id: request.id,
|
|
345
|
-
result: {
|
|
346
|
-
tools: MCP_TOOLS,
|
|
347
|
-
},
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if (method === 'resources/list') {
|
|
352
|
-
return {
|
|
353
|
-
jsonrpc: '2.0',
|
|
354
|
-
id: request.id,
|
|
355
|
-
result: {
|
|
356
|
-
resources: [],
|
|
357
|
-
},
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
if (method === 'prompts/list') {
|
|
362
|
-
return {
|
|
363
|
-
jsonrpc: '2.0',
|
|
364
|
-
id: request.id,
|
|
365
|
-
result: {
|
|
366
|
-
prompts: [],
|
|
367
|
-
},
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (method === 'tools/call') {
|
|
372
|
-
const toolName = String(request.params?.name ?? '');
|
|
373
|
-
const toolArgs =
|
|
374
|
-
(request.params?.arguments as Record<string, unknown> | undefined) ?? undefined;
|
|
375
|
-
const toolResult = await executeMcpTool(workspaceRoot, toolName, toolArgs);
|
|
376
|
-
return {
|
|
377
|
-
jsonrpc: '2.0',
|
|
378
|
-
id: request.id,
|
|
379
|
-
result: toolResult,
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return {
|
|
384
|
-
jsonrpc: '2.0',
|
|
385
|
-
id: request.id,
|
|
386
|
-
error: {
|
|
387
|
-
code: -32601,
|
|
388
|
-
message: `Method not found: ${method}`,
|
|
389
|
-
},
|
|
390
|
-
};
|
|
391
|
-
} catch (err) {
|
|
392
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
393
|
-
return {
|
|
394
|
-
jsonrpc: '2.0',
|
|
395
|
-
id: request.id,
|
|
396
|
-
error: {
|
|
397
|
-
code: -32603,
|
|
398
|
-
message,
|
|
399
|
-
},
|
|
400
|
-
};
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
export function startMcpServer(customWorkspaceRoot?: string): void {
|
|
405
|
-
const rl = readline.createInterface({
|
|
406
|
-
input: process.stdin,
|
|
407
|
-
terminal: false,
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
rl.on('line', async (line) => {
|
|
411
|
-
const trimmed = line.trim();
|
|
412
|
-
if (!trimmed) return;
|
|
413
|
-
|
|
414
|
-
let parsed: JsonRpcRequest;
|
|
415
|
-
try {
|
|
416
|
-
parsed = JSON.parse(trimmed) as JsonRpcRequest;
|
|
417
|
-
} catch {
|
|
418
|
-
const parseErr: JsonRpcResponse = {
|
|
419
|
-
jsonrpc: '2.0',
|
|
420
|
-
id: null,
|
|
421
|
-
error: { code: -32700, message: 'Parse error' },
|
|
422
|
-
};
|
|
423
|
-
process.stdout.write(`${JSON.stringify(parseErr)}\n`);
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const response = await handleMcpJsonRpcRequest(parsed, customWorkspaceRoot);
|
|
428
|
-
if (response) {
|
|
429
|
-
process.stdout.write(`${JSON.stringify(response)}\n`);
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const isDirectExecution =
|
|
435
|
-
Boolean(process.argv[1]) &&
|
|
436
|
-
(() => {
|
|
437
|
-
try {
|
|
438
|
-
return import.meta.url === pathToFileURL(fs.realpathSync(process.argv[1])).href;
|
|
439
|
-
} catch {
|
|
440
|
-
return false;
|
|
441
|
-
}
|
|
442
|
-
})();
|
|
443
|
-
|
|
444
|
-
if (isDirectExecution) {
|
|
445
|
-
startMcpServer();
|
|
446
|
-
}
|