
Memorydetective
io.github.carloshpdoc由 GitHub 账号 carloshpdoc 发布v1.18.1更新于 Oct 27, 2026
iOS leak/perf debugging via MCP: memgraph cycles, .trace analysis, SourceKit-LSP bridging.
概览
面向 iOS 内存泄漏与性能调试的本地 MCP 服务器:分析 .memgraph 与 .trace 产物,并把结论桥接到 Swift 源码。
- 可以做什么
- 分析内存图:汇总泄漏、提取循环引用、查找持有者、统计存活实例、对比快照并验证修复
- 对循环引用匹配已知反模式,给出修复提示与 Swift 修改前后模板
- 分析 Instruments 追踪:卡顿、动画掉帧、时间剖析、内存分配、启动耗时,并可一次生成追踪摘要
- 采集与录制:内存图、Time Profiler 追踪、模拟器截图与 UI 树、统一日志
- 编排验证修复流程:带 malloc 栈日志构建并启动、回放 UI 场景、修复前后各取快照
- 桥接代码:搜索 Swift 代码模式,经 SourceKit-LSP 查找符号定义与引用
- 适用场景
- 适用于排查 iOS 应用的循环引用、废弃内存、卡顿或启动变慢,并希望助手直接读取内存图与 Instruments 追踪产物,而不是粘贴大段原始输出。也适合确认已发布的修复是否真的减少了泄漏。
- 运行要求
- 以本地 stdio 进程运行,通过 npm 包 memorydetective 安装(需 Node.js)。需要 macOS 与 Xcode 命令行工具:leaks、xcrun xctrace、xcodebuild、simctl 与 log。采集类工具需要模拟器或真机访问权限;场景回放对 axe CLI 为软依赖。未声明账号或 API 密钥。
安装
在 SourceWeft 中
- 打开 控制台中的 Memorydetective,将其添加到工作区。
- 为需要使用其工具的对话启用该服务。
Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。
其他 MCP 客户端
参照 仓库 中的启动说明。
README
memorydetective
English · Português brasileiro
Diagnose iOS retain cycles and performance regressions from your chat window. No Xcode required.
[npm] [CI] [License: Apache 2.0] [GitHub stars] [macOS] [node]
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.
analyzeMetricKitPayloadis the 42nd MCP tool: ingests Apple MetricKit.mxdiagnosticJSON 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 afindCyclessuggestion). Plus three audit-close items: open-enumSupportStatusKind(downstream consumers add kinds without a breaking type bump), invocation-scopedschemaDiscoverycache (summarizeTraceend-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.tracebundles (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,
verifyFixwhitelist match modes (exact / substring / regex),recordViaInstrumentsAppcatches traces saved outsidewatchDir,inspectTracefault-tolerant fallback, configurablecountAliveframework-noise filter, variable-size class min/max/median.And v1.16: macOS 26.x recording-unblock release. New
recordViaInstrumentsAppMCP tool wraps the Instruments.app GUI flow: opens the app, surfaces step-by-step instructions, watches a directory for the saved.trace, and chains intoinspectTraceon success. Until Apple fixes thexcrun xctrace recordregression 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).summarizeTracenow chainsanalyzeNetworkActivity.replayScenariocaptures simulator screenshots per step.Earlier: v1.14 trace-side reliability,
analyzeNetworkActivity, unifiedsupportStatus[], FLEX-inspiredcountAlivesize view, MLeaksFinder + DebugSwift-inspiredverifyFixwhitelist. v1.13 shippedsummarizeTrace+/summarize-traceMCP 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.
Heads up for macOS 26.x users: Apple shipped a
task_for_pidkernel regression on macOS 26.x that blocksleaks --outputGraph,heap, ANDxctrace --template Allocationsagainst iOS simulator processes regardless ofMallocStackLogging. Even Xcode's "View Memory Graph Hierarchy" hits it unlessMalloc Stack Loggingis enabled in the scheme's Diagnostics tab. memorydetective surfaces this as a proactiveplatformAdvisoryon the first capture-class tool call, plus aworkaroundNoticewithissue: "macos-26-task-for-pid-broken"ifleaksis 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. SetMEMORYDETECTIVE_SUPPRESS_PLATFORM_ADVISORY=1to silence the notice once you have settled on a workaround.
Also on macOS 26.x:
xctrace recordis broken for simulator targets. Independent from thetask_for_pidregression above,xcrun xctrace record --time-limit Nsagainst iOS simulator processes wedges past the time limit, eventually exits when killed, and the resulting.tracebundle is missing template metadata.xctrace export --tocthen fails withDocument Missing Template Error. Re-validated against Xcode 26.5 (build 17F42, xctrace 16.0) 2026-05-15: regression survives the update. This hits the entirexctrace-based ecosystem the same way (memorydetective.recordTimeProfile, XcodeTraceMCP, and nakedxcrun xctrace recordcalls all fail identically). Workarounds: (1) userecordViaInstrumentsApp(v1.16, hardened in v1.17), which opens Instruments.app for you, prompts you to record + save the.trace, then chains intoinspectTraceautomatically once the bundle appears. v1.17 also catches saves outside the watch directory via an Instruments.app AppleScript document query, returningsavedOutsideWatchDir: trueplus 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 onrecordTimeProfile(bundleStatus: "wedged"when the on-disk bundle is the 52K stub) and oninspectTrace(returnsok: truewith diagnosis text instead of throwing). USAGE.md > Troubleshooting has a step-by-step.
Quickstart
Add to your MCP client config (Claude Code shown):
Or use it directly from the command line:
→ See Examples for chat-driven flows · API for the full tool reference · Configure for Claude Desktop / Cursor / Cline · 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.
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 carouselForEachis capturingselfstrongly through_viewModel.wrappedValue, which keepsDetailViewModeland 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.memgraphto~/Desktop/after.memgraphand tell me whetherDetailViewModelcount dropped.Claude: (calls
diffMemgraphs→countAlive)DetailViewModel: 12 → 0 (delta-12). Theswiftui.tag-index-projectioncycle 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
MyAppfor 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 (UDIDABCD-...). LaunchedMyApp(PID49581) withMallocStackLogging=1. Bundle idcom.example.MyApp.(calls
replayScenariowith 5 taps on the carousel + back-button cycle, repeat 5) Executed 25 actions in 12.3s. No failures.(calls
captureScenarioState({label:"before"})) Wrotebefore.memgraph(63,201 leaks),before.png,before.ui.json.MallocStackLoggingactive, no workaroundNotice.(you ship the fix and rebuild)
(calls
bootAndLaunchForLeakInvestigation+ replay +captureScenarioState({label:"after"}))(calls
diffMemgraphs→verifyFix)DetailViewModel: 15 → 0.swiftui.tag-index-projectionPASS. 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 (analyzeTimeProfilereturns a structured workaround notice whenxctracecan't symbolicate. See CHANGELOG known limits).
Analyze MetricKit payloads from real-user crashes (v1.18)
You: I have a folder of
.mxdiagnosticfiles 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 EXC_BAD_ACCESS clustered on the same top frame, all in builds 456 and 457 (clean on 458 + earlier). Pattern matches retain-cycle release; chain hint surfacesfindCyclesnext.Second: 5.4s hang on
libsqlite3.dylib(3 samples). Patterndb-lock. If you reproduce locally, chain intoanalyzeHangswithincludeStackClassification: truefor themainThreadViolations[]classifier.No symbolicated frames in v1.18 (raw
binaryUUID + offsetshipped; 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 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 atDetailViewModel. 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 matchesswiftui.tag-index-projectionwith high confidence.reachableFromCycleconfirms 4DetailViewModelinstances pinning ~1100NSURLSessionConfigurations each. The VM is the culprit, the network stack is collateral.(calls
swiftSearchPatternto locate.tag(+onImageSliderTappatterns in~/Development/myapp/Sources/) 1 match inMyApp/Views/MediaCarousel.swift:142.(calls
swiftGetSymbolDefinition({ symbolName: "DetailViewModel", projectRoot: "...", candidatePaths: ["MyApp/ViewModels/"] })) Declared atMyApp/ViewModels/DetailViewModel.swift:18.class DetailViewModel: ObservableObject.(calls
swiftFindSymbolReferencesto scope the blast radius. 7 callsites)Suggested fix at
MediaCarousel.swift:142: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:
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:
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, < 50 KB raw): MCP overhead is roughly token-neutral vs. Raw read. The dev-time win still holds (no manual cycle parsing) but the token win shrinks.
- One-shot symbol lookups without a leak attached: just use
grep, you don't need this. - First-time investigations on a new codebase: the agent still needs orientation turns regardless of MCP. The compounding wins kick in on the second and later investigations once the agent has cached the project's shape.
The win compounds with (a) file size, (b) investigation depth (multi-turn), and (c) how many leaks you investigate per quarter. For a single dev fixing one leak per year, the value is mostly the dev-time saving. For a team running CI gates with verifyFix across every PR, the token + time savings stack across hundreds of runs.
Configure
The memorydetective binary speaks MCP over stdio. Point any MCP-compatible client at it.
Claude Code
Claude Desktop
Restart Claude Desktop after editing.
Cursor
Cline (VS Code)
Kiro
Kiro supports MCP servers via its global config. The block mirrors Claude Desktop's:
Consult Kiro's MCP setup docs for the exact config file path on your system.
GitHub Copilot (experimental)
GitHub Copilot supports MCP servers in Agent mode (VS Code 1.94+). Add to .vscode/mcp.json in your repo:
Copilot's MCP integration moves fast. If this snippet is stale, see the VS Code MCP docs.
Environment variables
Every boolean MEMORYDETECTIVE_* flag below accepts the strtobool truthy set (case-insensitive): 1 / true / t / yes / y / on (truthy) and 0 / false / f / no / n / off (falsy). Unrecognized values emit a one-time stderr warning per variable and fall back to the documented default. Pre-v1.17 the parser was 1-only, which caused silent no-ops when operators exported =true or =yes. The advisory warning is gated on MEMORYDETECTIVE_SUPPRESS_PLATFORM_ADVISORY.
API
42 MCP tools + 34 Resources + 7 Prompts, grouped by purpose. Tool descriptions are tagged with a category prefix ([mg.memory], [mg.trace], [mg.build], [mg.scenario], [mg.code], [mg.log], [mg.render], [mg.ci], [mg.discover], [ops], [meta]) so related tools are visible at a glance.
Many tools include a suggestedNextCalls field in their response. A typed list of { tool, args, why } entries pre-populated from the current result, so the orchestrating LLM can chain calls without re-reasoning. Start with getInvestigationPlaybook(kind) for the canonical sequence. Or just type /investigate-leak (one of the Prompts) in any client that exposes MCP slash commands.
The cycle classifier ships 36 named antipatterns spanning SwiftUI (including the Swift 6 / @Observable / SwiftData / NavigationStack era, plus the v1.9 swiftui.observable-write-on-every-render shape), Combine, Swift Concurrency (incl. AsyncSequence-on-self and the new Observations API), UIKit (Timer/CADisplayLink/UIGestureRecognizer/KVO/URLSession/WebKit/DispatchSource, plus the v1.9 uikit.viewcontroller-retained-after-pop shape), Core Animation, Core Data, Coordinator pattern, and the popular third-party libs RxSwift + Realm. Each pattern carries:
- a textual one-line
fixHint - a confidence tier (
high/medium/low) - a
staticAnalysisHintpointing at the SwiftLint rule that complements the runtime evidence (or an explicit gap notice when no rule exists. Reinforces the differentiator: memorydetective sees what linters miss at parse time) - a
fixTemplatewith concrete Swift before/after snippets (new in v1.7) the agent can adapt directly to the user's code via the SourceKit-LSP source-bridging tools
Read & analyze (14)
All 9 trace-side analyzers below accept an optional second argument
AnalyzeTraceOptions(v1.18 D-02). When called bysummarizeTrace(which runs schema discovery once up front), the cache is forwarded so the per-analyzerxctrace --toccalls are skipped. Direct callers leave the option unset and behavior is identical to v1.17.
Capture / record (4)
Verify-fix orchestration (3, v1.8)
These three tools combine into a single deterministic verify-fix loop: launch the app with MallocStackLogging=1 so leaks works, drive the UI to amplify the suspected leak, snapshot before, ship the fix, snapshot after, then diffMemgraphs.
Discover (3)
Synthesize (1)
Production diagnostics (1, v1.18)
Render (1)
Ops (1)
CI / test integration (3)
Add memorydetective to your CI in 5 minutes
detectLeaksInXCTest + outputHtmlPath are the building blocks for a per-PR leak gate. The job below runs the named unit-test scheme on every push and PR, uploads the HTML report as a workflow artifact, and fails when new ROOT CYCLEs appear outside the allowlist. Copy the file into .github/workflows/leaks.yml and adjust the workspace + scheme + test identifier:
The same file is in examples/ci/github-actions-leaks.yml if you want to copy it verbatim. Notes:
- Simulator runtime: pin to iOS 18 on
macos-14runners. The macOS 26.x kernel regression (task_for_pid) breaksleaksagainst iOS 26 sims; iOS 18 is the canonical escape hatch (see the Highlights callout above). - Allowlist patterns: substrings matched against the leaking ROOT CYCLE's root class. Use them to mask known pre-existing leaks while you work the backlog.
_TtCcovers Swift mangled class prefixes that occasionally show up in SwiftUI internals. - HTML artifact: the report is self-contained (inline CSS, no external assets), so PR-comment bots and reviewers can preview it directly from the artifact URL.
- Build cache: add
actions/cache@v4keyed onPackage.resolved+*.xcconfigto skipbuild-for-testingrebuilds across runs. Then pass--skipBuildto the second invocation when chaining multipledetectLeaksInXCTestcalls on the same job.
Swift source bridging (5)
Pair the memory-graph diagnosis with source-code lookups via SourceKit-LSP. Closes the loop "found this leak in the cycle → find the file/line in your project".
These tools require macOS + Xcode (full Xcode, not just Command Line Tools. xcrun sourcekit-lsp must be available). They start a sourcekit-lsp subprocess per project root and reuse it across calls; the subprocess shuts down after a 5-minute idle window.
Why
captureMemgraphdoesn't work on physical iOS devices:leaks(1)only attaches to processes running on the local Mac (which includes iOS simulators). Memory Graph capture from a real device goes through Xcode's debugger over USB/lockdownd. Different mechanism, no public CLI equivalent.
Resources (34)
The cycle-pattern catalog is also surfaced as MCP resources, browsable at memorydetective://patterns/{patternId}. Each resource is a markdown body with the pattern name, a longer description, and the fix hint. Use this to let an agent (or a human in a UI-aware MCP client) browse the catalog without burning a classifyCycle call.
resources/list returns all 34 entries. resources/read resolves any memorydetective://patterns/{id} URI to its markdown body.
Prompts (7)
Investigation playbooks are exposed as MCP prompts (slash commands in clients that surface them, e.g. Claude Code).
Each prompt fills the canonical playbook's argument templates with the user-provided values, then hands the agent a ready-to-execute brief. Calls the same tools listed in Read & analyze. Prompts are an orchestration shortcut, not a separate engine.
CLI mode
The same binary is also a thin CLI for scripting and CI:
When called with no arguments, the binary starts as an MCP server over stdio.
The tool subcommand dispatches to any registered MCP tool by name, reading inputs from a JSON file. Exit code is 0 when the tool returns ok && passed !== false, 1 otherwise, so it slots cleanly into CI gates. Currently supported tool names: detectLeaksInXCTest, detectLeaksInXCUITest (the CI recipe above uses this).
Requirements
- macOS with Xcode Command Line Tools (
xcode-select --install) - Node.js ≥ 20
Develop
Contributing
Contributions are welcome. Bug reports, feature requests, new cycle patterns, all of it.
- Bugs / feature requests: open an issue.
- PRs: fork → branch →
npm install→ make changes →npm test(758 tests must stay green) → open a PR with a concise description of what changed and why.
Adding a cycle pattern to classifyCycle
classifyCycle ships with 36 built-in patterns covering SwiftUI (incl. Swift 6 / @Observable / SwiftData / NavigationStack / the v1.9 observable-write-on-every-render and viewcontroller-retained-after-pop shapes), Combine, Swift Concurrency (incl. AsyncSequence-on-self and Observations), UIKit (Timer / CADisplayLink / UIGestureRecognizer / KVO / URLSession / WebKit / DispatchSource), Core Animation, Core Data, the Coordinator pattern, RxSwift, and Realm. To add one:
- Edit
src/tools/classifyCycle.ts. Add an entry toPATTERNSwithid,name,fixHint, and amatchfunction. - Add a test in
src/tools/readTools.test.tsthat asserts the new pattern fires against a representative memgraph fixture. - Add a
staticAnalysisHintentry insrc/runtime/staticAnalysisHints.ts(the test in that file enforces 1:1 coverage withPATTERNS). - Add a
fixTemplateentry insrc/runtime/fixTemplates.ts(same 1:1 coverage guard). - Open a PR.
Support this project
If memorydetective saves you time, you can support continued development:
Every contribution helps keep this maintained and documented.
License
Apache 2.0. See LICENSE and NOTICE.
Permits commercial use, modification, distribution, patent use. Includes attribution clause via the NOTICE file.
Why "memorydetective"?
Hunting retain cycles in SwiftUI feels like detective work: you have a body (the leaked instance), a crime scene (the .memgraph), and a chain of suspects (the retain chain). The tool helps you read the evidence and name the killer. The brand follows the work.
来源:README.md,提交 98a4b8e
工具
0版本历史
1- v1.18.1最新Sep 16, 2026
评分
已满足适用检查项的 50%
该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。
- 近 30 天内可连通25 分
请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。
- 已声明认证方式10 分
请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。
- 已列出工具15 分
请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。
- 描述必需15 分
- 图标10 分
- 文档15 分
- 许可证10 分