planRenameSymbol
Plan TypeScript symbol renaming by computing file edits for renaming identifiers, variables, functions, and classes across your codebase without modifying files.
Instructions
Compute edits to rename a TypeScript symbol at a specific position. Returns edit plans without modifying the filesystem.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectRoot | Yes | Absolute or relative path to the project root | |
| filePath | Yes | Absolute path or path relative to projectRoot of the file containing the symbol | |
| line | Yes | 0-based line number of the symbol | |
| character | Yes | 0-based character position of the symbol | |
| newName | Yes | The new name for the symbol | |
| findInStrings | No | Whether to find occurrences in strings (default: false) | |
| findInComments | No | Whether to find occurrences in comments (default: false) |
Implementation Reference
- src/tools/planRenameSymbol.ts:24-156 (handler)The primary handler function implementing the planRenameSymbol tool logic using TypeScript LanguageService to compute rename edits.export function planRenameSymbol( params: PlanRenameSymbolParams ): PlanRenameSymbolResult { // 1. projectRoot を絶対パスに正規化 const absProjectRoot = path.resolve(params.projectRoot); // 2. createTsService で service を取得 let service: ts.LanguageService; try { const tsServiceResult = createTsService(absProjectRoot); service = tsServiceResult.service; } catch (error) { return { canRename: false, reason: error instanceof Error ? error.message : "Failed to create TS service", }; } // 3. filePath を絶対パスに正規化 const absFilePath = path.isAbsolute(params.filePath) ? params.filePath : path.resolve(absProjectRoot, params.filePath); // 4. ファイル内容を読み込み if (!fs.existsSync(absFilePath)) { return { canRename: false, reason: `File not found: ${absFilePath}`, }; } const fileText = fs.readFileSync(absFilePath, "utf8"); const sourceFile = ts.createSourceFile( absFilePath, fileText, ts.ScriptTarget.Latest, true ); // 5. ts.getPositionOfLineAndCharacter で位置を計算 const pos = ts.getPositionOfLineAndCharacter( sourceFile, params.line, params.character ); // 6. service.getRenameInfo() でリネーム可否を確認 const renameInfo = service.getRenameInfo(absFilePath, pos); if (!renameInfo.canRename) { return { canRename: false, reason: renameInfo.localizedErrorMessage ?? "Cannot rename this symbol", }; } // 7. service.findRenameLocations() でリネーム箇所を取得 const locations = service.findRenameLocations( absFilePath, pos, params.findInStrings ?? false, params.findInComments ?? false, false // providePrefixAndSuffixTextForRename ) ?? []; // 8. 各 location を TextEdit に変換し、fileName ごとにまとめる const editsByFile = new Map<string, TextEdit[]>(); for (const location of locations) { const fileName = location.fileName; // ファイルのテキストを読み込む if (!fs.existsSync(fileName)) { continue; } const locationFileText = fs.readFileSync(fileName, "utf8"); const locationSourceFile = ts.createSourceFile( fileName, locationFileText, ts.ScriptTarget.Latest, true ); // textSpan を Range に変換 const start = ts.getLineAndCharacterOfPosition( locationSourceFile, location.textSpan.start ); const end = ts.getLineAndCharacterOfPosition( locationSourceFile, location.textSpan.start + location.textSpan.length ); const range: Range = { start: { line: start.line, character: start.character, }, end: { line: end.line, character: end.character, }, }; // TextEdit を構築 const textEdit: TextEdit = { range, newText: params.newName, }; // fileName ごとにまとめる const edits = editsByFile.get(fileName) ?? []; edits.push(textEdit); editsByFile.set(fileName, edits); } // 9. FileTextEdits[] を構築 const fileTextEdits: FileTextEdits[] = Array.from(editsByFile.entries()).map( ([filePath, textEdits]) => ({ filePath, textEdits, }) ); return { canRename: true, edits: fileTextEdits, }; }
- src/types.ts:49-57 (schema)Type definition for input parameters of planRenameSymbol tool.export type PlanRenameSymbolParams = { projectRoot: string; // 絶対 or 相対 filePath: string; // 絶対 or projectRoot からの相対 line: number; // 0-based character: number; // 0-based newName: string; findInStrings?: boolean; // デフォルト false findInComments?: boolean; // デフォルト false };
- src/types.ts:62-70 (schema)Type definition for output result of planRenameSymbol tool.export type PlanRenameSymbolResult = | { canRename: false; reason: string; } | { canRename: true; edits: FileTextEdits[]; };
- src/index.ts:28-68 (registration)MCP tool registration defining name, description, and input schema for planRenameSymbol.{ name: "planRenameSymbol", description: "Compute edits to rename a TypeScript symbol at a specific position. Returns edit plans without modifying the filesystem.", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Absolute or relative path to the project root", }, filePath: { type: "string", description: "Absolute path or path relative to projectRoot of the file containing the symbol", }, line: { type: "number", description: "0-based line number of the symbol", }, character: { type: "number", description: "0-based character position of the symbol", }, newName: { type: "string", description: "The new name for the symbol", }, findInStrings: { type: "boolean", description: "Whether to find occurrences in strings (default: false)", }, findInComments: { type: "boolean", description: "Whether to find occurrences in comments (default: false)", }, }, required: ["projectRoot", "filePath", "line", "character", "newName"], }, },
- src/index.ts:151-162 (registration)Dispatch handler in MCP server that calls the planRenameSymbol function.case "planRenameSymbol": { const params = args as unknown as PlanRenameSymbolParams; const result = planRenameSymbol(params); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; }