mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[MCP Server] Truncate output in execution tools
GitOrigin-RevId: 429d3e6ff370d9f9d341906a641bb83385be9387
This commit is contained in:
committed by
intellij-monorepo-bot
parent
195e624fa6
commit
867c71bc95
@@ -1,9 +1,15 @@
|
||||
package com.intellij.mcpserver.toolsets
|
||||
|
||||
import com.intellij.mcpserver.util.TruncateMode
|
||||
|
||||
object Constants {
|
||||
const val RELATIVE_PATH_IN_PROJECT_DESCRIPTION: String = "Path relative to the project root"
|
||||
const val TIMEOUT_MILLISECONDS_DESCRIPTION: String = "Timeout in milliseconds"
|
||||
const val LONG_TIMEOUT_MILLISECONDS_VALUE: Int = 60 * 1000
|
||||
const val MEDIUM_TIMEOUT_MILLISECONDS_VALUE: Int = 10 * 1000
|
||||
const val SHORT_TIMEOUT_MILLISECONDS_VALUE: Int = 1 * 1000
|
||||
const val MAX_LINES_COUNT_DESCRIPTION: String = "Maximum number of lines to return"
|
||||
const val MAX_LINES_COUNT_VALUE: Int = 1000
|
||||
const val TRUNCATE_MODE_DESCRIPTION: String = "How to truncate the text: from the start, in the middle, at the end, or don't truncate at all"
|
||||
val TRUCATE_MODE_VALUE: TruncateMode = TruncateMode.START
|
||||
}
|
||||
@@ -19,7 +19,9 @@ import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.mcpFail
|
||||
import com.intellij.mcpserver.project
|
||||
import com.intellij.mcpserver.toolsets.Constants
|
||||
import com.intellij.mcpserver.util.TruncateMode
|
||||
import com.intellij.mcpserver.util.checkUserConfirmationIfNeeded
|
||||
import com.intellij.mcpserver.util.truncateText
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.util.Key
|
||||
@@ -71,6 +73,10 @@ class ExecutionToolset : McpToolset {
|
||||
configurationName: String,
|
||||
@McpDescription(Constants.TIMEOUT_MILLISECONDS_DESCRIPTION)
|
||||
timeout: Int = Constants.LONG_TIMEOUT_MILLISECONDS_VALUE,
|
||||
@McpDescription(Constants.MAX_LINES_COUNT_DESCRIPTION)
|
||||
maxLinesCount: Int = Constants.MAX_LINES_COUNT_VALUE,
|
||||
@McpDescription(Constants.TRUNCATE_MODE_DESCRIPTION)
|
||||
truncateMode: TruncateMode = Constants.TRUCATE_MODE_VALUE,
|
||||
): RunConfigurationResult {
|
||||
val project = currentCoroutineContext().project
|
||||
val runManager = RunManager.getInstance(project)
|
||||
@@ -128,10 +134,11 @@ class ExecutionToolset : McpToolset {
|
||||
val exitCode = withTimeoutOrNull(timeout.milliseconds) {
|
||||
exitCodeDeferred.await()
|
||||
}
|
||||
val output = truncateText(outputBuilder.toString(), maxLinesCount = maxLinesCount, truncateMode = truncateMode)
|
||||
return RunConfigurationResult(
|
||||
exitCode = exitCode,
|
||||
timedOut = exitCode == null,
|
||||
output = outputBuilder.toString()
|
||||
output = output
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+18
-11
@@ -8,6 +8,7 @@ import com.intellij.mcpserver.annotations.McpDescription
|
||||
import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.project
|
||||
import com.intellij.mcpserver.toolsets.Constants
|
||||
import com.intellij.mcpserver.util.TruncateMode
|
||||
import com.intellij.mcpserver.util.checkUserConfirmationIfNeeded
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.wm.ToolWindowManager
|
||||
@@ -16,14 +17,11 @@ import kotlinx.serialization.EncodeDefault
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jetbrains.plugins.terminal.TerminalToolWindowFactory
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
private val logger = logger<TerminalToolset>()
|
||||
|
||||
class TerminalToolset : McpToolset {
|
||||
private val maxLineCount = 2000
|
||||
private val timeout = 2.minutes
|
||||
|
||||
@McpTool
|
||||
@McpDescription("""
|
||||
Executes a specified shell command in the IDE's integrated terminal.
|
||||
@@ -42,17 +40,18 @@ class TerminalToolset : McpToolset {
|
||||
suspend fun execute_terminal_command(
|
||||
@McpDescription("Shell command to execute")
|
||||
command: String,
|
||||
|
||||
@McpDescription("""Whether to execute the command in a default user's shell (bash, zsh, etc.).
|
||||
|Useful if the command is not a commandline but a shell script, or if it's important to preserve real environment of the user's terminal.
|
||||
|In the case of 'false' value the command will be started as a process""")
|
||||
execute_in_shell: Boolean = false,
|
||||
|
||||
executeInShell: Boolean = false,
|
||||
@McpDescription("Whether to reuse an existing terminal window. Allows to avoid creating multiple terminals")
|
||||
reuse_existing_terminal_window: Boolean = true,
|
||||
|
||||
reuseExistingTerminalWindow: Boolean = true,
|
||||
@McpDescription(Constants.TIMEOUT_MILLISECONDS_DESCRIPTION)
|
||||
timeout_milliseconds: Int = Constants.LONG_TIMEOUT_MILLISECONDS_VALUE,
|
||||
timeout: Int = Constants.LONG_TIMEOUT_MILLISECONDS_VALUE,
|
||||
@McpDescription(Constants.MAX_LINES_COUNT_DESCRIPTION)
|
||||
maxLinesCount: Int = Constants.MAX_LINES_COUNT_VALUE,
|
||||
@McpDescription(Constants.TRUNCATE_MODE_DESCRIPTION)
|
||||
truncateMode: TruncateMode = Constants.TRUCATE_MODE_VALUE,
|
||||
): CommandExecutionResult {
|
||||
val project = currentCoroutineContext().project
|
||||
checkUserConfirmationIfNeeded(McpServerBundle.message("label.do.you.want.to.execute.command.in.terminal"), command, project)
|
||||
@@ -60,7 +59,15 @@ class TerminalToolset : McpToolset {
|
||||
// TODO pass from http request later (MCP Client name or something else)
|
||||
val id = "mcp_session"
|
||||
val window = ToolWindowManager.getInstance(project).getToolWindow(TerminalToolWindowFactory.TOOL_WINDOW_ID)
|
||||
return executeShellCommand(window = window, project = project, command = command, executeInShell = execute_in_shell, sessionId = if (reuse_existing_terminal_window) id else null, timeout = timeout)
|
||||
return executeShellCommand(window = window,
|
||||
project = project,
|
||||
command = command,
|
||||
executeInShell = executeInShell,
|
||||
sessionId = if (reuseExistingTerminalWindow) id else null,
|
||||
timeout = Constants.LONG_TIMEOUT_MILLISECONDS_VALUE.milliseconds,
|
||||
maxLinesCount = maxLinesCount,
|
||||
truncateMode = truncateMode
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
|
||||
+15
-3
@@ -9,6 +9,8 @@ import com.intellij.execution.process.ProcessOutputTypes
|
||||
import com.intellij.mcpserver.McpServerBundle
|
||||
import com.intellij.mcpserver.mcpFail
|
||||
import com.intellij.mcpserver.toolsets.terminal.TerminalToolset.CommandExecutionResult
|
||||
import com.intellij.mcpserver.util.TruncateMode
|
||||
import com.intellij.mcpserver.util.truncateText
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.Key
|
||||
@@ -28,7 +30,16 @@ import kotlin.time.Duration
|
||||
class CommandSession(val sessionId: String, val console: TerminalExecutionConsole)
|
||||
val MCP_TERMINAL_KEY: Key<CommandSession> = Key.create("MCP_TERMINAL_KEY")
|
||||
|
||||
suspend fun executeShellCommand(window: ToolWindow?, project: Project, command: String, executeInShell: Boolean, sessionId: String?, timeout: Duration): CommandExecutionResult {
|
||||
suspend fun executeShellCommand(
|
||||
window: ToolWindow?,
|
||||
project: Project,
|
||||
command: String,
|
||||
executeInShell: Boolean,
|
||||
sessionId: String?,
|
||||
timeout: Duration,
|
||||
maxLinesCount: Int,
|
||||
truncateMode: TruncateMode = TruncateMode.START,
|
||||
): CommandExecutionResult {
|
||||
val defaultShell = ShConfigurationType.getDefaultShell(project)
|
||||
|
||||
val commandLine = if (executeInShell) {
|
||||
@@ -95,8 +106,9 @@ suspend fun executeShellCommand(window: ToolWindow?, project: Project, command:
|
||||
val exitCodeValue = withTimeoutOrNull(timeout) {
|
||||
exitCode.await()
|
||||
}
|
||||
val truncateText = truncateText(text = output.toString(), maxLinesCount = maxLinesCount, truncateMode = truncateMode)
|
||||
if (exitCodeValue == null) {
|
||||
return CommandExecutionResult(is_timed_out = true, command_output = output.toString())
|
||||
return CommandExecutionResult(is_timed_out = true, command_output = truncateText)
|
||||
}
|
||||
return CommandExecutionResult(command_exit_code = exitCodeValue, command_output = output.toString())
|
||||
return CommandExecutionResult(command_exit_code = exitCodeValue, command_output = truncateText)
|
||||
}
|
||||
Reference in New Issue
Block a user