memorydetective: giving AI agents 31 tools to debug iOS leaks
Open-source MCP server that bridges LLM agents to Apple's binary artifacts. The premise fits in one sentence: turn a 50-500 MB binary memgraph into a 2-5 KB structured summary the agent can actually reason about.
8 min

memorydetective: giving AI agents 31 tools to debug iOS leaks
Core thesis
LLMs are great at reasoning about Swift code and bad at iOS memory debugging. The reason isn’t model intelligence. It’s access. The evidence lives in binary files the model can’t read. memorydetective is the missing bridge: 31 MCP tools that wrap Apple’s CLIs and return structured summaries the agent can actually process.
Who this article is for
- iOS engineers who have asked an LLM to fix a SwiftUI retain cycle and got a wholesale [weak self] refactor that didn’t resolve the problem.
- People building agents or MCP servers, curious about patterns for bridging LLMs to binary tools.
- Anyone using Claude Code, Cursor or Cline who wants to extend the agent for performance investigation.
Context
When you ask an agent to diagnose an iOS memory leak, it has no way to look at the heap. The .memgraph that Xcode exports is binary, 50 to 500 MB, and won’t fit in a context window. The leaks(1) text dump runs to 200 KB of repetitive frame chains. The Instruments .trace is a structured bundle the agent can’t navigate. Without access to the artifacts, the model falls back to what it knows from training: pattern matching on cycle shapes. The most common result is the wholesale [weak self] refactor that looks fine in the diff but didn’t fix the leak, broke two unrelated closures, and silently dropped async work.
The fix isn’t a smarter model. It’s giving the model the right tools. MCP made that buildable as a side project.
What memorydetective does
memorydetective is an open-source MCP server (Apache 2.0) that exposes 31 tools to Claude Code, Claude Desktop, Cursor, Cline, and any MCP client. The tools wrap Apple’s native CLIs (leaks, heap, vmmap, malloc_history, xctrace, SourceKit-LSP) and return 2-5 KB structured summaries instead of the raw dumps.
The win isn’t marginal. In a real investigation, the agent that needed to read 280 KB of text dump (~70,000 input tokens) to even understand the leak now consumes ~3,000 tokens across two tool calls. The ~25x reduction in token usage per investigation also translates to wall clock: a 60-minute Instruments session becomes ~10 minutes in chat.
The 31 tools group into buckets:
- Read & analyze .memgraph: analyzeMemgraph, findCycles, findRetainers, countAlive, reachableFromCycle, diffMemgraphs, verifyFix, classifyCycle (matches against 34 named SwiftUI / Combine / Concurrency / UIKit / Core Data / RxSwift / Realm antipatterns and returns a Swift before/after fixTemplate).
- Read & analyze .trace: analyzeHangs, analyzeAnimationHitches, analyzeTimeProfile, analyzeAllocations, analyzeAppLaunch. Wraps the schemas Apple ships but doesn’t expose programmatically.
- Capture: captureMemgraph, recordTimeProfile, logShow, logStream.
- Verify-fix orchestration (new in v1.8): bootAndLaunchForLeakInvestigation builds + boots + installs + launches with MallocStackLogging=1 in one call. replayScenario drives the iOS Simulator through tap, swipe, wait, type with a repeat count to amplify leaks that only show up after N iterations. captureScenarioState writes labeled .memgraph + .png + .ui.json snapshots for deterministic before/after diff.
- Swift source bridging: 5 tools that wrap SourceKit-LSP. The agent goes from “the cycle root is DetailViewModel” to “declared at Sources/ViewModels/DetailViewModel.swift:18, 7 callsites” without opening Xcode.
- CI gating: compareTracesByPattern for hangs, animation hitches and app launch regressions. detectLeaksInXCUITest (experimental) for PR gating.
What it looks like in chat
A concrete example, in Claude Code:
You: I just exported a memgraph to
~/Desktop/example.memgraph. Something is leaking after I close the 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 through _viewModel.wrappedValue, pinning ~28 MB of network state.(calls swiftSearchPattern in the project) 1 match:
MyApp/Views/MediaCarousel.swift:142.Suggested fix: replace
[weak self]in the tap handler with a static helper, or weak-capture the view model via[weak coord = self.coordinator]. Want me to apply it and verify with a fresh memgraph?
All in two chat turns. No Instruments open.
What became clear while building
Three design decisions ended up mattering more than the tool list itself.
The first was returning what the agent should do next, alongside the response. Each tool response carries a suggestedNextCalls field with a typed list of { tool, args, why }. This drastically reduces the turns where the agent has to re-reason about the result to figure out what to do next. Small in code, large in token economy.
The second was not swallowing system failures. When an Apple CLI regresses, like macOS 26.x where leaks –outputGraph started requiring MallocStackLogging=1 at launch, the tool doesn’t pretend it worked. It returns a structured workaroundNotice with stable issue ids (minimal-corpse, permission-denied, leaks-not-found, transient) and a fallback path the agent can chain. For other agents that will chain these calls in CI, this completely changes pipeline robustness.
The third was killing the output’s lifecycle. Each tool returns small named summaries, not raw streams. The agent never has to parse leaks(1) line by line. That’s what makes it possible for a leak investigation to cost cents instead of dollars per turn.
Honest limits
memorydetective only works on macOS because it depends on Apple’s CLIs. Memgraph capture works on Mac apps and iOS Simulator, not on physical devices: leaks(1) only attaches to local processes, and real-device capture goes through Xcode debugger over USB with no public CLI equivalent. analyzeTimeProfile returns a structured workaroundNotice when xctrace SIGSEGVs on heavy unsymbolicated traces, an Apple-side limitation I chose to surface explicitly rather than mask. replayScenario has a soft dependency on Cameron Cooke’s axe CLI for UI driving. Everything else works without axe.
What comes next
Version 1.8.0 is what I published last week on npm. The next roadmap steps include more cycle patterns in the classifier, especially from the Swift 6 era with @Observable and Observations, coverage of the remaining xctrace schemas (network, energy, file IO), and an alternative verify-fix loop that doesn’t require axe, using only Simulator APIs.
The roadmap is on GitHub. Issues and PRs are welcome.
How to install
On any Mac with Xcode Command Line Tools and Node 20+:
npm install -g memorydetective
Point your MCP client at it. For Claude Code:
// ~/.claude/settings.json
{
"mcpServers": {
"memorydetective": { "command": "memorydetective" }
}
}
The same block works for Claude Desktop, Cursor, Cline, Kiro and GitHub Copilot agent mode (full configs in the README).
Conclusion
memorydetective is what happens when you stop asking an LLM to guess and start giving it the tools it’s missing. The premise fits in one sentence: turn 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 investigation from “marginal” to “default”.
Apache 2.0. Open source. Works in any MCP client.
CTA
If you work with iOS and use Claude Code, Cursor or Cline, give memorydetective a shot on a real investigation. If it’s useful, the best way to support the project is to open an issue when something doesn’t work and tell me which cycle pattern you’d like the classifier to catch.
References
- memorydetective on GitHub
- memorydetective on npm
- Article B: An afternoon of iOS perf work with Claude in the loop (real production case study that drove the v1.7 and v1.8 features)
- Apple Developer: Gathering information about memory use
- WWDC21: Detect and diagnose memory issues
- Model Context Protocol spec