[mcp] Make tool more reliable and fix integration issues with babel (#33074)
## Summary Fix babel presets, and add a bit more context to the tool so that it is more reliable ## How did you test this change? Manually tested the mcp integrated with claude desktop
Jorge Cabiedes committed
Apr 30, 2025 at 15:42 UTC
d8074cbc798a500a5e76d19fe0c17edec22b2593
2 files changed
+48
-50
compiler/packages/react-mcp-server/src/index.ts
+19
-13
@@ -356,7 +356,14 @@ Server Components - Shift data-heavy logic to the server whenever possible. Brea
356
357
server.tool(
358
'review-react-runtime',
359
- 'Review the runtime of the code and get performance data to evaluate the proposed solution, the react code that is passed into this tool MUST contain an App component.',
359
+ `
360
+ Run this tool every time you propose a performance related change to verify if your suggestion actually improves performance.
361
+
362
+ This tool has some requirements on the code input:
363
+ - The react code that is passed into this tool MUST contain an App functional component without arrow function.
364
+ - DO NOT export anything since we can't parse export syntax with this tool.
365
+ - Only import React from 'react' and use all hooks and imports using the React. prefix like React.useState and React.useEffect
366
+ `,
367
{
368
text: z.string(),
369
},
@@ -387,25 +394,24 @@ server.tool(
394
for (let i = 0; i < iterations; i++) {
395
const performanceResults = await measurePerformance(text);
396
perfData.renderTime += performanceResults.renderTime;
390
- perfData.webVitals.cls += performanceResults.webVitals.cls?.value || 0;
391
- perfData.webVitals.lcp += performanceResults.webVitals.lcp?.value || 0;
392
- perfData.webVitals.inp += performanceResults.webVitals.inp?.value || 0;
393
- perfData.webVitals.fid += performanceResults.webVitals.fid?.value || 0;
394
- perfData.webVitals.ttfb +=
395
- performanceResults.webVitals.ttfb?.value || 0;
397
+ perfData.webVitals.cls += performanceResults.webVitals.cls || 0;
398
+ perfData.webVitals.lcp += performanceResults.webVitals.lcp || 0;
399
+ perfData.webVitals.inp += performanceResults.webVitals.inp || 0;
400
+ perfData.webVitals.fid += performanceResults.webVitals.fid || 0;
401
+ perfData.webVitals.ttfb += performanceResults.webVitals.ttfb || 0;
402
403
perfData.reactProfilerMetrics.id +=
398
- performanceResults.reactProfilerMetrics.actualDuration?.value || 0;
404
+ performanceResults.reactProfilerMetrics.actualDuration || 0;
405
perfData.reactProfilerMetrics.phase +=
400
- performanceResults.reactProfilerMetrics.phase?.value || 0;
406
+ performanceResults.reactProfilerMetrics.phase || 0;
407
perfData.reactProfilerMetrics.actualDuration +=
402
- performanceResults.reactProfilerMetrics.actualDuration?.value || 0;
408
+ performanceResults.reactProfilerMetrics.actualDuration || 0;
409
perfData.reactProfilerMetrics.baseDuration +=
404
- performanceResults.reactProfilerMetrics.baseDuration?.value || 0;
410
+ performanceResults.reactProfilerMetrics.baseDuration || 0;
411
perfData.reactProfilerMetrics.startTime +=
406
- performanceResults.reactProfilerMetrics.startTime?.value || 0;
412
+ performanceResults.reactProfilerMetrics.startTime || 0;
413
perfData.reactProfilerMetrics.commitTime +=
408
- performanceResults.reactProfilerMetrics.commitTim?.value || 0;
414
+ performanceResults.reactProfilerMetrics.commitTime || 0;
415
}
416
417
const formattedResults = `
compiler/packages/react-mcp-server/src/utils/runtimePerf.ts
+29
-37
@@ -1,28 +1,48 @@
1
import * as babel from '@babel/core';
2
import puppeteer from 'puppeteer';
3
4
-export async function measurePerformance(code: any) {
5
- let options = {
4
+export async function measurePerformance(code: string) {
5
+ const babelOptions = {
6
configFile: false,
7
babelrc: false,
8
- presets: [['@babel/preset-env'], '@babel/preset-react'],
8
+ presets: [
9
+ require.resolve('@babel/preset-env'),
10
+ require.resolve('@babel/preset-react'),
11
+ ],
12
};
13
11
- const parsed = await babel.parseAsync(code, options);
12
-
14
+ // Parse the code to AST
15
+ const parsed = await babel.parseAsync(code, babelOptions);
16
if (!parsed) {
17
throw new Error('Failed to parse code');
18
}
19
17
- const transpiled = await transformAsync(parsed);
20
+ // Transform AST to browser-compatible JavaScript
21
+ const transformResult = await babel.transformFromAstAsync(parsed, undefined, {
22
+ ...babelOptions,
23
+ filename: 'file.jsx',
24
+ plugins: [
25
+ () => ({
26
+ visitor: {
27
+ ImportDeclaration(
28
+ path: babel.NodePath<babel.types.ImportDeclaration>,
29
+ ) {
30
+ const value = path.node.source.value;
31
+ if (value === 'react' || value === 'react-dom') {
32
+ path.remove();
33
+ }
34
+ },
35
+ },
36
+ }),
37
+ ],
38
+ });
39
40
+ const transpiled = transformResult?.code || undefined;
41
if (!transpiled) {
42
throw new Error('Failed to transpile code');
43
}
44
23
- const browser = await puppeteer.launch({
24
- protocolTimeout: 600_000,
25
- });
45
+ const browser = await puppeteer.launch();
46
47
const page = await browser.newPage();
48
await page.setViewport({width: 1280, height: 720});
@@ -31,7 +51,6 @@ export async function measurePerformance(code: any) {
51
52
await page.waitForFunction(
53
'window.__RESULT__ !== undefined && (window.__RESULT__.renderTime !== null || window.__RESULT__.error !== null)',
34
- {timeout: 600_000},
54
);
55
56
const result = await page.evaluate(() => {
@@ -42,33 +61,6 @@ export async function measurePerformance(code: any) {
61
return result;
62
}
63
45
-/**
46
- * Transform AST into browser-compatible JavaScript
47
- * @param {babel.types.File} ast - The AST to transform
48
- * @param {Object} opts - Transformation options
49
- * @returns {Promise<string>} - The transpiled code
50
- */
51
-async function transformAsync(ast: babel.types.Node) {
52
- const result = await babel.transformFromAstAsync(ast, undefined, {
53
- filename: 'file.jsx',
54
- presets: [['@babel/preset-env'], '@babel/preset-react'],
55
- plugins: [
56
- () => ({
57
- visitor: {
58
- ImportDeclaration(path: any) {
59
- const value = path.node.source.value;
60
- if (value === 'react' || value === 'react-dom') {
61
- path.remove();
62
- }
63
- },
64
- },
65
- }),
66
- ],
67
- });
68
-
69
- return result?.code || '';
70
-}
71
-
64
function buildHtml(transpiled: string) {
65
const html = `
66
<!DOCTYPE html>