feat: Add additional params to get_heapsnapshot_edges (#2561)

Add additional parameters to get_heapsnapshot_edges for filtering and
sorting the emitted edges. By default this tool now does not print edges
to primitive values like true/false/null/undefined anymore unless
explicitly enabled. Edges are also sorted by retained size by default.
This commit is contained in:
Dominik Inführ
2026-08-12 13:49:47 +02:00
committed by GitHub
parent 7cebcf0f6d
commit b58c613f4b
8 changed files with 78 additions and 15 deletions
+3
View File
@@ -544,8 +544,11 @@ 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 outgoing edges for.
- **excludePrimitives** (boolean) _(optional)_: Whether to exclude primitive target nodes. Default is true.
- **minRetainedSize** (number) _(optional)_: Minimum retained size in bytes for target nodes.
- **pageIdx** (number) _(optional)_: The page index for pagination.
- **pageSize** (number) _(optional)_: The page size for pagination.
- **sortBy** (enum: "retainedSize", "selfSize", "name") _(optional)_: Sort order for edges. Default is retainedSize.
---
+8 -1
View File
@@ -43,6 +43,12 @@ export interface HeapSnapshotDetailedClassDiff extends HeapSnapshotClassDiff {
export type DuplicateStringGroup =
DevTools.HeapSnapshotModel.HeapSnapshotModel.DuplicateStringGroup;
export type HeapQueryOptions =
DevTools.HeapSnapshotModel.HeapSnapshotModel.HeapQueryOptions;
export type HeapEdgesQueryOptions =
DevTools.HeapSnapshotModel.HeapSnapshotModel.HeapEdgesQueryOptions;
export class HeapSnapshotManager {
#snapshotIdGenerator = createIdGenerator();
#snapshots = new Map<
@@ -241,13 +247,14 @@ export class HeapSnapshotManager {
async getEdges(
filePath: string,
nodeId: number,
options?: HeapEdgesQueryOptions,
): Promise<DevTools.HeapSnapshotModel.HeapSnapshotModel.ItemsRange> {
const snapshot = await this.getSnapshot(filePath);
const nodeIndex = await snapshot.nodeIndexForId(nodeId);
if (nodeIndex === undefined) {
throw new Error(`Node with ID ${nodeId} not found`);
}
const provider = snapshot.createEdgesProvider(nodeIndex);
const provider = snapshot.createEdgesProvider(nodeIndex, options);
return await provider.serializeItemsRange(0, Infinity);
}
+3 -1
View File
@@ -16,6 +16,7 @@ import type {
HeapSnapshotClassDiff,
HeapSnapshotDetailedClassDiff,
DuplicateStringGroup,
HeapEdgesQueryOptions,
} from './HeapSnapshotManager.js';
import {McpPage} from './McpPage.js';
import {type UncaughtError} from './PageCollector.js';
@@ -869,8 +870,9 @@ export class McpContext implements Context {
async getHeapSnapshotEdges(
filePath: string,
nodeId: number,
options?: HeapEdgesQueryOptions,
): Promise<DevTools.HeapSnapshotModel.HeapSnapshotModel.ItemsRange> {
return await this.#heapSnapshotManager.getEdges(filePath, nodeId);
return await this.#heapSnapshotManager.getEdges(filePath, nodeId, options);
}
async getHeapSnapshotClassDiffs(
+20
View File
@@ -500,6 +500,26 @@ export const commands: Commands = {
description: 'The node ID to get outgoing edges for.',
required: true,
},
sortBy: {
name: 'sortBy',
type: 'string',
description: 'Sort order for edges. Default is retainedSize.',
required: false,
enum: ['retainedSize', 'selfSize', 'name'],
},
minRetainedSize: {
name: 'minRetainedSize',
type: 'number',
description: 'Minimum retained size in bytes for target nodes.',
required: false,
},
excludePrimitives: {
name: 'excludePrimitives',
type: 'boolean',
description:
'Whether to exclude primitive target nodes. Default is true.',
required: false,
},
pageIdx: {
name: 'pageIdx',
type: 'number',
+12
View File
@@ -824,6 +824,18 @@
{
"name": "page_size",
"argType": "number"
},
{
"name": "sort_by",
"argType": "string"
},
{
"name": "min_retained_size",
"argType": "number"
},
{
"name": "exclude_primitives",
"argType": "boolean"
}
]
},
+2
View File
@@ -10,6 +10,7 @@ import type {
HeapSnapshotClassDiff,
HeapSnapshotDetailedClassDiff,
DuplicateStringGroup,
HeapEdgesQueryOptions,
} from '../HeapSnapshotManager.js';
import type {McpPage} from '../McpPage.js';
import {zod} from '../third_party/index.js';
@@ -290,6 +291,7 @@ export type Context = Readonly<{
getHeapSnapshotEdges(
filePath: string,
nodeId: number,
options?: HeapEdgesQueryOptions,
): Promise<DevTools.HeapSnapshotModel.HeapSnapshotModel.ItemsRange>;
getHeapSnapshotClassDiffs(
baseFilePath: string,
+17
View File
@@ -276,6 +276,18 @@ export const getHeapSnapshotEdges = defineTool({
schema: {
filePath: zod.string().describe('A path to a .heapsnapshot file to read.'),
nodeId: zod.number().describe('The node ID to get outgoing edges for.'),
sortBy: zod
.enum(['retainedSize', 'selfSize', 'name'])
.optional()
.describe('Sort order for edges. Default is retainedSize.'),
minRetainedSize: zod
.number()
.optional()
.describe('Minimum retained size in bytes for target nodes.'),
excludePrimitives: zod
.boolean()
.optional()
.describe('Whether to exclude primitive target nodes. Default is true.'),
pageIdx: zod.number().optional().describe('The page index for pagination.'),
pageSize: zod.number().optional().describe('The page size for pagination.'),
},
@@ -283,6 +295,11 @@ export const getHeapSnapshotEdges = defineTool({
const edges = await context.getHeapSnapshotEdges(
request.params.filePath,
request.params.nodeId,
{
sortBy: request.params.sortBy ?? 'retainedSize',
minRetainedSize: request.params.minRetainedSize,
excludePrimitives: request.params.excludePrimitives ?? true,
},
);
response.setHeapSnapshotNodes(edges, {
+13 -13
View File
@@ -296,8 +296,8 @@ value,count,totalSelfSize,totalRetainedSize,truncated,nodeIds
exports[`memory > get_heapsnapshot_edges > with pagination 1`] = `
## Heap Snapshot Data
name,type,nodeId,nodeName,selfSize,retainedSize
map,internal,25577,system / Map,0.0 kB,0.7 kB
constructor,property,25575,String,0.1 kB,0.4 kB
anchor,property,29295,anchor,0.0 kB,0.0 kB
Showing 1-2 of 56 (Page 1 of 28).
Next page: 1
`;
@@ -305,7 +305,10 @@ Next page: 1
exports[`memory > get_heapsnapshot_edges > with valid nodeId 1`] = `
## Heap Snapshot Data
name,type,nodeId,nodeName,selfSize,retainedSize
map,internal,25577,system / Map,0.0 kB,0.7 kB
constructor,property,25575,String,0.1 kB,0.4 kB
__proto__,property,25329,{constructor, __defineGetter__, __defineSetter__, …, get __proto__, set __proto__, toLocaleString},0.1 kB,0.3 kB
<symbol Symbol.iterator>,property,29391,[Symbol.iterator],0.0 kB,0.0 kB
anchor,property,29295,anchor,0.0 kB,0.0 kB
at,property,29297,at,0.0 kB,0.0 kB
big,property,29299,big,0.0 kB,0.0 kB
@@ -316,9 +319,9 @@ charCodeAt,property,29307,charCodeAt,0.0 kB,0.0 kB
codePointAt,property,29309,codePointAt,0.0 kB,0.0 kB
concat,property,29311,concat,0.0 kB,0.0 kB
endsWith,property,29313,endsWith,0.0 kB,0.0 kB
fixed,property,29319,fixed,0.0 kB,0.0 kB
fontcolor,property,29315,fontcolor,0.0 kB,0.0 kB
fontsize,property,29317,fontsize,0.0 kB,0.0 kB
fixed,property,29319,fixed,0.0 kB,0.0 kB
includes,property,29321,includes,0.0 kB,0.0 kB
indexOf,property,29323,indexOf,0.0 kB,0.0 kB
isWellFormed,property,29325,isWellFormed,0.0 kB,0.0 kB
@@ -338,28 +341,25 @@ search,property,29351,search,0.0 kB,0.0 kB
slice,property,29353,slice,0.0 kB,0.0 kB
small,property,29355,small,0.0 kB,0.0 kB
split,property,29357,split,0.0 kB,0.0 kB
startsWith,property,29369,startsWith,0.0 kB,0.0 kB
strike,property,29359,strike,0.0 kB,0.0 kB
sub,property,29361,sub,0.0 kB,0.0 kB
substr,property,29363,substr,0.0 kB,0.0 kB
substring,property,29365,substring,0.0 kB,0.0 kB
sup,property,29367,sup,0.0 kB,0.0 kB
startsWith,property,29369,startsWith,0.0 kB,0.0 kB
toString,property,29371,toString,0.0 kB,0.0 kB
toWellFormed,property,29373,toWellFormed,0.0 kB,0.0 kB
trim,property,29375,trim,0.0 kB,0.0 kB
trimStart,property,29377,trimStart,0.0 kB,0.0 kB
trimLeft,property,29377,trimStart,0.0 kB,0.0 kB
trimEnd,property,29379,trimEnd,0.0 kB,0.0 kB
trimRight,property,29379,trimEnd,0.0 kB,0.0 kB
toLocaleLowerCase,property,29381,toLocaleLowerCase,0.0 kB,0.0 kB
toLocaleUpperCase,property,29383,toLocaleUpperCase,0.0 kB,0.0 kB
toLowerCase,property,29385,toLowerCase,0.0 kB,0.0 kB
toString,property,29371,toString,0.0 kB,0.0 kB
toUpperCase,property,29387,toUpperCase,0.0 kB,0.0 kB
toWellFormed,property,29373,toWellFormed,0.0 kB,0.0 kB
trim,property,29375,trim,0.0 kB,0.0 kB
trimEnd,property,29379,trimEnd,0.0 kB,0.0 kB
trimLeft,property,29377,trimStart,0.0 kB,0.0 kB
trimRight,property,29379,trimEnd,0.0 kB,0.0 kB
trimStart,property,29377,trimStart,0.0 kB,0.0 kB
valueOf,property,29389,valueOf,0.0 kB,0.0 kB
<symbol Symbol.iterator>,property,29391,[Symbol.iterator],0.0 kB,0.0 kB
__proto__,property,25329,{constructor, __defineGetter__, __defineSetter__, …, get __proto__, set __proto__, toLocaleString},0.1 kB,0.3 kB
properties,internal,29393,system / PropertyArray,0.0 kB,0.0 kB
map,internal,25577,system / Map,0.0 kB,0.7 kB
1,hidden,71,,0.0 kB,0.0 kB
Showing 1-56 of 56 (Page 1 of 1).
`;