From ff848ff8248221413fc2a1c5528e276755519acd Mon Sep 17 00:00:00 2001 From: Artem Mikhalev Date: Thu, 2 Apr 2026 18:27:19 +0300 Subject: [PATCH] Add optimize_imports tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new MCP tool that removes unused imports and reorders remaining imports according to project code style settings. Implementation follows the same pattern as reformat_file — resolves the file via PSI, runs OptimizeImportsProcessor on EDT, and waits for completion via CountDownLatch. Closes #50 Co-Authored-By: Claude Opus 4.6 --- .../mcpserverplugin/McpToolManager.kt | 2 + .../mcpserverplugin/general/formatting.kt | 47 ++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/jetbrains/mcpserverplugin/McpToolManager.kt b/src/main/kotlin/org/jetbrains/mcpserverplugin/McpToolManager.kt index b5fcf9b..88734dc 100644 --- a/src/main/kotlin/org/jetbrains/mcpserverplugin/McpToolManager.kt +++ b/src/main/kotlin/org/jetbrains/mcpserverplugin/McpToolManager.kt @@ -29,6 +29,7 @@ import org.jetbrains.mcpserverplugin.general.SearchInFilesContentTool import org.jetbrains.mcpserverplugin.general.WaitTool import org.jetbrains.mcpserverplugin.git.GetVcsStatusTool import org.jetbrains.mcpserverplugin.general.ReformatCurrentFileTool +import org.jetbrains.mcpserverplugin.general.OptimizeImportsTool import org.jetbrains.mcpserverplugin.general.ReformatFileTool class McpToolManager { @@ -75,6 +76,7 @@ class McpToolManager { GetCurrentFileErrorsTool(), ReformatCurrentFileTool(), ReformatFileTool(), + OptimizeImportsTool(), GetProblemsTools(), ) } diff --git a/src/main/kotlin/org/jetbrains/mcpserverplugin/general/formatting.kt b/src/main/kotlin/org/jetbrains/mcpserverplugin/general/formatting.kt index 616274f..c20fda4 100644 --- a/src/main/kotlin/org/jetbrains/mcpserverplugin/general/formatting.kt +++ b/src/main/kotlin/org/jetbrains/mcpserverplugin/general/formatting.kt @@ -1,5 +1,6 @@ package org.jetbrains.mcpserverplugin.general +import com.intellij.codeInsight.actions.OptimizeImportsProcessor import com.intellij.codeInsight.actions.ReformatCodeProcessor import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.runReadAction @@ -21,7 +22,7 @@ class ReformatCurrentFileTool : AbstractMcpTool(NoArgs.serializer()) { override val description: String = """ Reformats the opened file in the JetBrains IDE editor. This tool doesn't require any parameters as it operates on the file currently open in the editor. - + Returns one of two possible responses: - "ok" if the file was successfully reformatted - "file doesn't exist or can't be opened" if there is no file currently selected in the editor @@ -54,7 +55,7 @@ class ReformatFileTool : AbstractMcpTool(PathInProject.serializer Reformats a specified file in the JetBrains IDE. Use this tool to apply code formatting rules to a file identified by its path. Requires a pathInProject parameter specifying the file location relative to the project root. - + Returns one of these responses: - "ok" if the file was successfully reformatted - error "project dir not found" if project directory cannot be determined @@ -89,3 +90,45 @@ class ReformatFileTool : AbstractMcpTool(PathInProject.serializer return Response("ok") } } + +class OptimizeImportsTool : AbstractMcpTool(PathInProject.serializer()) { + override val name: String = "optimize_imports" + override val description: String = """ + Optimizes imports in a specified file in the JetBrains IDE. + Removes unused imports and reorders remaining imports according to project code style settings. + Requires a pathInProject parameter specifying the file location relative to the project root. + + Returns one of these responses: + - "ok" if the imports were successfully optimized + - error "project dir not found" if project directory cannot be determined + - error "file doesn't exist or can't be opened" if the file doesn't exist or cannot be accessed + """.trimIndent() + + override fun handle(project: Project, args: PathInProject): Response { + val latch = CountDownLatch(1) + + val projectDir = project.guessProjectDir()?.toNioPathOrNull() + ?: return Response(error = "project dir not found") + + val file = runReadAction { + LocalFileSystem.getInstance() + .refreshAndFindFileByNioFile(projectDir.resolveRel(args.pathInProject)) + } + + if (file == null) {return Response(error = "file doesn't exist or can't be opened")} + + val psiFile = runReadAction { + return@runReadAction PsiManager.getInstance(project).findFile(file) + } + + if (psiFile == null) {return Response(error = "file doesn't exist or can't be opened")} + + val processor = OptimizeImportsProcessor(project, psiFile) + processor.setPostRunnable(Runnable { + latch.countDown() + }) + ApplicationManager.getApplication().invokeLater(Runnable { processor.run() }) + latch.await() + return Response("ok") + } +}