mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-09-14 19:45:30 +08:00
feat: Adds close_heapsnapshot MCP tool (#2174)
This commit adds the close_heapsnapshot MCP tool such that the coding agent can close heap snapshots again. Co-authored-by: Dominik Inführ <dinfuehr@chromium.org> Co-authored-by: Nicholas Roscino <nroscino@google.com>
This commit is contained in:
@@ -514,8 +514,9 @@ 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** (5 tools)
|
||||
- **Memory** (6 tools)
|
||||
- [`take_heapsnapshot`](docs/tool-reference.md#take_heapsnapshot)
|
||||
- [`close_heapsnapshot`](docs/tool-reference.md#close_heapsnapshot)
|
||||
- [`get_heapsnapshot_class_nodes`](docs/tool-reference.md#get_heapsnapshot_class_nodes)
|
||||
- [`get_heapsnapshot_details`](docs/tool-reference.md#get_heapsnapshot_details)
|
||||
- [`get_heapsnapshot_retainers`](docs/tool-reference.md#get_heapsnapshot_retainers)
|
||||
|
||||
+12
-1
@@ -39,8 +39,9 @@
|
||||
- [`take_snapshot`](#take_snapshot)
|
||||
- [`screencast_start`](#screencast_start)
|
||||
- [`screencast_stop`](#screencast_stop)
|
||||
- **[Memory](#memory)** (5 tools)
|
||||
- **[Memory](#memory)** (6 tools)
|
||||
- [`take_heapsnapshot`](#take_heapsnapshot)
|
||||
- [`close_heapsnapshot`](#close_heapsnapshot)
|
||||
- [`get_heapsnapshot_class_nodes`](#get_heapsnapshot_class_nodes)
|
||||
- [`get_heapsnapshot_details`](#get_heapsnapshot_details)
|
||||
- [`get_heapsnapshot_retainers`](#get_heapsnapshot_retainers)
|
||||
@@ -454,6 +455,16 @@ in the DevTools Elements panel (if any).
|
||||
|
||||
---
|
||||
|
||||
### `close_heapsnapshot`
|
||||
|
||||
**Description:** Closes a previously loaded memory heapsnapshot, freeing its memory. (requires flag: --experimentalMemory=true)
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **filePath** (string) **(required)**: A path to the .heapsnapshot file to close.
|
||||
|
||||
---
|
||||
|
||||
### `get_heapsnapshot_class_nodes`
|
||||
|
||||
**Description:** Loads a memory heapsnapshot and returns instances of a specific class with their IDs. (requires flag: --experimentalMemory=true)
|
||||
|
||||
@@ -201,12 +201,18 @@ export class HeapSnapshotManager {
|
||||
return {snapshot, worker: workerProxy};
|
||||
}
|
||||
|
||||
dispose(filePath: string): void {
|
||||
hasSnapshots(): boolean {
|
||||
return this.#snapshots.size > 0;
|
||||
}
|
||||
|
||||
dispose(filePath: string): boolean {
|
||||
const absolutePath = path.resolve(filePath);
|
||||
const cached = this.#snapshots.get(absolutePath);
|
||||
if (cached) {
|
||||
cached.worker.dispose();
|
||||
this.#snapshots.delete(absolutePath);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -889,4 +889,12 @@ export class McpContext implements Context {
|
||||
): Promise<DevTools.HeapSnapshotModel.HeapSnapshotModel.ItemsRange> {
|
||||
return await this.#heapSnapshotManager.getRetainers(filePath, nodeId);
|
||||
}
|
||||
|
||||
async closeHeapSnapshot(filePath: string): Promise<boolean> {
|
||||
return this.#heapSnapshotManager.dispose(filePath);
|
||||
}
|
||||
|
||||
hasHeapSnapshots(): boolean {
|
||||
return this.#heapSnapshotManager.hasSnapshots();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,19 @@ export const commands: Commands = {
|
||||
},
|
||||
},
|
||||
},
|
||||
close_heapsnapshot: {
|
||||
description:
|
||||
'Closes a previously loaded memory heapsnapshot, freeing its memory. (requires flag: --experimentalMemory=true)',
|
||||
category: 'Memory',
|
||||
args: {
|
||||
filePath: {
|
||||
name: 'filePath',
|
||||
type: 'string',
|
||||
description: 'A path to the .heapsnapshot file to close.',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
close_page: {
|
||||
description:
|
||||
'Closes the page by its index. The last open page cannot be closed.',
|
||||
|
||||
@@ -738,5 +738,14 @@
|
||||
"argType": "number"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "close_heapsnapshot",
|
||||
"args": [
|
||||
{
|
||||
"name": "file_path_length",
|
||||
"argType": "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -248,6 +248,7 @@ export type Context = Readonly<{
|
||||
filePath: string,
|
||||
nodeId: number,
|
||||
): Promise<DevTools.HeapSnapshotModel.HeapSnapshotModel.ItemsRange>;
|
||||
closeHeapSnapshot(filePath: string): Promise<boolean>;
|
||||
}>;
|
||||
|
||||
/**
|
||||
|
||||
@@ -154,3 +154,32 @@ export const getHeapSnapshotRetainers = defineTool({
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const closeHeapSnapshot = defineTool({
|
||||
name: 'close_heapsnapshot',
|
||||
description:
|
||||
'Closes a previously loaded memory heapsnapshot, freeing its memory.',
|
||||
annotations: {
|
||||
category: ToolCategory.MEMORY,
|
||||
readOnlyHint: false,
|
||||
conditions: ['experimentalMemory'],
|
||||
},
|
||||
verifyFilesSchema: ['filePath'],
|
||||
schema: {
|
||||
filePath: zod
|
||||
.string()
|
||||
.describe('A path to the .heapsnapshot file to close.'),
|
||||
},
|
||||
blockedByDialog: false,
|
||||
handler: async (request, response, context) => {
|
||||
const closed = await context.closeHeapSnapshot(request.params.filePath);
|
||||
if (!closed) {
|
||||
throw new Error(
|
||||
`Failed to close heap snapshot: ${request.params.filePath} was not loaded.`,
|
||||
);
|
||||
}
|
||||
response.appendResponseLine(
|
||||
`Closed heap snapshot: ${request.params.filePath}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
getHeapSnapshotDetails,
|
||||
getHeapSnapshotClassNodes,
|
||||
getHeapSnapshotRetainers,
|
||||
closeHeapSnapshot,
|
||||
} from '../../src/tools/memory.js';
|
||||
import {withMcpContext} from '../utils.js';
|
||||
|
||||
@@ -176,4 +177,50 @@ describe('memory', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('close_heapsnapshot', () => {
|
||||
it('with default options', async () => {
|
||||
await withMcpContext(async (response, context) => {
|
||||
const filePath = join(
|
||||
process.cwd(),
|
||||
'tests/fixtures/example.heapsnapshot',
|
||||
);
|
||||
|
||||
await getHeapSnapshotSummary.handler(
|
||||
{params: {filePath}},
|
||||
response,
|
||||
context,
|
||||
);
|
||||
|
||||
assert.ok(context.hasHeapSnapshots());
|
||||
|
||||
await closeHeapSnapshot.handler(
|
||||
{params: {filePath}},
|
||||
response,
|
||||
context,
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
response.responseLines.includes(`Closed heap snapshot: ${filePath}`),
|
||||
);
|
||||
assert.ok(!context.hasHeapSnapshots());
|
||||
});
|
||||
});
|
||||
|
||||
it('with non-existent snapshot', async () => {
|
||||
await withMcpContext(async (response, context) => {
|
||||
const filePath = join(
|
||||
process.cwd(),
|
||||
'tests/fixtures/example.heapsnapshot',
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
closeHeapSnapshot.handler({params: {filePath}}, response, context),
|
||||
{
|
||||
message: `Failed to close heap snapshot: ${filePath} was not loaded.`,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user