feat: Paginate by default in heap snapshot tools (#3201)

Show only the first page with 50 entries by default for the heap
snapshot MCP tools.
This commit is contained in:
Dominik Inführ authored and GitHub committed 2026-10-16 11:57:30 +00:00
1 parent 53ea78db7e
commit fb8375bd2d
4 files changed
+114 -76

No files matched your search

+16 -16
View File
@@ -525,8 +525,8 @@ in the DevTools Elements panel (if any).
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **pageIdx** (integer) _(optional)_: The zero-based page index. Defaults to 0.
- **pageSize** (integer) _(optional)_: The number of contexts to return per page. Defaults to 20.
- **pageIdx** (integer) _(optional)_: The zero-based page index. Defaults to 0 (the first page).
- **pageSize** (integer) _(optional)_: The maximum number of items to return per page. Defaults to 50.
- **retainedSize** (string) _(optional)_: Inclusive range for the dead-field score of a context (e.g. "10KB", "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.
- **scopeInfoNodeId** (integer) _(optional)_: Only return contexts declared by the scope with this ScopeInfo node id, as reported in the scope header of a previous call.
@@ -552,8 +552,8 @@ in the DevTools Elements panel (if any).
- **currentFilePath** (string) **(required)**: A path to the current .heapsnapshot file (later snapshot).
- **classIndex** (number) _(optional)_: Optional 0-based index of the class in the summary list to filter results, showing individual objects.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The zero-based page index. Defaults to 0 (the first page).
- **pageSize** (integer) _(optional)_: The maximum number of items to return per page. Defaults to 50.
---
@@ -568,8 +568,8 @@ in the DevTools Elements panel (if any).
- **filterName** (enum: "objectsRetainedByDetachedDomNodes", "objectsRetainedByConsole", "objectsRetainedByEventHandlers", "objectsRetainedByContexts", "sharedNativeContext", "noNativeContext", "attributedToSpecificNativeContext") _(optional)_: An optional filter to apply to the nodes.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **objectId** (number) _(optional)_: The object ID (nodeId) of the specific native context to filter by when filterName is attributedToSpecificNativeContext.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The zero-based page index. Defaults to 0 (the first page).
- **pageSize** (integer) _(optional)_: The maximum number of items to return per page. Defaults to 50.
---
@@ -583,8 +583,8 @@ in the DevTools Elements panel (if any).
- **filterName** (enum: "objectsRetainedByDetachedDomNodes", "objectsRetainedByConsole", "objectsRetainedByEventHandlers", "objectsRetainedByContexts", "sharedNativeContext", "noNativeContext", "attributedToSpecificNativeContext") _(optional)_: An optional filter to apply to the aggregates.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **objectId** (number) _(optional)_: The object ID (nodeId) of the specific native context to filter by when filterName is attributedToSpecificNativeContext.
- **pageIdx** (integer) _(optional)_: The page index for pagination of aggregates.
- **pageSize** (integer) _(optional)_: The page size for pagination of aggregates.
- **pageIdx** (integer) _(optional)_: The zero-based page index. Defaults to 0 (the first page).
- **pageSize** (integer) _(optional)_: The maximum number of items to return per page. Defaults to 50.
---
@@ -608,8 +608,8 @@ in the DevTools Elements panel (if any).
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The zero-based page index. Defaults to 0 (the first page).
- **pageSize** (integer) _(optional)_: The maximum number of items to return per page. Defaults to 50.
---
@@ -623,8 +623,8 @@ in the DevTools Elements panel (if any).
- **nodeId** (number) **(required)**: The node ID to get outgoing edges for.
- **excludePrimitives** (boolean) _(optional)_: Whether to exclude primitive target nodes. Default is true.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The zero-based page index. Defaults to 0 (the first page).
- **pageSize** (integer) _(optional)_: The maximum number of items to return per page. Defaults to 50.
- **retainedSize** (string) _(optional)_: Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-") for target nodes. A single value is treated as a minimum. Currently, only the lower bound is applied.
- **sortBy** (enum: "retainedSize", "selfSize", "name") _(optional)_: Sort order for edges. Default is retainedSize.
@@ -651,8 +651,8 @@ in the DevTools Elements panel (if any).
- **filePath** (string) **(required)**: A path to a .heapsnapshot file to read.
- **nodeId** (number) **(required)**: The node ID to get retainers for.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The zero-based page index. Defaults to 0 (the first page).
- **pageSize** (integer) _(optional)_: The maximum number of items to return per page. Defaults to 50.
---
@@ -693,8 +693,8 @@ in the DevTools Elements panel (if any).
- **isDetached** (boolean) _(optional)_: Whether to filter for detached DOM nodes.
- **maxNameLength** (integer) _(optional)_: Maximum length of names before truncation. Defaults to 100.
- **nodeType** (string) _(optional)_: Optional V8 node type filter (e.g. object, closure, string, array, code).
- **pageIdx** (integer) _(optional)_: The page index for pagination.
- **pageSize** (integer) _(optional)_: The page size for pagination.
- **pageIdx** (integer) _(optional)_: The zero-based page index. Defaults to 0 (the first page).
- **pageSize** (integer) _(optional)_: The maximum number of items to return per page. Defaults to 50.
- **propertyName** (string) _(optional)_: Optional property name filter for outgoing reference edges.
- **retainedSize** (string) _(optional)_: Inclusive retained size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.
- **selfSize** (string) _(optional)_: Inclusive self size range (e.g. "1MB-2MB", "-1MB", or "1MB-"). A single value is treated as a minimum.
+47 -16
View File
@@ -68,17 +68,20 @@ export const commands: Commands = {
pageIdx: {
name: 'pageIdx',
type: 'integer',
description: 'The zero-based page index. Defaults to 0.',
description:
'The zero-based page index. Defaults to 0 (the first page).',
required: false,
positional: false,
default: 0,
},
pageSize: {
name: 'pageSize',
type: 'integer',
description:
'The number of contexts to return per page. Defaults to 20.',
'The maximum number of items to return per page. Defaults to 50.',
required: false,
positional: false,
default: 50,
},
},
},
@@ -230,16 +233,20 @@ export const commands: Commands = {
pageIdx: {
name: 'pageIdx',
type: 'integer',
description: 'The page index for pagination.',
description:
'The zero-based page index. Defaults to 0 (the first page).',
required: false,
positional: false,
default: 0,
},
pageSize: {
name: 'pageSize',
type: 'integer',
description: 'The page size for pagination.',
description:
'The maximum number of items to return per page. Defaults to 50.',
required: false,
positional: false,
default: 50,
},
},
},
@@ -667,16 +674,20 @@ export const commands: Commands = {
pageIdx: {
name: 'pageIdx',
type: 'integer',
description: 'The page index for pagination.',
description:
'The zero-based page index. Defaults to 0 (the first page).',
required: false,
positional: false,
default: 0,
},
pageSize: {
name: 'pageSize',
type: 'integer',
description: 'The page size for pagination.',
description:
'The maximum number of items to return per page. Defaults to 50.',
required: false,
positional: false,
default: 50,
},
},
},
@@ -727,16 +738,20 @@ export const commands: Commands = {
pageIdx: {
name: 'pageIdx',
type: 'integer',
description: 'The page index for pagination of aggregates.',
description:
'The zero-based page index. Defaults to 0 (the first page).',
required: false,
positional: false,
default: 0,
},
pageSize: {
name: 'pageSize',
type: 'integer',
description: 'The page size for pagination of aggregates.',
description:
'The maximum number of items to return per page. Defaults to 50.',
required: false,
positional: false,
default: 50,
},
},
},
@@ -784,16 +799,20 @@ export const commands: Commands = {
pageIdx: {
name: 'pageIdx',
type: 'integer',
description: 'The page index for pagination.',
description:
'The zero-based page index. Defaults to 0 (the first page).',
required: false,
positional: false,
default: 0,
},
pageSize: {
name: 'pageSize',
type: 'integer',
description: 'The page size for pagination.',
description:
'The maximum number of items to return per page. Defaults to 50.',
required: false,
positional: false,
default: 50,
},
maxNameLength: {
name: 'maxNameLength',
@@ -859,16 +878,20 @@ export const commands: Commands = {
pageIdx: {
name: 'pageIdx',
type: 'integer',
description: 'The page index for pagination.',
description:
'The zero-based page index. Defaults to 0 (the first page).',
required: false,
positional: false,
default: 0,
},
pageSize: {
name: 'pageSize',
type: 'integer',
description: 'The page size for pagination.',
description:
'The maximum number of items to return per page. Defaults to 50.',
required: false,
positional: false,
default: 50,
},
},
},
@@ -931,16 +954,20 @@ export const commands: Commands = {
pageIdx: {
name: 'pageIdx',
type: 'integer',
description: 'The page index for pagination.',
description:
'The zero-based page index. Defaults to 0 (the first page).',
required: false,
positional: false,
default: 0,
},
pageSize: {
name: 'pageSize',
type: 'integer',
description: 'The page size for pagination.',
description:
'The maximum number of items to return per page. Defaults to 50.',
required: false,
positional: false,
default: 50,
},
},
},
@@ -1686,16 +1713,20 @@ export const commands: Commands = {
pageIdx: {
name: 'pageIdx',
type: 'integer',
description: 'The page index for pagination.',
description:
'The zero-based page index. Defaults to 0 (the first page).',
required: false,
positional: false,
default: 0,
},
pageSize: {
name: 'pageSize',
type: 'integer',
description: 'The page size for pagination.',
description:
'The maximum number of items to return per page. Defaults to 50.',
required: false,
positional: false,
default: 50,
},
maxNameLength: {
name: 'maxNameLength',
+12 -28
View File
@@ -30,19 +30,23 @@ const maxNameLengthSchema = zod
`Maximum length of names before truncation. Defaults to ${MAX_NAME_LENGTH}.`,
);
const DEFAULT_HEAP_SNAPSHOT_PAGE_SIZE = 50;
const pageIdxSchema = zod
.number()
.int()
.nonnegative()
.optional()
.describe('The page index for pagination.');
.default(0)
.describe('The zero-based page index. Defaults to 0 (the first page).');
const pageSizeSchema = zod
.number()
.int()
.positive()
.optional()
.describe('The page size for pagination.');
.default(DEFAULT_HEAP_SNAPSHOT_PAGE_SIZE)
.describe(
`The maximum number of items to return per page. Defaults to ${DEFAULT_HEAP_SNAPSHOT_PAGE_SIZE}.`,
);
export const takeHeapSnapshot = definePageTool(() => ({
name: 'take_heapsnapshot',
@@ -139,18 +143,8 @@ export const getHeapSnapshotDetails = defineBrowserlessTool(() => ({
'The object ID (nodeId) of the specific native context to filter by when filterName is attributedToSpecificNativeContext.',
),
maxNameLength: maxNameLengthSchema,
pageIdx: zod
.number()
.int()
.nonnegative()
.optional()
.describe('The page index for pagination of aggregates.'),
pageSize: zod
.number()
.int()
.positive()
.optional()
.describe('The page size for pagination of aggregates.'),
pageIdx: pageIdxSchema,
pageSize: pageSizeSchema,
},
blockedByDialog: false,
verifyFilesSchema: {
@@ -624,18 +618,8 @@ export const analyzeHeapSnapshotContexts = defineBrowserlessTool(() => ({
'Only return contexts declared by the scope with this ScopeInfo node id, as reported in the scope header of a previous call.',
),
maxNameLength: maxNameLengthSchema,
pageIdx: zod
.number()
.int()
.nonnegative()
.optional()
.describe('The zero-based page index. Defaults to 0.'),
pageSize: zod
.number()
.int()
.positive()
.optional()
.describe('The number of contexts to return per page. Defaults to 20.'),
pageIdx: pageIdxSchema,
pageSize: pageSizeSchema,
},
handler: async (request, response, context) => {
const analysis = await context.analyzeHeapSnapshotContexts(
+39 -16
View File
@@ -152,7 +152,7 @@ describe('memory', () => {
context.getHeapSnapshotAggregates.resolves(aggregates);
await getHeapSnapshotDetails(args).handler(
{params: {filePath: 'test.heapsnapshot'}},
{params: {filePath: 'test.heapsnapshot', pageIdx: 0, pageSize: 50}},
response,
context,
);
@@ -166,7 +166,7 @@ describe('memory', () => {
sinon.assert.calledOnceWithExactly(
response.setHeapSnapshotAggregates,
aggregates,
{pageIdx: undefined, pageSize: undefined, maxNameLength: undefined},
{pageIdx: 0, pageSize: 50, maxNameLength: undefined},
);
});
@@ -211,7 +211,14 @@ describe('memory', () => {
context.getHeapSnapshotNodesById.resolves(nodes);
await getHeapSnapshotClassNodes(args).handler(
{params: {filePath: 'test.heapsnapshot', id: 19}},
{
params: {
filePath: 'test.heapsnapshot',
id: 19,
pageIdx: 0,
pageSize: 50,
},
},
response,
context,
);
@@ -224,8 +231,8 @@ describe('memory', () => {
undefined,
);
sinon.assert.calledOnceWithExactly(response.setHeapSnapshotNodes, nodes, {
pageIdx: undefined,
pageSize: undefined,
pageIdx: 0,
pageSize: 50,
maxNameLength: undefined,
});
});
@@ -272,7 +279,14 @@ describe('memory', () => {
context.getHeapSnapshotRetainers.resolves(retainers);
await getHeapSnapshotRetainers(args).handler(
{params: {filePath: 'test.heapsnapshot', nodeId: 25341}},
{
params: {
filePath: 'test.heapsnapshot',
nodeId: 25341,
pageIdx: 0,
pageSize: 50,
},
},
response,
context,
);
@@ -285,7 +299,7 @@ describe('memory', () => {
sinon.assert.calledOnceWithExactly(
response.setHeapSnapshotNodes,
retainers,
{pageIdx: undefined, pageSize: undefined, maxNameLength: undefined},
{pageIdx: 0, pageSize: 50, maxNameLength: undefined},
);
});
@@ -492,7 +506,14 @@ describe('memory', () => {
context.getHeapSnapshotEdges.resolves(edges);
await getHeapSnapshotEdges(args).handler(
{params: {filePath: 'test.heapsnapshot', nodeId: 25341}},
{
params: {
filePath: 'test.heapsnapshot',
nodeId: 25341,
pageIdx: 0,
pageSize: 50,
},
},
response,
context,
);
@@ -508,8 +529,8 @@ describe('memory', () => {
},
);
sinon.assert.calledOnceWithExactly(response.setHeapSnapshotNodes, edges, {
pageIdx: undefined,
pageSize: undefined,
pageIdx: 0,
pageSize: 50,
maxNameLength: undefined,
});
});
@@ -589,6 +610,8 @@ describe('memory', () => {
params: {
baseFilePath: 'heap1.heapsnapshot',
currentFilePath: 'heap2.heapsnapshot',
pageIdx: 0,
pageSize: 50,
},
},
response,
@@ -603,7 +626,7 @@ describe('memory', () => {
sinon.assert.calledOnceWithExactly(
response.setHeapSnapshotClassDiffs,
diff,
{pageIdx: undefined, pageSize: undefined, maxNameLength: undefined},
{pageIdx: 0, pageSize: 50, maxNameLength: undefined},
);
});
@@ -673,7 +696,7 @@ describe('memory', () => {
context.getHeapSnapshotDuplicateStrings.resolves(duplicateStrings);
await getHeapSnapshotDuplicateStrings(args).handler(
{params: {filePath: 'test.heapsnapshot'}},
{params: {filePath: 'test.heapsnapshot', pageIdx: 0, pageSize: 50}},
response,
context,
);
@@ -685,7 +708,7 @@ describe('memory', () => {
sinon.assert.calledOnceWithExactly(
response.setHeapSnapshotDuplicateStrings,
duplicateStrings,
{pageIdx: undefined, pageSize: undefined, maxNameLength: undefined},
{pageIdx: 0, pageSize: 50, maxNameLength: undefined},
);
});
@@ -726,7 +749,7 @@ describe('memory', () => {
context.queryHeapSnapshotObjects.resolves(range);
await queryHeapSnapshotObjects(args).handler(
{params: {filePath: 'test.heapsnapshot'}},
{params: {filePath: 'test.heapsnapshot', pageIdx: 0, pageSize: 50}},
response,
context,
);
@@ -747,8 +770,8 @@ describe('memory', () => {
},
);
sinon.assert.calledOnceWithExactly(response.setHeapSnapshotNodes, range, {
pageIdx: undefined,
pageSize: undefined,
pageIdx: 0,
pageSize: 50,
maxNameLength: undefined,
});
});