Install
$ agentstack add mcp-carloshpdoc-memorydetective ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ● Shell / process execution Used
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
memorydetective
English · [Português brasileiro](README.pt-br.md)
> Diagnose iOS retain cycles and performance regressions from your chat window. No Xcode required.
[](https://www.npmjs.com/package/memorydetective) [](https://github.com/carloshpdoc/memorydetective/actions/workflows/ci.yml) [](./LICENSE) [](https://github.com/carloshpdoc/memorydetective/stargazers) [](#requirements) [](#requirements)
Highlights
- CLI-driven leak hunting. Read
.memgraphfiles captured by Xcode (or bymemorydetectiveitself on simulators), find ROOT CYCLEs, classify them against known SwiftUI/Combine patterns, and get a one-liner fix hint. All from a script or a chat. - MCP-native. Plugs into Claude Code, Claude Desktop, Cursor, Cline, and any other MCP client. The agent drives the full investigate → classify → suggest-fix loop without you opening Instruments.
- Honest about its limits. No mocked outputs, no over-promises. Hangs analysis works clean from
xctrace; sample-level Time Profile is parsed whenxctracesymbolicates the trace and returns a structured workaround notice when it can't (the underlyingxctraceSIGSEGV on heavy unsymbolicated traces is an Apple-side limitation we surface explicitly). Memory Graph capture works on Mac apps and iOS simulator; physical iOS devices still need Xcode.
> What's new in v1.18 (2026-05-17): MetricKit + audit-close. analyzeMetricKitPayload is the 42nd MCP tool: ingests Apple MetricKit .mxdiagnostic JSON payloads from real-device TestFlight / App Store builds (post-mortem production diagnostics, no MCP competitor covers this lane today). Three actionable outputs: crash clusters by exception type / binary / top frame, hang hotspots with localized-duration parsing ("5.4 sec" / "20秒"), CPU + disk exceptions. Cross-tool chain hints (e.g. objc_release-style top frame surfaces a findCycles suggestion). Plus three audit-close items: open-enum SupportStatusKind (downstream consumers add kinds without a breaking type bump), invocation-scoped schemaDiscovery cache (summarizeTrace end-to-end shaved from ~28s to ~15s on real Apple traces via single up-front TOC fetch), and local-only integration tests against real Apple .trace bundles (closes the v1.14 P+O drift class for good). 701 → 757 tests. 41 → 42 MCP tools. > > Also recent (v1.17): reliability pass. 14 bug fixes across three tiers. Headlines: strtobool env truthy parsing, verifyFix whitelist match modes (exact / substring / regex), recordViaInstrumentsApp catches traces saved outside watchDir, inspectTrace fault-tolerant fallback, configurable countAlive framework-noise filter, variable-size class min/max/median. > > And v1.16: macOS 26.x recording-unblock release. New recordViaInstrumentsApp MCP tool wraps the Instruments.app GUI flow: opens the app, surfaces step-by-step instructions, watches a directory for the saved .trace, and chains into inspectTrace on success. Until Apple fixes the xcrun xctrace record regression on macOS 26.x sims, this is the automated path. > > And v1.15: schema coverage + verify-fix UX. Three new MCP trace tools filled the remaining schema gap: analyzeMemoryFootprint (38th, VM resident / dirty / virtual + jetsam diagnosis), analyzeEnergyImpact (39th, battery drain investigation), analyzeLeakTimeline (40th, xctrace's leaks instrument as a time series). summarizeTrace now chains analyzeNetworkActivity. replayScenario captures simulator screenshots per step. > > Earlier: v1.14 trace-side reliability, analyzeNetworkActivity, unified supportStatus[], FLEX-inspired countAlive size view, MLeaksFinder + DebugSwift-inspired verifyFix whitelist. v1.13 shipped summarizeTrace + /summarize-trace MCP prompt. v1.12 completed reference-tree propagation. v1.11 added inspectTrace, diffMemgraphs reference-tree. v1.9 shipped analyzeAbandonedMemory, detectLeaksInXCTest, cleanupTraces, mainThreadViolations. Full notes in [CHANGELOG](./CHANGELOG.md).
> Heads up for macOS 26.x users: Apple shipped a task_for_pid kernel regression on macOS 26.x that blocks leaks --outputGraph, heap, AND xctrace --template Allocations against iOS simulator processes regardless of MallocStackLogging. Even Xcode's "View Memory Graph Hierarchy" hits it unless Malloc Stack Logging is enabled in the scheme's Diagnostics tab. memorydetective surfaces this as a proactive platformAdvisory on the first capture-class tool call, plus a workaroundNotice with issue: "macos-26-task-for-pid-broken" if leaks is invoked. The most reliable workaround today is to target an iOS 18 simulator runtime (install via Xcode > Settings > Platforms > +iOS 18.x). Empirically validated in the notelet investigation 2026-05-12 where three independent CLI memory-introspection paths all failed before iOS 18 was identified as the working escape hatch. Set MEMORYDETECTIVE_SUPPRESS_PLATFORM_ADVISORY=1 to silence the notice once you have settled on a workaround.
> Also on macOS 26.x: xctrace record is broken for simulator targets. Independent from the task_for_pid regression above, xcrun xctrace record --time-limit Ns against iOS simulator processes wedges past the time limit, eventually exits when killed, and the resulting .trace bundle is missing template metadata. xctrace export --toc then fails with Document Missing Template Error. Re-validated against Xcode 26.5 (build 17F42, xctrace 16.0) 2026-05-15: regression survives the update. This hits the entire xctrace-based ecosystem the same way (memorydetective.recordTimeProfile, XcodeTraceMCP, and naked xcrun xctrace record calls all fail identically). Workarounds: (1) use recordViaInstrumentsApp (v1.16, hardened in v1.17), which opens Instruments.app for you, prompts you to record + save the .trace, then chains into inspectTrace automatically once the bundle appears. v1.17 also catches saves outside the watch directory via an Instruments.app AppleScript document query, returning savedOutsideWatchDir: true plus the actual path; (2) record from an older macOS host with Xcode 26.0 if you have one; (3) record against a physical device (the regression appears to be simulator-specific). v1.17 added a viability probe on recordTimeProfile (bundleStatus: "wedged" when the on-disk bundle is the 52K stub) and on inspectTrace (returns ok: true with diagnosis text instead of throwing). USAGE.md > Troubleshooting has a step-by-step.
Quickstart
npm install -g memorydetective
Add to your MCP client config (Claude Code shown):
// ~/.claude/settings.json
{
"mcpServers": {
"memorydetective": { "command": "memorydetective" }
}
}
Or use it directly from the command line:
memorydetective analyze ~/Desktop/myapp.memgraph
memorydetective classify ~/Desktop/myapp.memgraph
→ See [Examples](#examples) for chat-driven flows · [API](#api) for the full tool reference · [Configure](#configure) for Claude Desktop / Cursor / Cline · [USAGE.md](./USAGE.md) for the full walkthrough including how fixes flow from diagnosis to your codebase.
Examples
> Realistic chat snippets. The agent calls the MCP tools listed in [API](#api).
Diagnose a leak from a memgraph
> You: I just exported a memory graph at ~/Desktop/example.memgraph. Something's leaking after I close a detail screen. What is it? > > Claude: (calls analyzeMemgraph → classifyCycle) > Found 4 ROOT CYCLE blocks across 60,436 leaks. The dominant one matches swiftui.tag-index-projection with high confidence. A closure attached via .tag() on a photo carousel ForEach is capturing self strongly through _viewModel.wrappedValue, which keeps DetailViewModel and the navigation coordinator alive after dismissal. > > Suggested fix: replace [weak self] capture in the tap handler with a static helper, or weak-capture the view-model via [weak coord = self.coordinator]. Want me to find the exact file?
Verify a fix actually worked
> You: I shipped the fix. Compare ~/Desktop/before.memgraph to ~/Desktop/after.memgraph and tell me whether DetailViewModel count dropped. > > Claude: (calls diffMemgraphs → countAlive) > DetailViewModel: 12 → 0 (delta -12). The swiftui.tag-index-projection cycle is gone. Total leak count fell from 60,436 to 55,576 (−4,860). Three other ROOT CYCLEs persist. They're SwiftUI internals, not your code.
Self-contained verify-fix loop (v1.8)
When leaks --outputGraph fails on macOS 26.x (Failed to get DYLD info for task), the cause is usually that the app was not launched with MallocStackLogging=1. The new orchestration tools handle this end to end without you wiring up xcodebuild + simctl + axe yourself.
> You: Reproduce the leak in MyApp for me. Build, launch with the right env vars, repeat the photo carousel flow 5 times to amplify it, capture before, then I'll ship the fix and you capture after. > > Claude: (calls bootAndLaunchForLeakInvestigation) > Built + booted iPhone 15 (UDID ABCD-...). Launched MyApp (PID 49581) with MallocStackLogging=1. Bundle id com.example.MyApp. > > (calls replayScenario with 5 taps on the carousel + back-button cycle, repeat 5) > Executed 25 actions in 12.3s. No failures. > > (calls captureScenarioState({label:"before"})) > Wrote before.memgraph (63,201 leaks), before.png, before.ui.json. MallocStackLogging active, no workaroundNotice. > > (you ship the fix and rebuild) > > (calls bootAndLaunchForLeakInvestigation + replay + captureScenarioState({label:"after"})) > > (calls diffMemgraphs → verifyFix) > DetailViewModel: 15 → 0. swiftui.tag-index-projection PASS. Bytes freed: 28.4 MB.
Find the slow path on a real device
> You: Profile DemoApp on my iPhone for 90 seconds and tell me where the hangs are. > > Claude: (calls listTraceDevices → recordTimeProfile → analyzeHangs) > Recorded ~/Desktop/run.trace (90s, attached to DemoApp on iPhone 17 Pro Max). Found 23 user-visible hangs (>500 ms each), longest 1.16 s, average 769 ms. Severe load. All on the main thread. Sample-level hotspots aren't symbolicated yet; open the trace in Instruments for that step (analyzeTimeProfile returns a structured workaround notice when xctrace can't symbolicate. See CHANGELOG known limits).
Analyze MetricKit payloads from real-user crashes (v1.18)
> You: I have a folder of .mxdiagnostic files from the last 30 days of TestFlight at ~/Downloads/metrickit-may/. What's catching real users? > > Claude: (calls analyzeMetricKitPayload({ payloadDir: "~/Downloads/metrickit-may/" })) > 47 payloads, 2026-04-15 → 2026-05-15. Top: 18 crashes EXCBADACCESS clustered on the same top frame, all in builds 456 and 457 (clean on 458 + earlier). Pattern matches retain-cycle release; chain hint surfaces findCycles next. > > Second: 5.4s hang on libsqlite3.dylib (3 samples). Pattern db-lock. If you reproduce locally, chain into analyzeHangs with includeStackClassification: true for the mainThreadViolations[] classifier. > > No symbolicated frames in v1.18 (raw binaryUUID + offset shipped; dSYM lookup is a separate tool, future release). Localized hang strings handled: input was "5.4 sec", normalized to 5400ms; one diagnostic carried "20秒" (Japanese), normalized to 20000ms.
See [USAGE.md § MetricKit production post-mortem](./USAGE.md#end-to-end-example-metrickit-production-post-mortem-v118) for the full end-to-end including how to wire analyzeMetricKitPayload into a verify-fix loop across multiple TestFlight builds.
End-to-end: leak → file → fix suggestion
The full loop, from artifact to code edit. Combines memory-graph analysis with Swift source-bridging.
> You: Tem um leak. Memgraph em ~/Desktop/example-leaks.memgraph. The cycle points at DetailViewModel. Find where it lives in the project ~/Development/myapp/ and suggest a fix. > > Claude: (calls analyzeMemgraph → classifyCycle → reachableFromCycle) > Found 4 ROOT CYCLE blocks. The dominant one matches swiftui.tag-index-projection with high confidence. reachableFromCycle confirms 4 DetailViewModel instances pinning ~1100 NSURLSessionConfigurations each. The VM is the culprit, the network stack is collateral. > > (calls swiftSearchPattern to locate .tag( + onImageSliderTap patterns in ~/Development/myapp/Sources/) > 1 match in MyApp/Views/MediaCarousel.swift:142. > > (calls swiftGetSymbolDefinition({ symbolName: "DetailViewModel", projectRoot: "...", candidatePaths: ["MyApp/ViewModels/"] })) > Declared at MyApp/ViewModels/DetailViewModel.swift:18. class DetailViewModel: ObservableObject. > > (calls swiftFindSymbolReferences to scope the blast radius. 7 callsites) > > Suggested fix at MediaCarousel.swift:142: > ``swift > // before > .tag(index) > .onImageSliderTap { index in > self.viewModel.handlePhotoTap(at: index) > } > > // after. Static helper + weak captures > .tag(index) > .onImageSliderTap { [weak vm = _viewModel.wrappedValue, > weak coord = self.coordinator] index in > Self.handlePhotoTap(index: index, viewModel: vm, coordinator: coord) > } > ` > Want me to apply this and verify with a fresh .memgraph`?
What it saves you
The pitch in one sentence: memorydetective turns a 50-500 MB binary memgraph (or a 200 KB leaks(1) text dump) into a 2-5 KB structured summary your AI agent can actually reason about. That changes the economics of using an LLM for iOS perf investigation.
Tokens (when paired with an AI agent like Claude / Cursor / Cline)
A real-world retain-cycle investigation, run twice. Once with memorydetective, once with the agent reading the raw leaks(1) output directly:
| Step | Without MCP (agent reads raw output) | With memorydetective | |---|---|---| | Load leaks text dump (~280 KB) | ~70,000 input tokens | n/a | | analyzeMemgraph summary | n/a | ~750 input tokens | | classifyCycle + fix hint | agent re-reasons over the dump per follow-up (3-4 extra turns) | 1 turn, structured patternId + fixHint | | findRetainers / reachableFromCycle | agent re-scans the dump | ~500 tokens, scoped query | | Net per investigation | ~85,000 tokens, ~6 turns | ~3,000 tokens, ~2 turns |
Translates to roughly $0.40-$1.20 per investigation depending on the model (Claude Opus / Sonnet / Haiku). Compounds linearly with file size and investigation depth.
Developer time
The same investigation, measured by the developer:
| Step | Without MCP | With memorydetective | |---|---|---| | Capture memgraph + run leaks | 5 min | 5 min (same) | | Read & interpret leaks text dump | 15-30 min (skim 200 KB of repetitive frames) | 30 sec (read 3 KB summary) | | Identify the responsible pattern | 10-20 min (recognize the cycle shape from experience) | instant (classifier returns patternId + fix hint) | | Locate the suspect type in source | 10-15 min (grep + manual navigation) | 30 sec (swiftGetSymbolDefinition returns file:line) | | Find every callsite to gauge fix blast radius | 5-10 min (Xcode / grep) | 10 sec (swiftFindSymbolReferences) | | Net wall-clock | 45-80 min | ~10 min |
Numbers are rounded from a single anonymized real investigation (a SwiftUI retain cycle over a tagged ForEach that pinned ~28 MB of network-stack state). Your mileage will vary with cycle complexity and codebase size.
When the win is marginal
Be honest about where this doesn't help much:
- Tiny memgraphs (a single cycle,
Claude Code
// ~/.claude/settings.json (global) or .mcp.json (per-project)
{
"mcpServers": {
"memorydetective": { "command": "memorydetective" }
}
}
Claude Desktop
// ~/Librar
…
## Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [carloshpdoc](https://github.com/carloshpdoc)
- **Source:** [carloshpdoc/memorydetective](https://github.com/carloshpdoc/memorydetective)
- **License:** Apache-2.0
- **Homepage:** https://www.npmjs.com/package/memorydetective
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.