@@ -275,6 +275,83 @@ server.tool(
275275 } ,
276276) ;
277277
278+ server . tool (
279+ 'review-react-runtime' ,
280+ `Run this tool every time you propose a performance related change to verify if your suggestion actually improves performance.
281+ <requirements>
282+ This tool has some requirements on the code input:
283+ - The react code that is passed into this tool MUST contain an App functional component without arrow function.
284+ - DO NOT export anything since we can't parse export syntax with this tool.
285+ - Only import React from 'react' and use all hooks and imports using the React. prefix like React.useState and React.useEffect
286+ </requirements>
287+
288+ <goals>
289+ - LCP - loading speed: good ≤ 2.5 s, needs-improvement 2.5-4 s, poor > 4 s
290+ - INP - input responsiveness: good ≤ 200 ms, needs-improvement 200-500 ms, poor > 500 ms
291+ - CLS - visual stability: good ≤ 0.10, needs-improvement 0.10-0.25, poor > 0.25
292+ - (Optional: FCP ≤ 1.8 s, TTFB ≤ 0.8 s)
293+ </goals>
294+
295+ <evaluation>
296+ Classify each metric with the thresholds above. Identify the worst category in the order poor > needs-improvement > good.
297+ </evaluation>
298+
299+ <iterate>
300+ (repeat until every metric is good or two consecutive cycles show no gain)
301+ - Apply one focused change based on the failing metric plus React-specific guidance:
302+ - LCP: lazy-load off-screen images, inline critical CSS, preconnect, use React.lazy + Suspense for below-the-fold modules. if the user requests for it, use React Server Components for static content (Server Components).
303+ - INP: wrap non-critical updates in useTransition, avoid calling setState inside useEffect.
304+ - CLS: reserve space via explicit width/height or aspect-ratio, keep stable list keys, use fixed-size skeleton loaders, animate only transform/opacity, avoid inserting ads or banners without placeholders.
305+
306+ Stop when every metric is classified as good. Return the final metric table and the list of applied changes.
307+ </iterate>
308+ ` ,
309+ {
310+ text : z . string ( ) ,
311+ iterations : z . number ( ) . optional ( ) . default ( 2 ) ,
312+ } ,
313+ async ( { text, iterations} ) => {
314+ try {
315+ const results = await measurePerformance ( text , iterations ) ;
316+ const formattedResults = `
317+ # React Component Performance Results
318+
319+ ## Mean Render Time
320+ ${ results . renderTime / iterations } ms
321+
322+ ## Mean Web Vitals
323+ - Cumulative Layout Shift (CLS): ${ results . webVitals . cls / iterations } ms
324+ - Largest Contentful Paint (LCP): ${ results . webVitals . lcp / iterations } ms
325+ - Interaction to Next Paint (INP): ${ results . webVitals . inp / iterations } ms
326+ - First Input Delay (FID): ${ results . webVitals . fid / iterations } ms
327+
328+ ## Mean React Profiler
329+ - Actual Duration: ${ results . reactProfiler . actualDuration / iterations } ms
330+ - Base Duration: ${ results . reactProfiler . baseDuration / iterations } ms
331+ ` ;
332+
333+ return {
334+ content : [
335+ {
336+ type : 'text' as const ,
337+ text : formattedResults ,
338+ } ,
339+ ] ,
340+ } ;
341+ } catch ( error ) {
342+ return {
343+ isError : true ,
344+ content : [
345+ {
346+ type : 'text' as const ,
347+ text : `Error measuring performance: ${ error . message } \n\n${ error . stack } ` ,
348+ } ,
349+ ] ,
350+ } ;
351+ }
352+ } ,
353+ ) ;
354+
278355server . prompt ( 'review-react-code' , ( ) => ( {
279356 messages : [
280357 {
@@ -354,129 +431,6 @@ Server Components - Shift data-heavy logic to the server whenever possible. Brea
354431 ] ,
355432} ) ) ;
356433
357- server . tool (
358- 'review-react-runtime' ,
359- `Run this tool every time you propose a performance related change to verify if your suggestion actually improves performance.
360- <requirements>
361- This tool has some requirements on the code input:
362- - The react code that is passed into this tool MUST contain an App functional component without arrow function.
363- - DO NOT export anything since we can't parse export syntax with this tool.
364- - Only import React from 'react' and use all hooks and imports using the React. prefix like React.useState and React.useEffect
365- </requirements>
366-
367- <goals>
368- - LCP - loading speed: good ≤ 2.5 s, needs-improvement 2.5-4 s, poor > 4 s
369- - INP - input responsiveness: good ≤ 200 ms, needs-improvement 200-500 ms, poor > 500 ms
370- - CLS - visual stability: good ≤ 0.10, needs-improvement 0.10-0.25, poor > 0.25
371- - (Optional: FCP ≤ 1.8 s, TTFB ≤ 0.8 s)
372- </goals>
373-
374- <evaluation>
375- Classify each metric with the thresholds above. Identify the worst category in the order poor > needs-improvement > good.
376- </evaluation>
377-
378- <iterate>
379- (repeat until every metric is good or two consecutive cycles show no gain)
380- - Apply one focused change based on the failing metric plus React-specific guidance:
381- - LCP: lazy-load off-screen images, inline critical CSS, preconnect, use React.lazy + Suspense for below-the-fold modules. if the user requests for it, use React Server Components for static content (Server Components).
382- - INP: wrap non-critical updates in useTransition, avoid calling setState inside useEffect.
383- - CLS: reserve space via explicit width/height or aspect-ratio, keep stable list keys, use fixed-size skeleton loaders, animate only transform/opacity, avoid inserting ads or banners without placeholders.
384-
385- Stop when every metric is classified as good. Return the final metric table and the list of applied changes.
386- </iterate>
387- ` ,
388- {
389- text : z . string ( ) ,
390- } ,
391- async ( { text} ) => {
392- try {
393- const iterations = 20 ;
394-
395- let perfData = {
396- renderTime : 0 ,
397- webVitals : {
398- cls : 0 ,
399- lcp : 0 ,
400- inp : 0 ,
401- fid : 0 ,
402- ttfb : 0 ,
403- } ,
404- reactProfilerMetrics : {
405- id : 0 ,
406- phase : 0 ,
407- actualDuration : 0 ,
408- baseDuration : 0 ,
409- startTime : 0 ,
410- commitTime : 0 ,
411- } ,
412- error : null ,
413- } ;
414-
415- for ( let i = 0 ; i < iterations ; i ++ ) {
416- const performanceResults = await measurePerformance ( text ) ;
417- perfData . renderTime += performanceResults . renderTime ;
418- perfData . webVitals . cls += performanceResults . webVitals . cls || 0 ;
419- perfData . webVitals . lcp += performanceResults . webVitals . lcp || 0 ;
420- perfData . webVitals . inp += performanceResults . webVitals . inp || 0 ;
421- perfData . webVitals . fid += performanceResults . webVitals . fid || 0 ;
422- perfData . webVitals . ttfb += performanceResults . webVitals . ttfb || 0 ;
423-
424- perfData . reactProfilerMetrics . id +=
425- performanceResults . reactProfilerMetrics . actualDuration || 0 ;
426- perfData . reactProfilerMetrics . phase +=
427- performanceResults . reactProfilerMetrics . phase || 0 ;
428- perfData . reactProfilerMetrics . actualDuration +=
429- performanceResults . reactProfilerMetrics . actualDuration || 0 ;
430- perfData . reactProfilerMetrics . baseDuration +=
431- performanceResults . reactProfilerMetrics . baseDuration || 0 ;
432- perfData . reactProfilerMetrics . startTime +=
433- performanceResults . reactProfilerMetrics . startTime || 0 ;
434- perfData . reactProfilerMetrics . commitTime +=
435- performanceResults . reactProfilerMetrics . commitTime || 0 ;
436- }
437-
438- const formattedResults = `
439- # React Component Performance Results
440-
441- ## Mean Render Time
442- ${ perfData . renderTime / iterations } ms
443-
444- ## Mean Web Vitals
445- - Cumulative Layout Shift (CLS): ${ perfData . webVitals . cls / iterations }
446- - Largest Contentful Paint (LCP): ${ perfData . webVitals . lcp / iterations } ms
447- - Interaction to Next Paint (INP): ${ perfData . webVitals . inp / iterations } ms
448- - First Input Delay (FID): ${ perfData . webVitals . fid / iterations } ms
449- - Time to First Byte (TTFB): ${ perfData . webVitals . ttfb / iterations } ms
450-
451- ## Mean React Profiler
452- - Actual Duration: ${ perfData . reactProfilerMetrics . actualDuration / iterations } ms
453- - Base Duration: ${ perfData . reactProfilerMetrics . baseDuration / iterations } ms
454- - Start Time: ${ perfData . reactProfilerMetrics . startTime / iterations } ms
455- - Commit Time: ${ perfData . reactProfilerMetrics . commitTime / iterations } ms
456- ` ;
457-
458- return {
459- content : [
460- {
461- type : 'text' as const ,
462- text : formattedResults ,
463- } ,
464- ] ,
465- } ;
466- } catch ( error ) {
467- return {
468- isError : true ,
469- content : [
470- {
471- type : 'text' as const ,
472- text : `Error measuring performance: ${ error . message } \n\n${ error . stack } ` ,
473- } ,
474- ] ,
475- } ;
476- }
477- } ,
478- ) ;
479-
480434async function main ( ) {
481435 const transport = new StdioServerTransport ( ) ;
482436 await server . connect ( transport ) ;
0 commit comments