@memlab/mcp-server 2.0.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.
Files changed (107) hide show
  1. package/README.md +285 -0
  2. package/bin/memlab-mcp.js +3 -0
  3. package/dist/heap-state.d.ts +14 -0
  4. package/dist/heap-state.d.ts.map +1 -0
  5. package/dist/heap-state.js +25 -0
  6. package/dist/heap-state.js.map +1 -0
  7. package/dist/index.d.ts +12 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +71 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/tools/aggregate-nodes.d.ts +12 -0
  12. package/dist/tools/aggregate-nodes.d.ts.map +1 -0
  13. package/dist/tools/aggregate-nodes.js +136 -0
  14. package/dist/tools/aggregate-nodes.js.map +1 -0
  15. package/dist/tools/class-histogram.d.ts +12 -0
  16. package/dist/tools/class-histogram.d.ts.map +1 -0
  17. package/dist/tools/class-histogram.js +94 -0
  18. package/dist/tools/class-histogram.js.map +1 -0
  19. package/dist/tools/closure-inspection.d.ts +12 -0
  20. package/dist/tools/closure-inspection.d.ts.map +1 -0
  21. package/dist/tools/closure-inspection.js +107 -0
  22. package/dist/tools/closure-inspection.js.map +1 -0
  23. package/dist/tools/detached-dom.d.ts +12 -0
  24. package/dist/tools/detached-dom.d.ts.map +1 -0
  25. package/dist/tools/detached-dom.js +53 -0
  26. package/dist/tools/detached-dom.js.map +1 -0
  27. package/dist/tools/dominator-subtree.d.ts +12 -0
  28. package/dist/tools/dominator-subtree.d.ts.map +1 -0
  29. package/dist/tools/dominator-subtree.js +77 -0
  30. package/dist/tools/dominator-subtree.js.map +1 -0
  31. package/dist/tools/duplicated-strings.d.ts +12 -0
  32. package/dist/tools/duplicated-strings.d.ts.map +1 -0
  33. package/dist/tools/duplicated-strings.js +78 -0
  34. package/dist/tools/duplicated-strings.js.map +1 -0
  35. package/dist/tools/eval.d.ts +12 -0
  36. package/dist/tools/eval.d.ts.map +1 -0
  37. package/dist/tools/eval.js +119 -0
  38. package/dist/tools/eval.js.map +1 -0
  39. package/dist/tools/find-by-property.d.ts +12 -0
  40. package/dist/tools/find-by-property.d.ts.map +1 -0
  41. package/dist/tools/find-by-property.js +77 -0
  42. package/dist/tools/find-by-property.js.map +1 -0
  43. package/dist/tools/find-nodes-by-class.d.ts +12 -0
  44. package/dist/tools/find-nodes-by-class.d.ts.map +1 -0
  45. package/dist/tools/find-nodes-by-class.js +38 -0
  46. package/dist/tools/find-nodes-by-class.js.map +1 -0
  47. package/dist/tools/for-each.d.ts +12 -0
  48. package/dist/tools/for-each.d.ts.map +1 -0
  49. package/dist/tools/for-each.js +185 -0
  50. package/dist/tools/for-each.js.map +1 -0
  51. package/dist/tools/get-node.d.ts +12 -0
  52. package/dist/tools/get-node.d.ts.map +1 -0
  53. package/dist/tools/get-node.js +51 -0
  54. package/dist/tools/get-node.js.map +1 -0
  55. package/dist/tools/get-property.d.ts +12 -0
  56. package/dist/tools/get-property.d.ts.map +1 -0
  57. package/dist/tools/get-property.js +88 -0
  58. package/dist/tools/get-property.js.map +1 -0
  59. package/dist/tools/get-references.d.ts +12 -0
  60. package/dist/tools/get-references.d.ts.map +1 -0
  61. package/dist/tools/get-references.js +61 -0
  62. package/dist/tools/get-references.js.map +1 -0
  63. package/dist/tools/get-referrers.d.ts +12 -0
  64. package/dist/tools/get-referrers.d.ts.map +1 -0
  65. package/dist/tools/get-referrers.js +61 -0
  66. package/dist/tools/get-referrers.js.map +1 -0
  67. package/dist/tools/global-variables.d.ts +12 -0
  68. package/dist/tools/global-variables.d.ts.map +1 -0
  69. package/dist/tools/global-variables.js +331 -0
  70. package/dist/tools/global-variables.js.map +1 -0
  71. package/dist/tools/largest-objects.d.ts +12 -0
  72. package/dist/tools/largest-objects.d.ts.map +1 -0
  73. package/dist/tools/largest-objects.js +32 -0
  74. package/dist/tools/largest-objects.js.map +1 -0
  75. package/dist/tools/load-snapshot.d.ts +12 -0
  76. package/dist/tools/load-snapshot.d.ts.map +1 -0
  77. package/dist/tools/load-snapshot.js +39 -0
  78. package/dist/tools/load-snapshot.js.map +1 -0
  79. package/dist/tools/object-shape.d.ts +12 -0
  80. package/dist/tools/object-shape.d.ts.map +1 -0
  81. package/dist/tools/object-shape.js +86 -0
  82. package/dist/tools/object-shape.js.map +1 -0
  83. package/dist/tools/reports.d.ts +12 -0
  84. package/dist/tools/reports.d.ts.map +1 -0
  85. package/dist/tools/reports.js +540 -0
  86. package/dist/tools/reports.js.map +1 -0
  87. package/dist/tools/retainer-trace.d.ts +12 -0
  88. package/dist/tools/retainer-trace.d.ts.map +1 -0
  89. package/dist/tools/retainer-trace.js +68 -0
  90. package/dist/tools/retainer-trace.js.map +1 -0
  91. package/dist/tools/search-nodes.d.ts +12 -0
  92. package/dist/tools/search-nodes.d.ts.map +1 -0
  93. package/dist/tools/search-nodes.js +85 -0
  94. package/dist/tools/search-nodes.js.map +1 -0
  95. package/dist/tools/snapshot-summary.d.ts +12 -0
  96. package/dist/tools/snapshot-summary.d.ts.map +1 -0
  97. package/dist/tools/snapshot-summary.js +73 -0
  98. package/dist/tools/snapshot-summary.js.map +1 -0
  99. package/dist/tools/stale-collections.d.ts +12 -0
  100. package/dist/tools/stale-collections.d.ts.map +1 -0
  101. package/dist/tools/stale-collections.js +97 -0
  102. package/dist/tools/stale-collections.js.map +1 -0
  103. package/dist/utils.d.ts +82 -0
  104. package/dist/utils.d.ts.map +1 -0
  105. package/dist/utils.js +220 -0
  106. package/dist/utils.js.map +1 -0
  107. package/package.json +58 -0
package/README.md ADDED
@@ -0,0 +1,285 @@
1
+ # @memlab/mcp-server
2
+
3
+ An [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that wraps [MemLab](https://facebook.github.io/memlab/)'s heap analysis APIs, giving AI coding assistants (Claude Code, Cursor, etc.) interactive tools to explore JavaScript heap snapshots, find memory leaks, and identify optimization opportunities.
4
+
5
+ ## Quick Start
6
+
7
+ ### Option 1: npx (no install)
8
+
9
+ Add to your Claude Code MCP config (`~/.claude.json` or `.mcp.json`):
10
+
11
+ ```json
12
+ {
13
+ "mcpServers": {
14
+ "memlab": {
15
+ "type": "stdio",
16
+ "command": "npx",
17
+ "args": ["@memlab/mcp-server"]
18
+ }
19
+ }
20
+ }
21
+ ```
22
+
23
+ ### Option 2: Global install
24
+
25
+ ```bash
26
+ npm install -g @memlab/mcp-server
27
+ ```
28
+
29
+ Then configure:
30
+
31
+ ```json
32
+ {
33
+ "mcpServers": {
34
+ "memlab": {
35
+ "type": "stdio",
36
+ "command": "memlab-mcp"
37
+ }
38
+ }
39
+ }
40
+ ```
41
+
42
+ ### Option 3: From source
43
+
44
+ ```bash
45
+ git clone https://github.com/facebook/memlab.git
46
+ cd memlab
47
+ npm install
48
+ npm run build
49
+ ```
50
+
51
+ Then configure:
52
+
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "memlab": {
57
+ "type": "stdio",
58
+ "command": "node",
59
+ "args": ["/path/to/memlab/packages/mcp/dist/index.js"]
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ ## How It Works
66
+
67
+ The server holds a loaded `IHeapSnapshot` in memory across tool calls (loading is expensive for large heaps). Only one snapshot can be loaded at a time. All tools are read-only — they analyze the heap but never modify it.
68
+
69
+ ## Getting a Heap Snapshot
70
+
71
+ ### Chrome DevTools
72
+
73
+ 1. Open DevTools (F12) > Memory tab
74
+ 2. Select "Heap snapshot" and click "Take snapshot"
75
+ 3. Right-click the snapshot in the left panel > "Save..."
76
+ 4. Save the `.heapsnapshot` file
77
+
78
+ ### Node.js
79
+
80
+ ```js
81
+ const v8 = require('v8');
82
+ const snapshot = v8.writeHeapSnapshot();
83
+ console.log(`Heap snapshot written to ${snapshot}`);
84
+ ```
85
+
86
+ ## Tools Reference
87
+
88
+ ### `memlab_load_snapshot`
89
+
90
+ Load and parse a `.heapsnapshot` file. Builds indexes, computes the dominator tree, and calculates retained sizes.
91
+
92
+ ```
93
+ Input: { file_path: "/path/to/snapshot.heapsnapshot" }
94
+ Output: { status, file_path, node_count, edge_count, total_size }
95
+ ```
96
+
97
+ ### `memlab_snapshot_summary`
98
+
99
+ Overview stats: total nodes/edges, total size, breakdown by node type with dominator-aware aggregate retained sizes.
100
+
101
+ ### `memlab_largest_objects`
102
+
103
+ Top N objects by retained size, filtering out internal/meta nodes.
104
+
105
+ ```
106
+ Input: { limit?: 20 }
107
+ ```
108
+
109
+ ### `memlab_get_node`
110
+
111
+ Look up a single node by numeric ID with full details (size, type, detachment status, dominator, location, string value).
112
+
113
+ ```
114
+ Input: { node_id: 12345 }
115
+ ```
116
+
117
+ ### `memlab_find_nodes_by_class`
118
+
119
+ Find all objects with a given constructor/class name, sorted by retained size.
120
+
121
+ ```
122
+ Input: { class_name: "FiberNode", limit?: 20 }
123
+ ```
124
+
125
+ ### `memlab_get_references`
126
+
127
+ Outgoing edges from a node (what it points to), sorted by target retained size.
128
+
129
+ ```
130
+ Input: { node_id: 12345, limit?: 30 }
131
+ ```
132
+
133
+ ### `memlab_get_referrers`
134
+
135
+ Incoming edges to a node (what points to it), sorted by source retained size.
136
+
137
+ ```
138
+ Input: { node_id: 12345, limit?: 30 }
139
+ ```
140
+
141
+ ### `memlab_retainer_trace`
142
+
143
+ Shortest path from a GC root to a node. Shows why the object is retained in memory.
144
+
145
+ ```
146
+ Input: { node_id: 12345 }
147
+ ```
148
+
149
+ ### `memlab_detached_dom`
150
+
151
+ Find detached DOM elements still retained in memory (common memory leak source). Supports count-only and ids-only modes for large result sets.
152
+
153
+ ```
154
+ Input: { output_mode?: "full"|"count"|"ids", limit?: 20 }
155
+ ```
156
+
157
+ ### `memlab_duplicated_strings`
158
+
159
+ Find duplicated string instances ranked by total retained size.
160
+
161
+ ```
162
+ Input: { limit?: 15 }
163
+ ```
164
+
165
+ ### `memlab_stale_collections`
166
+
167
+ Find Map/Set/Array collections holding detached DOM or unmounted Fiber nodes.
168
+
169
+ ```
170
+ Input: { limit?: 15 }
171
+ ```
172
+
173
+ ### `memlab_global_variables`
174
+
175
+ Non-built-in global variables on the Window object, sorted by retained size.
176
+
177
+ ```
178
+ Input: { limit?: 20 }
179
+ ```
180
+
181
+ ### `memlab_search_nodes`
182
+
183
+ General-purpose search combining filters: name pattern (regex), node type, size thresholds, detachment status.
184
+
185
+ ```
186
+ Input: { name_pattern?: "Regex", type?: "object", min_retained_size?: 1000000, limit?: 20 }
187
+ ```
188
+
189
+ ### `memlab_get_property`
190
+
191
+ Look up a specific property of a node by name and return the target node with full details.
192
+
193
+ ```
194
+ Input: { node_id: 12345, property_name: "stateNode" }
195
+ ```
196
+
197
+ ### `memlab_object_shape`
198
+
199
+ Show all named properties of an object with target types and sizes.
200
+
201
+ ```
202
+ Input: { node_id: 12345, include_internal?: false, limit?: 50 }
203
+ ```
204
+
205
+ ### `memlab_class_histogram`
206
+
207
+ Instance count and total retained size per constructor name, sorted by aggregate retained size (dominator-aware). The Chrome DevTools "Summary" view equivalent.
208
+
209
+ ```
210
+ Input: { limit?: 30, min_count?: 1, node_type?: "object" }
211
+ ```
212
+
213
+ ### `memlab_dominator_subtree`
214
+
215
+ Show objects dominated by a given node — what would be freed if it were garbage collected.
216
+
217
+ ```
218
+ Input: { node_id: 12345, limit?: 20 }
219
+ ```
220
+
221
+ ### `memlab_closure_inspection`
222
+
223
+ Inspect a closure's captured variables, source location, and scope context. Critical for diagnosing closure-based memory leaks.
224
+
225
+ ```
226
+ Input: { node_id: 12345 }
227
+ ```
228
+
229
+ ### `memlab_find_by_property`
230
+
231
+ Find all objects that have a specific property name. Useful for React internals (`__reactFiber$`), custom markers, or framework-specific patterns.
232
+
233
+ ```
234
+ Input: { property_name: "__reactFiber$", limit?: 20 }
235
+ ```
236
+
237
+ ### `memlab_aggregate`
238
+
239
+ Aggregate heap nodes by type, name, or name prefix. Returns grouped statistics with dominator-aware retained sizes (no double-counting).
240
+
241
+ ```
242
+ Input: { group_by: "type"|"name"|"name_prefix", name_pattern?: "...", limit?: 30 }
243
+ ```
244
+
245
+ ### `memlab_reports`
246
+
247
+ Run curated memory analysis reports. Use `"list"` to see available reports, pick one by name, or use `"full_analysis"` to run all reports for comprehensive triage.
248
+
249
+ ```
250
+ Input: { report: "list"|"full_analysis"|"detached_dom"|"duplicated_strings"|..., limit?: 10 }
251
+ ```
252
+
253
+ ### `memlab_eval`
254
+
255
+ Execute arbitrary JavaScript against the loaded heap snapshot in a sandboxed VM. Has access to `snapshot`, `utils`, and `helpers` but no filesystem/network access.
256
+
257
+ ```
258
+ Input: { code: "...", timeout_ms?: 30000 }
259
+ ```
260
+
261
+ ### `memlab_for_each`
262
+
263
+ Structured map/filter/reduce over all heap nodes with code predicates.
264
+
265
+ ```
266
+ Input: { filter_code: "node.type === 'closure'", map_code?: "...", reduce_code?: "...", limit?: 100 }
267
+ ```
268
+
269
+ ## Example Workflow
270
+
271
+ A typical memory investigation:
272
+
273
+ 1. **Load the snapshot**: "Load the heap snapshot at /tmp/my-app.heapsnapshot"
274
+ 2. **Get an overview**: "Show me a summary of the heap"
275
+ 3. **Find the biggest objects**: "What are the largest objects by retained size?"
276
+ 4. **Investigate a specific object**: "Show me the retainer trace for node 48231"
277
+ 5. **Check for common leak patterns**:
278
+ - "Are there any detached DOM nodes?"
279
+ - "Show me duplicated strings"
280
+ - "Are any collections holding stale objects?"
281
+ 6. **Drill into references**: "What does node 48231 reference?"
282
+
283
+ ## License
284
+
285
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ import '../dist/index.js';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import type { IHeapSnapshot } from '@memlab/core';
11
+ export declare function getSnapshot(): IHeapSnapshot;
12
+ export declare function getFilePath(): string | null;
13
+ export declare function setSnapshot(snapshot: IHeapSnapshot, filePath: string): void;
14
+ //# sourceMappingURL=heap-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heap-state.d.ts","sourceRoot":"","sources":["../src/heap-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAKhD,wBAAgB,WAAW,IAAI,aAAa,CAK3C;AAED,wBAAgB,WAAW,IAAI,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG3E"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ let currentSnapshot = null;
11
+ let currentFilePath = null;
12
+ export function getSnapshot() {
13
+ if (!currentSnapshot) {
14
+ throw new Error('No heap snapshot loaded. Use memlab_load_snapshot first.');
15
+ }
16
+ return currentSnapshot;
17
+ }
18
+ export function getFilePath() {
19
+ return currentFilePath;
20
+ }
21
+ export function setSnapshot(snapshot, filePath) {
22
+ currentSnapshot = snapshot;
23
+ currentFilePath = filePath;
24
+ }
25
+ //# sourceMappingURL=heap-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heap-state.js","sourceRoot":"","sources":["../src/heap-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,IAAI,eAAe,GAAyB,IAAI,CAAC;AACjD,IAAI,eAAe,GAAkB,IAAI,CAAC;AAE1C,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAuB,EAAE,QAAgB;IACnE,eAAe,GAAG,QAAQ,CAAC;IAC3B,eAAe,GAAG,QAAQ,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @format
9
+ * @oncall memory_lab
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG"}
package/dist/index.js ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ *
8
+ * @format
9
+ * @oncall memory_lab
10
+ */
11
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
12
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
+ import { registerLoadSnapshot } from './tools/load-snapshot.js';
14
+ import { registerSnapshotSummary } from './tools/snapshot-summary.js';
15
+ import { registerLargestObjects } from './tools/largest-objects.js';
16
+ import { registerGetNode } from './tools/get-node.js';
17
+ import { registerFindNodesByClass } from './tools/find-nodes-by-class.js';
18
+ import { registerGetReferences } from './tools/get-references.js';
19
+ import { registerGetReferrers } from './tools/get-referrers.js';
20
+ import { registerRetainerTrace } from './tools/retainer-trace.js';
21
+ import { registerDetachedDom } from './tools/detached-dom.js';
22
+ import { registerDuplicatedStrings } from './tools/duplicated-strings.js';
23
+ import { registerStaleCollections } from './tools/stale-collections.js';
24
+ import { registerGlobalVariables } from './tools/global-variables.js';
25
+ import { registerSearchNodes } from './tools/search-nodes.js';
26
+ import { registerGetProperty } from './tools/get-property.js';
27
+ import { registerObjectShape } from './tools/object-shape.js';
28
+ import { registerClassHistogram } from './tools/class-histogram.js';
29
+ import { registerDominatorSubtree } from './tools/dominator-subtree.js';
30
+ import { registerClosureInspection } from './tools/closure-inspection.js';
31
+ import { registerFindByProperty } from './tools/find-by-property.js';
32
+ import { registerAggregateNodes } from './tools/aggregate-nodes.js';
33
+ import { registerEval } from './tools/eval.js';
34
+ import { registerForEach } from './tools/for-each.js';
35
+ import { registerReports } from './tools/reports.js';
36
+ const server = new McpServer({
37
+ name: 'memlab',
38
+ version: '1.1.0',
39
+ });
40
+ registerLoadSnapshot(server);
41
+ registerSnapshotSummary(server);
42
+ registerLargestObjects(server);
43
+ registerGetNode(server);
44
+ registerFindNodesByClass(server);
45
+ registerGetReferences(server);
46
+ registerGetReferrers(server);
47
+ registerRetainerTrace(server);
48
+ registerDetachedDom(server);
49
+ registerDuplicatedStrings(server);
50
+ registerStaleCollections(server);
51
+ registerGlobalVariables(server);
52
+ registerSearchNodes(server);
53
+ registerGetProperty(server);
54
+ registerObjectShape(server);
55
+ registerClassHistogram(server);
56
+ registerDominatorSubtree(server);
57
+ registerClosureInspection(server);
58
+ registerFindByProperty(server);
59
+ registerAggregateNodes(server);
60
+ registerReports(server);
61
+ registerEval(server);
62
+ registerForEach(server);
63
+ async function main() {
64
+ const transport = new StdioServerTransport();
65
+ await server.connect(transport);
66
+ }
67
+ main().catch(err => {
68
+ console.error('Fatal error:', err);
69
+ process.exit(1);
70
+ });
71
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
+ export declare function registerAggregateNodes(server: McpServer): void;
12
+ //# sourceMappingURL=aggregate-nodes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate-nodes.d.ts","sourceRoot":"","sources":["../../src/tools/aggregate-nodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAoCvE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAqI9D"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import { z } from 'zod';
11
+ import memlabCore from '@memlab/core';
12
+ const { utils } = memlabCore;
13
+ import { getSnapshot } from '../heap-state.js';
14
+ import { errorResult, textResult, formatBytes, formatNumber, markdownTable, } from '../utils.js';
15
+ function getGroupKey(node, groupBy) {
16
+ switch (groupBy) {
17
+ case 'type':
18
+ return node.type;
19
+ case 'name':
20
+ return node.name;
21
+ case 'name_prefix': {
22
+ const spaceIdx = node.name.indexOf(' ');
23
+ return spaceIdx > 0 ? node.name.substring(0, spaceIdx) : node.name;
24
+ }
25
+ }
26
+ }
27
+ export function registerAggregateNodes(server) {
28
+ server.tool('memlab_aggregate', 'Aggregate heap nodes by type, name, or name prefix. Returns grouped statistics (count, total self size, aggregate retained size) sorted by retained size. Retained size uses dominator-aware aggregation (no double-counting).', {
29
+ name_pattern: z
30
+ .string()
31
+ .optional()
32
+ .describe('Regex pattern to match against node names'),
33
+ type: z.string().optional().describe('Node type filter'),
34
+ min_retained_size: z
35
+ .number()
36
+ .optional()
37
+ .describe('Minimum retained size in bytes'),
38
+ min_self_size: z
39
+ .number()
40
+ .optional()
41
+ .describe('Minimum self (shallow) size in bytes'),
42
+ is_detached: z
43
+ .boolean()
44
+ .optional()
45
+ .describe('Filter by detachment status'),
46
+ group_by: z
47
+ .enum(['type', 'name', 'name_prefix'])
48
+ .describe('What to group on: "type" (node type), "name" (full name), "name_prefix" (first word of name)'),
49
+ metrics: z
50
+ .array(z.enum(['count', 'sum_self_size', 'retained_size']))
51
+ .optional()
52
+ .default(['count', 'sum_self_size', 'retained_size'])
53
+ .describe('Metrics to compute per group'),
54
+ limit: z
55
+ .number()
56
+ .optional()
57
+ .default(30)
58
+ .describe('Maximum number of groups to return (default 30)'),
59
+ }, async ({ name_pattern, type, min_retained_size, min_self_size, is_detached, group_by, metrics, limit, }) => {
60
+ try {
61
+ const snapshot = getSnapshot();
62
+ const nameRegex = name_pattern ? new RegExp(name_pattern, 'i') : null;
63
+ const groups = new Map();
64
+ snapshot.nodes.forEach(node => {
65
+ if (node.id <= 3)
66
+ return;
67
+ if (type && node.type !== type)
68
+ return;
69
+ if (nameRegex && !nameRegex.test(node.name))
70
+ return;
71
+ if (min_retained_size && node.retainedSize < min_retained_size)
72
+ return;
73
+ if (min_self_size && node.self_size < min_self_size)
74
+ return;
75
+ if (is_detached !== undefined) {
76
+ const detached = node.is_detached || node.name.startsWith('Detached ');
77
+ if (is_detached !== detached)
78
+ return;
79
+ }
80
+ const key = getGroupKey(node, group_by);
81
+ let stats = groups.get(key);
82
+ if (!stats) {
83
+ stats = { count: 0, sum_self_size: 0, node_ids: new Set() };
84
+ groups.set(key, stats);
85
+ }
86
+ stats.count++;
87
+ stats.sum_self_size += node.self_size;
88
+ stats.node_ids.add(node.id);
89
+ });
90
+ const metricsSet = new Set(metrics);
91
+ const needsRetained = metricsSet.has('retained_size');
92
+ // Compute dominator-aware aggregate retained size per group.
93
+ const entries = Array.from(groups.entries()).map(([key, stats]) => {
94
+ const retainedSize = needsRetained
95
+ ? utils.aggregateDominatorMetrics(stats.node_ids, snapshot, () => true, (node) => node.retainedSize)
96
+ : 0;
97
+ return { key, ...stats, retained_size: retainedSize };
98
+ });
99
+ const sorted = entries
100
+ .sort((a, b) => b.retained_size - a.retained_size ||
101
+ b.sum_self_size - a.sum_self_size)
102
+ .slice(0, limit);
103
+ const headers = [
104
+ group_by === 'type'
105
+ ? 'Type'
106
+ : group_by === 'name'
107
+ ? 'Name'
108
+ : 'Name Prefix',
109
+ ];
110
+ if (metricsSet.has('count'))
111
+ headers.push('Count');
112
+ if (metricsSet.has('sum_self_size'))
113
+ headers.push('Self Size');
114
+ if (metricsSet.has('retained_size'))
115
+ headers.push('Retained Size');
116
+ const rightCols = new Set();
117
+ for (let i = 1; i < headers.length; i++)
118
+ rightCols.add(i);
119
+ const rows = sorted.map(stats => {
120
+ const row = [stats.key];
121
+ if (metricsSet.has('count'))
122
+ row.push(formatNumber(stats.count));
123
+ if (metricsSet.has('sum_self_size'))
124
+ row.push(formatBytes(stats.sum_self_size));
125
+ if (metricsSet.has('retained_size'))
126
+ row.push(formatBytes(stats.retained_size));
127
+ return row;
128
+ });
129
+ return textResult(`Aggregation by ${group_by} (${formatNumber(groups.size)} groups, showing ${rows.length})\n\n${markdownTable(headers, rows, rightCols)}`);
130
+ }
131
+ catch (err) {
132
+ return errorResult(err);
133
+ }
134
+ });
135
+ }
136
+ //# sourceMappingURL=aggregate-nodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregate-nodes.js","sourceRoot":"","sources":["../../src/tools/aggregate-nodes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @format
8
+ * @oncall memory_lab
9
+ */
10
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
11
+ export declare function registerClassHistogram(server: McpServer): void;
12
+ //# sourceMappingURL=class-histogram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class-histogram.d.ts","sourceRoot":"","sources":["../../src/tools/class-histogram.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAcvE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0G9D"}