Skip to main content
Glama
carloshpdoc

memorydetective

Get the canonical tool sequence for a known investigation kind

getInvestigationPlaybook

Retrieve a versioned investigation pipeline for known iOS issues like memory leaks or performance hangs, ensuring correct tool sequence.

Instructions

[meta] Returns a versioned, declarative pipeline for a known investigation flow (memgraph-leak, perf-hangs, ui-jank, app-launch-slow, verify-fix). Each step has a tool name, purpose, and argsTemplate. Use this once at the start of an investigation so any LLM agent can follow the right sequence without rediscovering it from individual tool descriptions.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
kindYesWhich investigation flow to return. `memgraph-leak` is the most common — diagnose a SwiftUI/Combine retain cycle from a `.memgraph` and locate it in source.

Implementation Reference

  • The main handler function that looks up a Playbook by kind (e.g., 'memgraph-leak') and returns it. Throws if the kind is unknown.
    export async function getInvestigationPlaybook(
      input: GetInvestigationPlaybookInput,
    ): Promise<GetInvestigationPlaybookResult> {
      const playbook = PLAYBOOKS[input.kind];
      if (!playbook) {
        throw new Error(
          `Unknown playbook kind: ${input.kind}. Known: ${Object.keys(PLAYBOOKS).join(", ")}`,
        );
      }
      return { ok: true, playbook };
    }
  • Zod schema definitions: PlaybookKindEnum (valid kinds) and getInvestigationPlaybookSchema (input: {kind}). Also exports TypeScript types.
    export const PlaybookKindEnum = z.enum([
      "memgraph-leak",
      "perf-hangs",
      "ui-jank",
      "app-launch-slow",
      "verify-fix",
    ]);
    
    export const getInvestigationPlaybookSchema = z.object({
      kind: PlaybookKindEnum.describe(
        "Which investigation flow to return. `memgraph-leak` is the most common — diagnose a SwiftUI/Combine retain cycle from a `.memgraph` and locate it in source.",
      ),
    });
    
    export type GetInvestigationPlaybookInput = z.infer<
      typeof getInvestigationPlaybookSchema
    >;
    
    export type PlaybookKind = z.infer<typeof PlaybookKindEnum>;
  • src/index.ts:497-509 (registration)
    Registration of the 'getInvestigationPlaybook' tool with the MCP server, including title, description, inputSchema, and handler callback.
    server.registerTool(
      "getInvestigationPlaybook",
      {
        title: "Get the canonical tool sequence for a known investigation kind",
        description:
          "[meta] Returns a versioned, declarative pipeline for a known investigation flow (`memgraph-leak`, `perf-hangs`, `ui-jank`, `app-launch-slow`, `verify-fix`). Each step has a tool name, purpose, and argsTemplate. Use this once at the start of an investigation so any LLM agent can follow the right sequence without rediscovering it from individual tool descriptions.",
        inputSchema: getInvestigationPlaybookSchema.shape,
      },
      async (input) => {
        const result = await getInvestigationPlaybook(input);
        return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
      },
    );
  • The PLAYBOOKS constant defining all 5 playbooks (memgraph-leak, perf-hangs, ui-jank, app-launch-slow, verify-fix) with their steps, purposes, argsTemplates and resultGuidance.
    const PLAYBOOKS: Record<PlaybookKind, Playbook> = {
      "memgraph-leak": {
        kind: "memgraph-leak",
        summary:
          "Diagnose a SwiftUI / Combine retain cycle from a `.memgraph` snapshot, locate the offending code, and propose a fix.",
        steps: [
          {
            step: 1,
            tool: "analyzeMemgraph",
            purpose:
              "Run leaks(1) and get totals + top-level ROOT CYCLE summaries with class chains in compact form.",
            argsTemplate: { path: "<absolute path to the .memgraph>" },
            resultGuidance:
              "Note the count of ROOT CYCLEs and the dominant class chain. The response includes `suggestedNextCalls` — follow them.",
          },
          {
            step: 2,
            tool: "classifyCycle",
            purpose:
              "Match each ROOT CYCLE against the built-in catalog of known antipatterns. Returns a fix hint and pre-populated `suggestedNextCalls` for source-code lookup.",
            argsTemplate: { path: "<same path as step 1>" },
            resultGuidance:
              "If `primaryMatch` is `null`, the cycle is novel — use `findRetainers` to walk the chain manually instead.",
          },
          {
            step: 3,
            tool: "reachableFromCycle",
            purpose:
              "Confirm which app-level class is the actual culprit (cycle root) versus collateral retained instances.",
            argsTemplate: {
              path: "<same path>",
              rootClassName: "<class from step 2's primaryMatch>",
            },
            resultGuidance:
              "If a single app-level class dominates `counts`, that's the leak. If many compete, the cycle may be deeper than a single owner.",
          },
          {
            step: 4,
            tool: "swiftSearchPattern",
            purpose:
              "Locate the code construct the classifier flagged (e.g. `.tag(`, `.sink {`, `Task {`).",
            argsTemplate: {
              filePath: "<a candidate Swift file in the project>",
              pattern: "<from step 2's suggestedNextCalls>",
            },
          },
          {
            step: 5,
            tool: "swiftGetSymbolDefinition",
            purpose: "Jump to the declaration of the cycle's app-level class.",
            argsTemplate: {
              symbolName: "<class from step 3>",
              candidatePaths: ["<Sources/, app target dirs>"],
            },
          },
          {
            step: 6,
            tool: "swiftFindSymbolReferences",
            purpose:
              "List every callsite — useful to compare capture-list patterns across them and detect inconsistencies.",
            argsTemplate: {
              symbolName: "<class from step 3>",
              filePath: "<from step 5 result>",
            },
          },
        ],
        seeAlso: ["verify-fix"],
      },
    
      "perf-hangs": {
        kind: "perf-hangs",
        summary:
          "Diagnose user-visible main-thread hangs from a `.trace` recorded with the Time Profiler or Hangs template.",
        steps: [
          {
            step: 1,
            tool: "listTraceDevices",
            purpose: "Find the simulator or device UDID to attach to.",
            argsTemplate: {},
          },
          {
            step: 2,
            tool: "recordTimeProfile",
            purpose: "Capture a fresh `.trace` while reproducing the slow path.",
            argsTemplate: {
              template: "Time Profiler",
              deviceId: "<from step 1>",
              attachAppName: "<your app name>",
              durationSec: 90,
              output: "<absolute path ending in .trace>",
            },
          },
          {
            step: 3,
            tool: "analyzeHangs",
            purpose:
              "Parse the `potential-hangs` schema; report Hang vs Microhang counts plus the top N longest.",
            argsTemplate: {
              tracePath: "<from step 2>",
              minDurationMs: 250,
            },
          },
          {
            step: 4,
            tool: "swiftSearchPattern",
            purpose:
              "If hangs are dominated by a specific call site (visible in `top` entries), grep for likely main-thread offenders: `Task { ... }` blocks without `[weak self]`, synchronous I/O on the main queue, etc.",
            argsTemplate: {
              filePath: "<candidate file>",
              pattern: "DispatchQueue\\.main\\.sync|Task\\s*\\{",
            },
          },
        ],
        seeAlso: ["ui-jank", "app-launch-slow"],
      },
    
      "ui-jank": {
        kind: "ui-jank",
        summary:
          "Diagnose dropped frames / animation hitches from a `.trace` recorded with the Animation Hitches template.",
        steps: [
          {
            step: 1,
            tool: "recordTimeProfile",
            purpose:
              "Capture a `.trace` with the Animation Hitches template active.",
            argsTemplate: {
              template: "Animation Hitches",
              deviceId: "<UDID>",
              attachAppName: "<app>",
              durationSec: 60,
              output: "<.trace path>",
            },
          },
          {
            step: 2,
            tool: "analyzeAnimationHitches",
            purpose:
              "Parse the `animation-hitches` schema; report by-type counts and the count of user-perceptible hitches (>100ms).",
            argsTemplate: { tracePath: "<from step 1>", minDurationMs: 100 },
          },
          {
            step: 3,
            tool: "swiftFindSymbolReferences",
            purpose:
              "Once a suspected `View` is identified, find callsites to scope which screens render with this view.",
            argsTemplate: { symbolName: "<View name>", filePath: "<source>" },
          },
        ],
      },
    
      "app-launch-slow": {
        kind: "app-launch-slow",
        summary:
          "Diagnose cold/warm launch slowness from a `.trace` recorded with the App Launch template.",
        steps: [
          {
            step: 1,
            tool: "recordTimeProfile",
            purpose: "Capture a launch trace.",
            argsTemplate: {
              template: "App Launch",
              deviceId: "<UDID>",
              launchBundleId: "<com.example.app>",
              durationSec: 30,
              output: "<.trace path>",
            },
          },
          {
            step: 2,
            tool: "analyzeAppLaunch",
            purpose:
              "Get cold/warm classification + per-phase breakdown (process-creation, dyld, ObjC init, AppDelegate, first-frame).",
            argsTemplate: { tracePath: "<from step 1>" },
          },
          {
            step: 3,
            tool: "swiftSearchPattern",
            purpose:
              "If `appdelegate-init` dominates, grep for synchronous work in `application(_:didFinishLaunchingWithOptions:)`.",
            argsTemplate: {
              filePath: "<AppDelegate.swift>",
              pattern: "didFinishLaunchingWithOptions",
            },
          },
        ],
      },
    
      "verify-fix": {
        kind: "verify-fix",
        summary:
          "Confirm a fix actually closed a known cycle by diffing a before/after pair of `.memgraph` snapshots.",
        steps: [
          {
            step: 1,
            tool: "diffMemgraphs",
            purpose:
              "Compare totals + class-count deltas + cycle signatures across before/after snapshots.",
            argsTemplate: {
              before: "<path to before.memgraph>",
              after: "<path to after.memgraph>",
            },
            resultGuidance:
              "Look for the originally-classified cycle in `cycles.goneFromBefore`. If still in `cycles.persisted`, the fix didn't address the right capture.",
          },
          {
            step: 2,
            tool: "classifyCycle",
            purpose:
              "Re-classify the after snapshot to confirm no new patterns appeared.",
            argsTemplate: { path: "<path to after.memgraph>" },
          },
        ],
        seeAlso: ["memgraph-leak"],
      },
    };
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It explains the output format (steps with tool name, purpose, argsTemplate) and gives detailed context. Could be more explicit about side effects (likely none), but overall transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is informative and front-loaded with purpose. It contains some meta-text ('[meta]') and examples, which add value without being overly verbose. Could be slightly shorter, but still well-structured.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With one parameter, 100% schema coverage, and no output schema or annotations, the description sufficiently explains the tool's function, when to use it, and what it returns. It is complete for the given complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with a detailed enum description. The description adds context about the most common kind but does not significantly enhance parameter understanding beyond the schema. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it returns a versioned, declarative pipeline for known investigation flows and lists specific kinds. It distinguishes from sibling analysis tools by noting that agents can follow the sequence without rediscovering tool descriptions.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says 'Use this once at the start of an investigation' – clear when to use. Does not provide when-not-to-use or alternative names, but the context of siblings implies it's a meta-tool for planning.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/carloshpdoc/memorydetective'

If you have feedback or need assistance with the MCP directory API, please join our Discord server