Skip to main content
Glama
carloshpdoc

memorydetective

Locate a Swift symbol's source declaration

swiftGetSymbolDefinition

Finds the file:line where a Swift symbol is declared, enabling direct navigation from cycle analysis to source code.

Instructions

[mg.code] Find the file:line where a Swift symbol (class, struct, enum, protocol, func, var, etc.) is declared. Pre-scans candidatePaths (or hint.filePath) with a fast regex first, then asks SourceKit-LSP for jump-to-definition. Returns the position even when LSP can't follow through. Use after findRetainers / classifyCycle surface a class name from a memgraph cycle to land in the actual source file.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
symbolNameYesName of the Swift symbol to locate (class, struct, enum, protocol, func, var, etc.).
hintNoOptional hint to speed up the search. `filePath` skips the project scan; `module` is reserved for future multi-module work.
projectRootNoOverride the project root. Default discovers the nearest Package.swift / .xcodeproj / .xcworkspace from the cwd.
candidatePathsNoIf provided, search these files for the symbol declaration before asking SourceKit-LSP. Speeds up location when the agent already has a guess (e.g. from `findSymbolReferences` or `swift_search_pattern`).

Implementation Reference

  • Main handler function for the swiftGetSymbolDefinition tool. Resolves the project root, iterates candidate files to find a symbol declaration via regex pre-scan, then queries SourceKit-LSP for jump-to-definition. Falls back to the pre-scan position if LSP returns nothing.
    export async function swiftGetSymbolDefinition(
      input: SwiftGetSymbolDefinitionInput,
    ): Promise<SwiftGetSymbolDefinitionResult> {
      const root = input.projectRoot
        ? resolvePath(input.projectRoot)
        : projectRootFor(
            input.candidatePaths?.[0] ?? input.hint?.filePath ?? process.cwd(),
          );
      if (!existsSync(root)) {
        throw new Error(`Project root not found: ${root}`);
      }
    
      const fileCandidates = [
        ...(input.hint?.filePath ? [input.hint.filePath] : []),
        ...(input.candidatePaths ?? []),
      ];
    
      for (const fp of fileCandidates) {
        const abs = resolvePath(fp);
        if (!existsSync(abs)) continue;
        const pos = findSymbolDeclaration(abs, input.symbolName);
        if (pos) {
          const client = await acquireClient(root);
          const defs = await lspDefinition(client, abs, pos.line, pos.character);
          return {
            ok: true,
            symbolName: input.symbolName,
            definitions:
              defs.length > 0
                ? defs
                : [{ filePath: abs, line: pos.line, character: pos.character }],
            preScanHit: { filePath: abs, matchedText: pos.matchedText.trim() },
          };
        }
      }
    
      // No candidate found locally. SourceKit-LSP doesn't expose a workspace-wide
      // "find me a symbol named X" endpoint over plain LSP, so we punt and let
      // the agent feed back candidate paths from a prior `searchPattern` or
      // `findCycles` call.
      return {
        ok: true,
        symbolName: input.symbolName,
        definitions: [],
        preScanHit: undefined,
      };
    }
  • Zod schema defining the input parameters: symbolName (required), hint (optional filePath/module), projectRoot (optional), candidatePaths (optional array of file paths).
    export const swiftGetSymbolDefinitionSchema = z.object({
      symbolName: z
        .string()
        .min(1)
        .describe(
          "Name of the Swift symbol to locate (class, struct, enum, protocol, func, var, etc.).",
        ),
      hint: z
        .object({
          filePath: z.string().optional(),
          module: z.string().optional(),
        })
        .optional()
        .describe(
          "Optional hint to speed up the search. `filePath` skips the project scan; `module` is reserved for future multi-module work.",
        ),
      projectRoot: z
        .string()
        .optional()
        .describe(
          "Override the project root. Default discovers the nearest Package.swift / .xcodeproj / .xcworkspace from the cwd.",
        ),
      candidatePaths: z
        .array(z.string())
        .optional()
        .describe(
          "If provided, search these files for the symbol declaration before asking SourceKit-LSP. Speeds up location when the agent already has a guess (e.g. from `findSymbolReferences` or `swift_search_pattern`).",
        ),
    });
  • Result type: ok status, symbol name, array of definition locations, and optional pre-scan hit info.
    export interface SwiftGetSymbolDefinitionResult {
      ok: boolean;
      symbolName: string;
      /** Definition locations returned by SourceKit-LSP (or pre-scan when LSP returns nothing). */
      definitions: SourceLocation[];
      /** When set, indicates we located the symbol via filename pre-scan rather than a true LSP query. */
      preScanHit?: { filePath: string; matchedText: string };
    }
  • src/index.ts:427-439 (registration)
    Registration of the tool via server.registerTool with the name, title/description metadata, input schema, and handler callback.
    server.registerTool(
      "swiftGetSymbolDefinition",
      {
        title: "Locate a Swift symbol's source declaration",
        description:
          "[mg.code] Find the file:line where a Swift symbol (class, struct, enum, protocol, func, var, etc.) is declared. Pre-scans `candidatePaths` (or `hint.filePath`) with a fast regex first, then asks SourceKit-LSP for jump-to-definition. Returns the position even when LSP can't follow through. Use after `findRetainers` / `classifyCycle` surface a class name from a memgraph cycle to land in the actual source file.",
        inputSchema: swiftGetSymbolDefinitionSchema.shape,
      },
      async (input) => {
        const result = await swiftGetSymbolDefinition(input);
        return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
      },
    );
  • Pre-scan helper that finds a symbol's declaration in a file using regex, first matching declaration keywords (class/struct/enum/etc.), then falling back to any standalone word occurrence. Returns 0-based LSP-compatible positions.
    export function findSymbolDeclaration(
      filePath: string,
      symbolName: string,
    ): SymbolPosition | null {
      const text = readFileSync(filePath, "utf8");
      const lines = text.split(/\r?\n/);
    
      const declRe = new RegExp(
        `\\b(?:class|struct|enum|protocol|func|var|let|actor|extension)\\s+${escapeRegex(symbolName)}\\b`,
      );
      for (let i = 0; i < lines.length; i++) {
        const m = lines[i].match(declRe);
        if (m) {
          const ch = lines[i].indexOf(symbolName, m.index ?? 0);
          if (ch >= 0) {
            return { line: i, character: ch, matchedText: lines[i] };
          }
        }
      }
    
      const wordRe = new RegExp(`\\b${escapeRegex(symbolName)}\\b`);
      for (let i = 0; i < lines.length; i++) {
        const m = lines[i].match(wordRe);
        if (m) {
          return {
            line: i,
            character: m.index ?? 0,
            matchedText: lines[i],
          };
        }
      }
      return null;
    }
Behavior4/5

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

With no annotations, the description carries full burden. It transparently explains the two-step process (regex pre-scan then SourceKit-LSP) and notes that it returns a position even when LSP fails. This is good behavioral disclosure without needing to repeat safe/read nature.

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

Conciseness5/5

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

The description is four sentences, each adding distinct value: purpose, method, fallback behavior, and usage context. It is front-loaded with the core action and free of redundancy.

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

Completeness3/5

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

The description lacks detail on the return format (e.g., whether it returns a file path and line number or a full position object). Given there is no output schema, this is a gap. However, the behavioral details and parameter guidance cover most needs.

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

Parameters4/5

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

All parameters have schema descriptions (100% coverage). The description adds value beyond the schema by explaining the optimization purpose of hint.filePath and candidatePaths, and the projectRoot override behavior.

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 the verb 'find' and the resource 'a Swift symbol's source declaration' (file:line). It specifies symbol types (class, struct, etc.) and distinguishes from siblings by providing a specific use case context (after memgraph analysis tools).

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?

The description explicitly says 'Use after findRetainers / classifyCycle...', providing clear context. It does not explicitly mention when not to use or list alternatives, but the context is strong and the sibling list helps differentiation.

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