Repository: facebook/react
Pull request: [mcp] Make tool more reliable and fix integration issues with babel


## 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


Changed files:
filename: compiler/packages/react-mcp-server/src/index.ts
patch: @@ -356,7 +356,14 @@ Server Components - Shift data-heavy logic to the server whenever possible. Brea
 
 server.tool(
   'review-react-runtime',
-  '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.',
+  `
+  Run this tool every time you propose a performance related change to verify if your suggestion actually improves performance.
+
+  This tool has some requirements on the code input:
+  - The react code that is passed into this tool MUST contain an App functional component without arrow function.
+  - DO NOT export anything since we can't parse export syntax with this tool.
+  - Only import React from 'react' and use all hooks and imports using the React. prefix like React.useState and React.useEffect
+  `,
   {
     text: z.string(),
   },
@@ -387,25 +394,24 @@ server.tool(
       for (let i = 0; i < iterations; i++) {
         const performanceResults = await measurePerformance(text);
         perfData.renderTime += performanceResults.renderTime;
-        perfData.webVitals.cls += performanceResults.webVitals.cls?.value || 0;
-        perfData.webVitals.lcp += performanceResults.webVitals.lcp?.value || 0;
-        perfData.webVitals.inp += performanceResults.webVitals.inp?.value || 0;
-        perfData.webVitals.fid += performanceResults.webVitals.fid?.value || 0;
-        perfData.webVitals.ttfb +=
-          performanceResults.webVitals.ttfb?.value || 0;
+        perfData.webVitals.cls += performanceResults.webVitals.cls || 0;
+        perfData.webVitals.lcp += performanceResults.webVitals.lcp || 0;
+        perfData.webVitals.inp += performanceResults.webVitals.inp || 0;
+        perfData.webVitals.fid += performanceResults.webVitals.fid || 0;
+        perfData.webVitals.ttfb += performanceResults.webVitals.ttfb || 0;
 
         perfData.reactProfilerMetrics.id +=
-          performanceResults.reactProfilerMetrics.actualDuration?.value || 0;
+          performanceResults.reactProfilerMetrics.actualDuration || 0;
         perfData.reactProfilerMetrics.phase +=
-          performanceResults.reactProfilerMetrics.phase?.value || 0;
+          performanceResults.reactProfilerMetrics.phase || 0;
         perfData.reactProfilerMetrics.actualDuration +=
-          performanceResults.reactProfilerMetrics.actualDuration?.value || 0;
+          performanceResults.reactProfilerMetrics.actualDuration || 0;
         perfData.reactProfilerMetrics.baseDuration +=
-          performanceResults.reactProfilerMetrics.baseDuration?.value || 0;
+          performanceResults.reactProfilerMetrics.baseDuration || 0;
         perfData.reactProfilerMetrics.startTime +=
-          performanceResults.reactProfilerMetrics.startTime?.value || 0;
+          performanceResults.reactProfilerMetrics.startTime || 0;
         perfData.reactProfilerMetrics.commitTime +=
-          performanceResults.reactProfilerMetrics.commitTim?.value || 0;
+          performanceResults.reactProfilerMetrics.commitTime || 0;
       }
 
       const formattedResults = `

filename: compiler/packages/react-mcp-server/src/utils/runtimePerf.ts
patch: @@ -1,28 +1,48 @@
 import * as babel from '@babel/core';
 import puppeteer from 'puppeteer';
 
-export async function measurePerformance(code: any) {
-  let options = {
+export async function measurePerformance(code: string) {
+  const babelOptions = {
     configFile: false,
     babelrc: false,
-    presets: [['@babel/preset-env'], '@babel/preset-react'],
+    presets: [
+      require.resolve('@babel/preset-env'),
+      require.resolve('@babel/preset-react'),
+    ],
   };
 
-  const parsed = await babel.parseAsync(code, options);
-
+  // Parse the code to AST
+  const parsed = await babel.parseAsync(code, babelOptions);
   if (!parsed) {
     throw new Error('Failed to parse code');
   }
 
-  const transpiled = await transformAsync(parsed);
+  // Transform AST to browser-compatible JavaScript
+  const transformResult = await babel.transformFromAstAsync(parsed, undefined, {
+    ...babelOptions,
+    filename: 'file.jsx',
+    plugins: [
+      () => ({
+        visitor: {
+          ImportDeclaration(
+            path: babel.NodePath<babel.types.ImportDeclaration>,
+          ) {
+            const value = path.node.source.value;
+            if (value === 'react' || value === 'react-dom') {
+              path.remove();
+            }
+          },
+        },
+      }),
+    ],
+  });
 
+  const transpiled = transformResult?.code || undefined;
   if (!transpiled) {
     throw new Error('Failed to transpile code');
   }
 
-  const browser = await puppeteer.launch({
-    protocolTimeout: 600_000,
-  });
+  const browser = await puppeteer.launch();
 
   const page = await browser.newPage();
   await page.setViewport({width: 1280, height: 720});
@@ -31,7 +51,6 @@ export async function measurePerformance(code: any) {
 
   await page.waitForFunction(
     'window.__RESULT__ !== undefined && (window.__RESULT__.renderTime !== null || window.__RESULT__.error !== null)',
-    {timeout: 600_000},
   );
 
   const result = await page.evaluate(() => {
@@ -42,33 +61,6 @@ export async function measurePerformance(code: any) {
   return result;
 }
 
-/**
- * Transform AST into browser-compatible JavaScript
- * @param {babel.types.File} ast - The AST to transform
- * @param {Object} opts - Transformation options
- * @returns {Promise<string>} - The transpiled code
- */
-async function transformAsync(ast: babel.types.Node) {
-  const result = await babel.transformFromAstAsync(ast, undefined, {
-    filename: 'file.jsx',
-    presets: [['@babel/preset-env'], '@babel/preset-react'],
-    plugins: [
-      () => ({
-        visitor: {
-          ImportDeclaration(path: any) {
-            const value = path.node.source.value;
-            if (value === 'react' || value === 'react-dom') {
-              path.remove();
-            }
-          },
-        },
-      }),
-    ],
-  });
-
-  return result?.code || '';
-}
-
 function buildHtml(transpiled: string) {
   const html = `
         <!DOCTYPE html>