Artigo

memorydetective: dando a agentes de IA 31 ferramentas para debugar leaks de iOS

MCP server open source que conecta agentes LLM aos artefatos binários da Apple. A premissa cabe em uma frase: turn um memgraph binário de 50 a 500 MB em um summary estruturado de 2 a 5 KB que o agente consegue de fato processar.

Artigo original do site. Apache 2.0, código aberto, roda em qualquer cliente MCP.

Em uma leitura

TL;DR

  • LLMs são ótimos para raciocinar sobre código Swift e ruins para debugar memória no iOS porque não têm acesso aos artefatos binários.
  • memorydetective embrulha as CLIs da Apple (leaks, heap, vmmap, xctrace, SourceKit-LSP) em 31 ferramentas MCP.
  • Em uma investigação real, token usage cai ~25x e wall clock vai de 60 minutos para 10. Apache 2.0.

Tese central

LLMs são ótimos para raciocinar sobre código Swift e ruins para debugar memória no iOS. O motivo não é falta de inteligência do modelo. É falta de acesso. As evidências do problema vivem em arquivos binários que o modelo não consegue ler. memorydetective é a ponte que faltava: 31 ferramentas MCP que embrulham as CLIs da Apple e devolvem summaries estruturados que o agente consegue de fato processar.

Para quem é este artigo

  • Engenheiros iOS que já sentiram a frustração de pedir a um LLM para consertar um retain cycle no SwiftUI e receber um wholesale [weak self] que não resolve.
  • Pessoas construindo agentes ou MCP servers e curiosas sobre patterns de bridging entre LLM e ferramentas binárias.
  • Quem usa Claude Code, Cursor ou Cline e quer estender o agente para perf investigation.

Contexto

Quando tu pede a um agente para diagnosticar um leak de memória no iOS, ele não tem como olhar para o heap. O .memgraph que o Xcode exporta é binário, tem 50 a 500 MB e não cabe em janela de contexto. O dump textual do leaks(1) chega a 200 KB de frame chains repetitivas. O .trace do Instruments é um bundle estruturado que o agente não consegue navegar. Sem acesso aos artefatos, o modelo recorre ao que conhece de treinamento: pattern matching em shapes de cycle. O resultado mais comum é o refactor wholesale de [weak self] que parece bom no diff, mas não consertou o leak, quebrou duas closures não relacionadas e silenciosamente perdeu trabalho assíncrono.

A solução não é usar um modelo melhor. É dar ao modelo as ferramentas certas. MCP fez isso virar coisa que se constrói como side project.

O que memorydetective faz

memorydetective é um MCP server open source (Apache 2.0) que expõe 31 ferramentas para Claude Code, Claude Desktop, Cursor, Cline e qualquer cliente MCP. As ferramentas embrulham as CLIs nativas da Apple (leaks, heap, vmmap, malloc_history, xctrace, SourceKit-LSP) e devolvem summaries estruturados de 2 a 5 KB no lugar dos dumps brutos.

O ganho não é marginal. Em uma investigação real, o agente que precisava ler 280 KB de dump textual em ~70.000 tokens de input para tentar entender o leak agora consome ~3.000 tokens em duas chamadas de tool. A redução de ~25x em token usage por investigação também se traduz em wall clock: o que era uma sessão de 60 minutos no Instruments vira ~10 minutos no chat.

As 31 ferramentas em buckets

  • Read & analyze de .memgraph: analyzeMemgraph, findCycles, findRetainers, countAlive, reachableFromCycle, diffMemgraphs, verifyFix, classifyCycle (matcheia contra 34 antipatterns nomeados de SwiftUI, Combine, Concurrency, UIKit, Core Data, RxSwift, Realm e devolve um fixTemplate Swift before/after).
  • Read & analyze de .trace: analyzeHangs, analyzeAnimationHitches, analyzeTimeProfile, analyzeAllocations, analyzeAppLaunch. Embrulham os schemas que a Apple ship mas não expõe programaticamente.
  • Capture: captureMemgraph, recordTimeProfile, logShow, logStream.
  • Verify-fix orchestration (novidade da v1.8): bootAndLaunchForLeakInvestigation faz build, boot, install e launch com MallocStackLogging=1 em uma chamada só. replayScenario dirige o iOS Simulator através de tap, swipe, wait e type com repeat count para amplificar leaks que só aparecem depois de N iterações. captureScenarioState escreve snapshots labeled de .memgraph + .png + .ui.json para diff determinístico antes e depois.
  • Swift source bridging: 5 ferramentas que embrulham SourceKit-LSP. O agente vai de "o cycle root é DetailViewModel" para "declared at Sources/ViewModels/DetailViewModel.swift:18, 7 callsites" sem abrir o Xcode.
  • CI gating: compareTracesByPattern para regressão de hangs, animation hitches e app launch. detectLeaksInXCUITest (experimental) para gating no PR.

Como parece em chat

Um exemplo concreto, no Claude Code:

You: Acabei de exportar um memgraph em ~/Desktop/example.memgraph. Algo está vazando depois que eu fecho a tela de detalhe. O que é?

Claude: (chama analyzeMemgraph + classifyCycle)
Encontrei 4 ROOT CYCLE blocks em 60.436 leaks. O dominante matcheia swiftui.tag-index-projection com alta confiança. Uma closure aplicada via .tag() em um ForEach de carrossel de fotos está capturando self através de _viewModel.wrappedValue, prendendo aproximadamente 28 MB de network state.

(chama swiftSearchPattern no projeto)
1 match: MyApp/Views/MediaCarousel.swift:142. Sugestão de fix: substituir [weak self] no tap handler por um helper estático, ou weak capture do view model via [weak coord = self.coordinator]. Quer que eu aplique e verifique com um memgraph fresco?

Tudo isso em duas chamadas de chat. Sem Instruments aberto.

O que ficou claro construindo

Três decisões de design ficaram mais importantes do que a lista de tools em si.

A primeira foi devolver o que o agente precisa fazer a seguir, junto da resposta. Cada tool response carrega um campo suggestedNextCalls com uma lista tipada de { tool, args, why }. Isso reduz drasticamente os turns em que o agente precisa re-raciocinar sobre o resultado para descobrir o próximo passo. É pequeno em código e grande em token economy.

A segunda foi não engolir falhas do sistema. Quando uma CLI da Apple regride, como aconteceu no macOS 26.x onde leaks --outputGraph passou a exigir MallocStackLogging=1 no launch, o tool não finge que funcionou. Ele devolve um workaroundNotice estruturado com issue ids estáveis (minimal-corpse, permission-denied, leaks-not-found, transient) e um caminho de fallback para o agente chainear. Para outros agentes que vão chainear essas chamadas em CI, isso muda completamente a robustez do pipeline.

A terceira foi matar o ciclo de vida do output. Cada tool entrega summaries pequenos e nomeados, não stream cru. O agente nunca precisa parsear o output da leaks(1) linha por linha. Isso é o que torna possível uma investigação de leak custar centavos em vez de dólares por turn.

Limites honestos

memorydetective só funciona em macOS porque depende das CLIs da Apple. Memgraph capture funciona em apps Mac e iOS Simulator, não em devices físicos: leaks(1) só attacha a processos locais, e capture de device real passa por Xcode debugger via USB sem equivalente em CLI pública. analyzeTimeProfile devolve um workaroundNotice estruturado quando o xctrace dá SIGSEGV em traces pesados unsymbolicated, que é uma limitação Apple-side que decidi expor explicitamente em vez de mascarar. replayScenario tem dependência soft do CLI axe do Cameron Cooke para drive de UI. O resto roda sem axe instalado.

Como instalar

Em qualquer Mac com Xcode Command Line Tools e Node 20+:

npm install -g memorydetective

Aponta o teu cliente MCP. Para Claude Code:

// ~/.claude/settings.json
{
  "mcpServers": {
    "memorydetective": { "command": "memorydetective" }
  }
}

O mesmo bloco serve para Claude Desktop, Cursor, Cline, Kiro e GitHub Copilot agent mode (full configs no README).

Conclusão

memorydetective é o que acontece quando tu para de pedir a um LLM para adivinhar e começa a dar a ele as ferramentas que faltam. A premissa do projeto cabe em uma frase: turn a 50 a 500 MB binary memgraph (or a 200 KB leaks(1) text dump) into a 2 a 5 KB structured summary your AI agent can actually reason about. Isso muda a economia de usar LLM para investigação iOS de "marginal" para "default".

Apache 2.0. Open source. Roda em qualquer cliente MCP.

Referências

Para aprofundar