Skip to main content
Glama

rename_symbol

Rename TypeScript symbols (variables, functions, classes) across the entire codebase by specifying the root directory, file path, line, old name, and new name for consistent refactoring.

Instructions

Rename a TypeScript symbol (variable, function, class, etc.) across the codebase

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filePathYesFile path containing the symbol (relative to root)
lineYesLine number (1-based) or string to match in the line
newNameYesNew name for the symbol
oldNameYesCurrent name of the symbol
rootYesRoot directory for resolving relative paths

Implementation Reference

  • Zod input schema defining parameters for the rename_symbol tool: root, relativePath, optional line, textTarget, newName.
    const schema = z.object({
      root: z.string().describe("Root directory for resolving relative paths"),
      relativePath: z
        .string()
        .describe("File path containing the symbol (relative to root)"),
      line: z
        .union([z.number(), z.string()])
        .describe("Line number (1-based) or string to match in the line")
        .optional(),
      textTarget: z.string().describe("Symbol to rename"),
      newName: z.string().describe("New name for the symbol"),
    });
  • The core tool handler for lsp_rename_symbol (referred to as rename_symbol in docs/filters). Defines the tool with name, description, schema, and execute function that performs the rename via LSP and formats the output showing changed files.
    export function createRenameSymbolTool(
      client: LSPClient,
    ): McpToolDef<typeof schema> {
      return {
        name: "lsp_rename_symbol",
        description:
          "Rename a symbol across the codebase using LSP. Requires exact position or text target in the specified line.",
        schema,
        execute: async (args) => {
          const result = await handleRenameSymbol(args, client);
          if (result.isErr()) {
            throw new Error(result.error);
          }
    
          // Format output
          const { message, changedFiles } = result.value;
          const output = [message, "", "Changes:"];
    
          for (const file of changedFiles) {
            const relativePath = path.relative(args.root, file.filePath);
            output.push(`  ${relativePath}:`);
    
            for (const change of file.changes) {
              output.push(
                `    Line ${change.line}: "${change.oldText}" → "${change.newText}"`,
              );
            }
          }
    
          return output.join("\n");
        },
      };
    }
  • Registration of the rename_symbol tool (as lsp_rename_symbol) within the array of all LSP tools returned by createLSPTools.
    export function createLSPTools(client: LSPClient): McpToolDef<any>[] {
      return [
        createHoverTool(client),
        createReferencesTool(client),
        createDefinitionsTool(client),
        createDiagnosticsTool(client),
        createRenameSymbolTool(client),
        createDocumentSymbolsTool(client),
        createCompletionTool(client),
        createSignatureHelpTool(client),
        createFormatDocumentTool(client),
        createWorkspaceSymbolsTool(client),
        createCodeActionsTool(client),
        createCheckCapabilitiesTool(client),
        createDeleteSymbolTool(client),
      ];
  • Core helper function that orchestrates the rename operation, dispatching to line-specific or text-search-based rename logic.
    async function handleRenameSymbol(
      request: RenameSymbolRequest,
      client: LSPClient,
    ): Promise<Result<RenameSymbolSuccess, string>> {
      try {
        if (request.line !== undefined) {
          return performRenameWithLine(request, client);
        } else {
          return performRenameWithoutLine(request, client);
        }
      } catch (error) {
        return err(error instanceof Error ? error.message : String(error));
      }
    }
  • Key helper implementing the LSP rename at specific position: opens project files, performs LSP rename request, applies WorkspaceEdit changes to files, returns success with changed files details.
    async function performRenameAtPosition(
      request: RenameSymbolRequest,
      fileUri: string,
      fileContent: string,
      targetLine: number,
      symbolPosition: number,
      client: LSPClient,
    ): Promise<Result<RenameSymbolSuccess, string>> {
      try {
        if (!client) {
          return err("LSP client not available");
        }
    
        // Open all TypeScript/JavaScript files in the project to ensure LSP knows about them
        const projectFiles = await findProjectFiles(request.root);
        for (const file of projectFiles) {
          if (file !== path.resolve(request.root, request.relativePath)) {
            try {
              const content = readFileSync(file, "utf-8");
              client.openDocument(`file://${file}`, content);
            } catch (e) {
              debug(`[lspRenameSymbol] Failed to open file: ${file}`, e);
            }
          }
        }
    
        // Open the target document
        client.openDocument(fileUri, fileContent);
        await new Promise<void>((resolve) => setTimeout(resolve, 1000));
    
        const position: Position = {
          line: targetLine,
          character: symbolPosition,
        };
    
        // Optional: Check if rename is possible at this position
        try {
          const prepareResult = await client.prepareRename(fileUri, position);
    
          if (prepareResult === null) {
            return err(
              `Cannot rename symbol at line ${targetLine + 1}, column ${
                symbolPosition + 1
              }`,
            );
          }
        } catch {
          // Some LSP servers don't support prepareRename, continue with rename
        }
    
        // Perform rename
        let workspaceEdit: WorkspaceEdit | null = null;
    
        try {
          // Use the client's rename method which handles errors properly
          workspaceEdit = await client.rename(fileUri, position, request.newName);
        } catch (error: any) {
          // Check if LSP doesn't support rename (e.g., TypeScript Native Preview)
          if (
            error.code === -32601 ||
            error.message?.includes("Unhandled method") ||
            error.message?.includes("Method not found")
          ) {
            return err("LSP server doesn't support rename operation");
          }
          // Re-throw other errors
          throw error;
        }
    
        if (!workspaceEdit) {
          // LSP returned null, try TypeScript tool as fallback
          return err("No changes from LSP rename operation");
        }
    
        // Debug: Log the workspace edit
        debug(
          "[lspRenameSymbol] WorkspaceEdit from LSP:",
          JSON.stringify(workspaceEdit, null, 2),
        );
    
        // Apply changes and format result
        const result = await applyWorkspaceEdit(request.root, workspaceEdit);
    
        // Close all opened documents
        client.closeDocument(fileUri);
        for (const file of projectFiles) {
          if (file !== path.resolve(request.root, request.relativePath)) {
            try {
              client.closeDocument(`file://${file}`);
            } catch (e) {
              // Ignore close errors
            }
          }
        }
    
        return ok(result);
      } catch (error) {
        return err(error instanceof Error ? error.message : String(error));
      }
    }

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/mizchi/typescript-mcp'

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