refactor: Merge MCP tools for comparing heap snapshots (#2281)

Instead of two separate MCP tools, we can have one instead which takes
classIndex as optional argument.

Co-authored-by: Dominik Inführ <dinfuehr@chromium.org>
This commit is contained in:
Dominik Inführ
2026-07-02 15:26:36 +02:00
committed by GitHub
parent 8d8cf1299d
commit 3f4a49a89a
7 changed files with 72 additions and 110 deletions
+2 -3
View File
@@ -514,11 +514,10 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
- [`take_snapshot`](docs/tool-reference.md#take_snapshot)
- [`screencast_start`](docs/tool-reference.md#screencast_start)
- [`screencast_stop`](docs/tool-reference.md#screencast_stop)
- **Memory** (11 tools)
- **Memory** (10 tools)
- [`take_heapsnapshot`](docs/tool-reference.md#take_heapsnapshot)
- [`close_heapsnapshot`](docs/tool-reference.md#close_heapsnapshot)
- [`compare_heapsnapshots_class_nodes`](docs/tool-reference.md#compare_heapsnapshots_class_nodes)
- [`compare_heapsnapshots_summary`](docs/tool-reference.md#compare_heapsnapshots_summary)
- [`compare_heapsnapshots`](docs/tool-reference.md#compare_heapsnapshots)
- [`get_heapsnapshot_class_nodes`](docs/tool-reference.md#get_heapsnapshot_class_nodes)
- [`get_heapsnapshot_details`](docs/tool-reference.md#get_heapsnapshot_details)
- [`get_heapsnapshot_dominators`](docs/tool-reference.md#get_heapsnapshot_dominators)
+5 -17
View File
@@ -39,11 +39,10 @@
- [`take_snapshot`](#take_snapshot)
- [`screencast_start`](#screencast_start)
- [`screencast_stop`](#screencast_stop)
- **[Memory](#memory)** (11 tools)
- **[Memory](#memory)** (10 tools)
- [`take_heapsnapshot`](#take_heapsnapshot)
- [`close_heapsnapshot`](#close_heapsnapshot)
- [`compare_heapsnapshots_class_nodes`](#compare_heapsnapshots_class_nodes)
- [`compare_heapsnapshots_summary`](#compare_heapsnapshots_summary)
- [`compare_heapsnapshots`](#compare_heapsnapshots)
- [`get_heapsnapshot_class_nodes`](#get_heapsnapshot_class_nodes)
- [`get_heapsnapshot_details`](#get_heapsnapshot_details)
- [`get_heapsnapshot_dominators`](#get_heapsnapshot_dominators)
@@ -464,26 +463,15 @@ in the DevTools Elements panel (if any).
---
### `compare_heapsnapshots_class_nodes`
### `compare_heapsnapshots`
**Description:** Loads two memory heapsnapshots and returns the diff details (added/deleted instances) for a specific class. (requires flag: --memoryDebugging=true)
**Parameters:**
- **baseFilePath** (string) **(required)**: A path to the base .heapsnapshot file (earlier snapshot).
- **classIndex** (number) **(required)**: 0-based index of the class in the summary list to filter results, showing individual objects.
- **currentFilePath** (string) **(required)**: A path to the current .heapsnapshot file (later snapshot).
---
### `compare_heapsnapshots_summary`
**Description:** Loads two memory heapsnapshots and returns the summary diff between them (classes with changes). (requires flag: --memoryDebugging=true)
**Description:** Loads two memory heapsnapshots and returns the comparison. If classIndex is provided, returns detailed diff for that class, otherwise returns summary diff. (requires flag: --memoryDebugging=true)
**Parameters:**
- **baseFilePath** (string) **(required)**: A path to the base .heapsnapshot file (earlier snapshot).
- **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.
---
+4 -25
View File
@@ -108,9 +108,9 @@ export const commands: Commands = {
},
},
},
compare_heapsnapshots_class_nodes: {
compare_heapsnapshots: {
description:
'Loads two memory heapsnapshots and returns the diff details (added/deleted instances) for a specific class. (requires flag: --memoryDebugging=true)',
'Loads two memory heapsnapshots and returns the comparison. If classIndex is provided, returns detailed diff for that class, otherwise returns summary diff. (requires flag: --memoryDebugging=true)',
category: 'Memory',
args: {
baseFilePath: {
@@ -131,29 +131,8 @@ export const commands: Commands = {
name: 'classIndex',
type: 'number',
description:
'0-based index of the class in the summary list to filter results, showing individual objects.',
required: true,
},
},
},
compare_heapsnapshots_summary: {
description:
'Loads two memory heapsnapshots and returns the summary diff between them (classes with changes). (requires flag: --memoryDebugging=true)',
category: 'Memory',
args: {
baseFilePath: {
name: 'baseFilePath',
type: 'string',
description:
'A path to the base .heapsnapshot file (earlier snapshot).',
required: true,
},
currentFilePath: {
name: 'currentFilePath',
type: 'string',
description:
'A path to the current .heapsnapshot file (later snapshot).',
required: true,
'Optional 0-based index of the class in the summary list to filter results, showing individual objects.',
required: false,
},
},
},
+20 -1
View File
@@ -826,7 +826,8 @@
"name": "class_index",
"argType": "number"
}
]
],
"isDeprecated": true
},
{
"name": "compare_heapsnapshots_summary",
@@ -839,6 +840,24 @@
"name": "current_file_path_length",
"argType": "number"
}
],
"isDeprecated": true
},
{
"name": "compare_heapsnapshots",
"args": [
{
"name": "base_file_path_length",
"argType": "number"
},
{
"name": "current_file_path_length",
"argType": "number"
},
{
"name": "class_index",
"argType": "number"
}
]
}
]
+19 -39
View File
@@ -281,39 +281,10 @@ export const getHeapSnapshotDominators = defineTool({
},
});
export const compareHeapSnapshotsSummary = defineTool({
name: 'compare_heapsnapshots_summary',
export const compareHeapSnapshots = defineTool({
name: 'compare_heapsnapshots',
description:
'Loads two memory heapsnapshots and returns the summary diff between them (classes with changes).',
annotations: {
category: ToolCategory.MEMORY,
readOnlyHint: true,
conditions: ['memoryDebugging'],
},
verifyFilesSchema: ['baseFilePath', 'currentFilePath'],
schema: {
baseFilePath: zod
.string()
.describe('A path to the base .heapsnapshot file (earlier snapshot).'),
currentFilePath: zod
.string()
.describe('A path to the current .heapsnapshot file (later snapshot).'),
},
blockedByDialog: false,
handler: async (request, response, context) => {
const diff = await context.getHeapSnapshotClassDiffs(
request.params.baseFilePath,
request.params.currentFilePath,
);
response.setHeapSnapshotClassDiffs(diff);
},
});
export const compareHeapSnapshotsClassNodes = defineTool({
name: 'compare_heapsnapshots_class_nodes',
description:
'Loads two memory heapsnapshots and returns the diff details (added/deleted instances) for a specific class.',
'Loads two memory heapsnapshots and returns the comparison. If classIndex is provided, returns detailed diff for that class, otherwise returns summary diff.',
annotations: {
category: ToolCategory.MEMORY,
readOnlyHint: true,
@@ -329,17 +300,26 @@ export const compareHeapSnapshotsClassNodes = defineTool({
.describe('A path to the current .heapsnapshot file (later snapshot).'),
classIndex: zod
.number()
.optional()
.describe(
'0-based index of the class in the summary list to filter results, showing individual objects.',
'Optional 0-based index of the class in the summary list to filter results, showing individual objects.',
),
},
blockedByDialog: false,
handler: async (request, response, context) => {
const classDiffResult = await context.getHeapSnapshotDetailedClassDiff(
request.params.baseFilePath,
request.params.currentFilePath,
request.params.classIndex,
);
response.setHeapSnapshotDetailedClassDiff(classDiffResult);
if (request.params.classIndex !== undefined) {
const classDiffResult = await context.getHeapSnapshotDetailedClassDiff(
request.params.baseFilePath,
request.params.currentFilePath,
request.params.classIndex,
);
response.setHeapSnapshotDetailedClassDiff(classDiffResult);
} else {
const diff = await context.getHeapSnapshotClassDiffs(
request.params.baseFilePath,
request.params.currentFilePath,
);
response.setHeapSnapshotClassDiffs(diff);
}
},
});
+11 -11
View File
@@ -1,13 +1,4 @@
exports[`memory > compare_heapsnapshots_class_nodes > compare heap-1 to heap-2 with classIndex filter 1`] = `
## Heap Snapshot Data
### Heap Snapshot Detailed Diff
NewObject: # new: 2, # deleted: 0, # delta: +2, alloc size: +0.1 kB, freed size: 0.0 kB, size delta: +0.1 kB
Objects:
+ @35179 (self_size: 0.1 kB)
+ @35181 (self_size: 0.1 kB)
`;
exports[`memory > compare_heapsnapshots_summary > compare heap-1 to heap-2 1`] = `
exports[`memory > compare_heapsnapshots > compare heap-1 to heap-2 1`] = `
## Heap Snapshot Data
### Heap Snapshot Diff
index,className,addedCount,removedCount,countDelta,addedSize,removedSize,sizeDelta
@@ -23,7 +14,16 @@ index,className,addedCount,removedCount,countDelta,addedSize,removedSize,sizeDel
9,InitialObject,0,2,-2,0.0 kB,0.1 kB,-0.1 kB
`;
exports[`memory > compare_heapsnapshots_summary > compare heap-2 to heap-3 1`] = `
exports[`memory > compare_heapsnapshots > compare heap-1 to heap-2 with classIndex filter 1`] = `
## Heap Snapshot Data
### Heap Snapshot Detailed Diff
NewObject: # new: 2, # deleted: 0, # delta: +2, alloc size: +0.1 kB, freed size: 0.0 kB, size delta: +0.1 kB
Objects:
+ @35179 (self_size: 0.1 kB)
+ @35181 (self_size: 0.1 kB)
`;
exports[`memory > compare_heapsnapshots > compare heap-2 to heap-3 1`] = `
## Heap Snapshot Data
### Heap Snapshot Diff
index,className,addedCount,removedCount,countDelta,addedSize,removedSize,sizeDelta
+11 -14
View File
@@ -21,8 +21,7 @@ import {
getHeapSnapshotRetainingPaths,
getHeapSnapshotEdges,
getHeapSnapshotDominators,
compareHeapSnapshotsSummary,
compareHeapSnapshotsClassNodes,
compareHeapSnapshots,
} from '../../src/tools/memory.js';
import {withMcpContext} from '../utils.js';
@@ -364,7 +363,7 @@ describe('memory', () => {
});
});
describe('compare_heapsnapshots_summary', () => {
describe('compare_heapsnapshots', () => {
it('compare heap-1 to heap-2', async t => {
await withMcpContext(async (response, context) => {
const filePathA = join(
@@ -376,14 +375,14 @@ describe('memory', () => {
'tests/fixtures/heap-2.heapsnapshot',
);
await compareHeapSnapshotsSummary.handler(
await compareHeapSnapshots.handler(
{params: {baseFilePath: filePathA, currentFilePath: filePathB}},
response,
context,
);
const responseData = await response.handle(
compareHeapSnapshotsSummary.name,
compareHeapSnapshots.name,
context,
);
const output = responseData.content
@@ -405,14 +404,14 @@ describe('memory', () => {
'tests/fixtures/heap-3.heapsnapshot',
);
await compareHeapSnapshotsSummary.handler(
await compareHeapSnapshots.handler(
{params: {baseFilePath: filePathA, currentFilePath: filePathB}},
response,
context,
);
const responseData = await response.handle(
compareHeapSnapshotsSummary.name,
compareHeapSnapshots.name,
context,
);
const output = responseData.content
@@ -422,9 +421,7 @@ describe('memory', () => {
t.assert.snapshot(output);
});
});
});
describe('compare_heapsnapshots_class_nodes', () => {
it('compare heap-1 to heap-2 with classIndex filter', async t => {
await withMcpContext(async (response, context) => {
const filePathA = join(
@@ -436,7 +433,7 @@ describe('memory', () => {
'tests/fixtures/heap-2.heapsnapshot',
);
await compareHeapSnapshotsClassNodes.handler(
await compareHeapSnapshots.handler(
{
params: {
baseFilePath: filePathA,
@@ -449,7 +446,7 @@ describe('memory', () => {
);
const responseData = await response.handle(
compareHeapSnapshotsClassNodes.name,
compareHeapSnapshots.name,
context,
);
const output = responseData.content
@@ -460,7 +457,7 @@ describe('memory', () => {
});
});
it('compare heap-1 to heap-2 with invalid classIndex throws error', async t => {
it('compare heap-1 to heap-2 with invalid classIndex throws error', async () => {
await withMcpContext(async (response, context) => {
const filePathA = join(
process.cwd(),
@@ -471,8 +468,8 @@ describe('memory', () => {
'tests/fixtures/heap-2.heapsnapshot',
);
await t.assert.rejects(
compareHeapSnapshotsClassNodes.handler(
await assert.rejects(
compareHeapSnapshots.handler(
{
params: {
baseFilePath: filePathA,