@perfonext/render-mcp 0.1.0 → 0.3.0
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/README.md +77 -22
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/ingest/server.d.ts +53 -0
- package/dist/ingest/server.d.ts.map +1 -0
- package/dist/ingest/server.js +211 -0
- package/dist/ingest/server.js.map +1 -0
- package/dist/parser/analysis.d.ts +7 -2
- package/dist/parser/analysis.d.ts.map +1 -1
- package/dist/parser/analysis.js +359 -3
- package/dist/parser/analysis.js.map +1 -1
- package/dist/parser/react-profile.d.ts.map +1 -1
- package/dist/parser/react-profile.js +48 -2
- package/dist/parser/react-profile.js.map +1 -1
- package/dist/parser/react-scan-lite.d.ts +8 -0
- package/dist/parser/react-scan-lite.d.ts.map +1 -0
- package/dist/parser/react-scan-lite.js +114 -0
- package/dist/parser/react-scan-lite.js.map +1 -0
- package/dist/parser/types.d.ts +75 -0
- package/dist/parser/types.d.ts.map +1 -1
- package/dist/tools/begin-render-analysis.d.ts +3 -0
- package/dist/tools/begin-render-analysis.d.ts.map +1 -0
- package/dist/tools/begin-render-analysis.js +69 -0
- package/dist/tools/begin-render-analysis.js.map +1 -0
- package/dist/tools/compare-renders.d.ts +3 -0
- package/dist/tools/compare-renders.d.ts.map +1 -0
- package/dist/tools/compare-renders.js +50 -0
- package/dist/tools/compare-renders.js.map +1 -0
- package/dist/tools/get-captured-renders.d.ts +3 -0
- package/dist/tools/get-captured-renders.d.ts.map +1 -0
- package/dist/tools/get-captured-renders.js +77 -0
- package/dist/tools/get-captured-renders.js.map +1 -0
- package/dist/tools/get-hot-commits.d.ts.map +1 -1
- package/dist/tools/get-hot-commits.js +3 -2
- package/dist/tools/get-hot-commits.js.map +1 -1
- package/dist/tools/get-render-summary.js +1 -1
- package/dist/tools/get-render-summary.js.map +1 -1
- package/dist/tools/load-render-profile.d.ts.map +1 -1
- package/dist/tools/load-render-profile.js +3 -1
- package/dist/tools/load-render-profile.js.map +1 -1
- package/dist/tools/run-render-capture.d.ts +3 -0
- package/dist/tools/run-render-capture.d.ts.map +1 -0
- package/dist/tools/run-render-capture.js +59 -0
- package/dist/tools/run-render-capture.js.map +1 -0
- package/dist/tools/stop-render-capture.d.ts +3 -0
- package/dist/tools/stop-render-capture.d.ts.map +1 -0
- package/dist/tools/stop-render-capture.js +61 -0
- package/dist/tools/stop-render-capture.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getCaptureSession } from '../ingest/server.js';
|
|
3
|
+
export function registerRunRenderCapture(server) {
|
|
4
|
+
server.registerTool('run_render_capture', {
|
|
5
|
+
title: 'Run Render Capture',
|
|
6
|
+
description: 'Called after instrumentation is wired up (steps 1–3 of begin_render_analysis). ' +
|
|
7
|
+
'Ask the user how they want to generate renders before calling this tool:\n' +
|
|
8
|
+
' manual-interaction — start the dev server and the user clicks through the app.\n' +
|
|
9
|
+
' test-suite — run an existing automated test suite against the running app.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
sessionId: z.string().describe('The sessionId returned by begin_render_analysis.'),
|
|
12
|
+
method: z
|
|
13
|
+
.enum(['manual-interaction', 'test-suite'])
|
|
14
|
+
.describe('"manual-interaction": user navigates the app manually (fast, focused). ' +
|
|
15
|
+
'"test-suite": run an existing test suite (automated; scope to one file/feature to keep payload manageable).'),
|
|
16
|
+
},
|
|
17
|
+
}, async ({ sessionId, method }) => {
|
|
18
|
+
const session = getCaptureSession(sessionId);
|
|
19
|
+
if (!session || session.status !== 'active') {
|
|
20
|
+
return {
|
|
21
|
+
content: [{
|
|
22
|
+
type: 'text',
|
|
23
|
+
text: JSON.stringify({ error: `No active session for sessionId "${sessionId}". Call begin_render_analysis first.` }, null, 2),
|
|
24
|
+
}],
|
|
25
|
+
isError: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (method === 'manual-interaction') {
|
|
29
|
+
const result = {
|
|
30
|
+
method,
|
|
31
|
+
sessionId,
|
|
32
|
+
instructions: [
|
|
33
|
+
'1. Start the dev server (e.g. `npm run dev`).',
|
|
34
|
+
'2. Open the app in your browser and interact with the pages/flows you want to profile.',
|
|
35
|
+
'3. When done, call stop_render_capture to end the session.',
|
|
36
|
+
].join('\n'),
|
|
37
|
+
nextStep: `stop_render_capture({ sessionId: "${sessionId}" })`,
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// test-suite
|
|
44
|
+
const result = {
|
|
45
|
+
method,
|
|
46
|
+
sessionId,
|
|
47
|
+
instructions: [
|
|
48
|
+
'1. Start the dev server if not already running (e.g. `npm run dev`).',
|
|
49
|
+
'2. Run your test suite. Scope to a single test file or feature to keep the payload manageable.',
|
|
50
|
+
'3. When the run finishes, call stop_render_capture to end the session.',
|
|
51
|
+
].join('\n'),
|
|
52
|
+
nextStep: `stop_render_capture({ sessionId: "${sessionId}" })`,
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=run-render-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-render-capture.js","sourceRoot":"","sources":["../../src/tools/run-render-capture.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,UAAU,wBAAwB,CAAC,MAAiB;IACxD,MAAM,CAAC,YAAY,CAAC,oBAAoB,EAAE;QACxC,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EACT,iFAAiF;YACjF,4EAA4E;YAC5E,oFAAoF;YACpF,sFAAsF;QACxF,WAAW,EAAE;YACX,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;YAClF,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,oBAAoB,EAAE,YAAY,CAAC,CAAC;iBAC1C,QAAQ,CACP,yEAAyE;gBACzE,6GAA6G,CAC9G;SACJ;KACF,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE;QACjC,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC5C,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,oCAAoC,SAAS,sCAAsC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;qBAC9H,CAAC;gBACF,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,KAAK,oBAAoB,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG;gBACb,MAAM;gBACN,SAAS;gBACT,YAAY,EAAE;oBACZ,+CAA+C;oBAC/C,wFAAwF;oBACxF,4DAA4D;iBAC7D,CAAC,IAAI,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,qCAAqC,SAAS,MAAM;aAC/D,CAAC;YACF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aAC5E,CAAC;QACJ,CAAC;QAED,aAAa;QACb,MAAM,MAAM,GAAG;YACb,MAAM;YACN,SAAS;YACT,YAAY,EAAE;gBACZ,sEAAsE;gBACtE,gGAAgG;gBAChG,wEAAwE;aACzE,CAAC,IAAI,CAAC,IAAI,CAAC;YACZ,QAAQ,EAAE,qCAAqC,SAAS,MAAM;SAC/D,CAAC;QACF,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-render-capture.d.ts","sourceRoot":"","sources":["../../src/tools/stop-render-capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQpE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAiEjE"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { deleteCaptureSession, getCaptureSession, stopCaptureSession } from '../ingest/server.js';
|
|
3
|
+
import { adaptReactScanEvents } from '../parser/react-scan-lite.js';
|
|
4
|
+
import { storeRenderProfile } from '../store.js';
|
|
5
|
+
import { formatMs } from '../format.js';
|
|
6
|
+
export function registerStopRenderCapture(server) {
|
|
7
|
+
server.registerTool('stop_render_capture', {
|
|
8
|
+
title: 'Stop Render Capture',
|
|
9
|
+
description: 'Stop a live render-capture session, finalize the buffered events into a render profile, ' +
|
|
10
|
+
'and return a profileId you can use with get_render_summary, get_slow_components, ' +
|
|
11
|
+
'get_rerender_causes, and compare_renders.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
sessionId: z.string().describe('The sessionId returned by start_render_capture'),
|
|
14
|
+
},
|
|
15
|
+
}, async ({ sessionId }) => {
|
|
16
|
+
const session = getCaptureSession(sessionId);
|
|
17
|
+
if (!session) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: 'text', text: JSON.stringify({ error: `Session "${sessionId}" not found` }, null, 2) }],
|
|
20
|
+
isError: true,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (session.status === 'stopped') {
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: 'text', text: JSON.stringify({ error: `Session "${sessionId}" is already stopped` }, null, 2) }],
|
|
26
|
+
isError: true,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Mark stopped before processing so no new events are accepted.
|
|
30
|
+
stopCaptureSession(sessionId);
|
|
31
|
+
const commitCount = session.commits.length;
|
|
32
|
+
if (commitCount === 0) {
|
|
33
|
+
const warning = session.profilingAvailable === false
|
|
34
|
+
? 'No commits captured. The app reported profiling-hooks-status: false — ' +
|
|
35
|
+
'this usually means the app is running a production build. ' +
|
|
36
|
+
'Switch to `next dev` or alias react-dom to react-dom/profiling.'
|
|
37
|
+
: 'No commits captured. Make sure the instrumentation snippet was loaded ' +
|
|
38
|
+
'and you interacted with the app while the session was active.';
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: 'text', text: JSON.stringify({ warning, sessionId, commitCount: 0 }, null, 2) }],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const profile = adaptReactScanEvents(session.commits, sessionId);
|
|
44
|
+
storeRenderProfile(profile);
|
|
45
|
+
// Session data is no longer needed — free memory.
|
|
46
|
+
deleteCaptureSession(sessionId);
|
|
47
|
+
const result = {
|
|
48
|
+
profileId: profile.id,
|
|
49
|
+
sessionId,
|
|
50
|
+
commitCount: profile.commits.length,
|
|
51
|
+
componentCount: profile.components.length,
|
|
52
|
+
totalCommitDuration: formatMs(profile.totalCommitDuration),
|
|
53
|
+
profilingAvailable: session.profilingAvailable,
|
|
54
|
+
nextStep: `call get_render_summary with profileId "${profile.id}"`,
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=stop-render-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-render-capture.js","sourceRoot":"","sources":["../../src/tools/stop-render-capture.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@perfonext/render-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for analyzing Next.js React Profiler exports and
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "MCP server for analyzing Next.js React Profiler exports: render summaries, rerender causes, hot commits, and profile diffing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|