mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
[AIA/MCP Server] Add reliable API to report user-friendly tool activity description
GitOrigin-RevId: 4b95ea4d179c1b44dbefb1c5eae1037684e53573
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2c814de7d8
commit
069e66b706
@@ -0,0 +1,12 @@
|
||||
package com.intellij.mcpserver
|
||||
|
||||
import kotlin.coroutines.AbstractCoroutineContextElement
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
data class McpToolDescriptorElement(val mcpToolDescriptor: McpToolDescriptor?) : AbstractCoroutineContextElement(Key) {
|
||||
companion object Key : CoroutineContext.Key<McpToolDescriptorElement>
|
||||
}
|
||||
|
||||
val CoroutineContext.currentToolDescriptorOrNull: McpToolDescriptor? get() = get(McpToolDescriptorElement)?.mcpToolDescriptor
|
||||
val CoroutineContext.currentToolDescriptor: McpToolDescriptor get() = get(McpToolDescriptorElement)?.mcpToolDescriptor
|
||||
?: error("currentToolDescriptor called outside of a MCP tool call")
|
||||
@@ -3,6 +3,7 @@ package com.intellij.mcpserver
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.application
|
||||
import com.intellij.util.messages.Topic
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
interface ToolCallListener {
|
||||
companion object {
|
||||
@@ -12,9 +13,9 @@ interface ToolCallListener {
|
||||
|
||||
fun beforeMcpToolCall(mcpToolDescriptor: McpToolDescriptor) {}
|
||||
|
||||
fun afterMcpToolCall(mcpToolDescriptor: McpToolDescriptor, events: List<McpToolSideEffectEvent>) {}
|
||||
fun afterMcpToolCall(mcpToolDescriptor: McpToolDescriptor, events: List<McpToolSideEffectEvent>, error: Throwable?) {}
|
||||
|
||||
fun toolActivity(toolDescription: String) {}
|
||||
fun toolActivity(mcpToolDescriptor: McpToolDescriptor, toolActivityDescription: String) {}
|
||||
}
|
||||
|
||||
sealed interface McpToolSideEffectEvent
|
||||
@@ -26,6 +27,6 @@ class FileDeletedEvent(val file: VirtualFile, val content: String?) : FileEvent
|
||||
class FileMovedEvent(val file: VirtualFile, val oldParent: VirtualFile, val newParent: VirtualFile) : FileEvent
|
||||
class FileContentChangeEvent(val file: VirtualFile, val oldContent: String?, val newContent: String) : FileEvent
|
||||
|
||||
fun reportToolActivity(toolDescription: String) {
|
||||
application.messageBus.syncPublisher(ToolCallListener.TOPIC).toolActivity(toolDescription)
|
||||
fun CoroutineContext.reportToolActivity(toolDescription: String) {
|
||||
application.messageBus.syncPublisher(ToolCallListener.TOPIC).toolActivity(this.currentToolDescriptor, toolDescription)
|
||||
}
|
||||
@@ -217,47 +217,47 @@ private fun McpTool.mcpToolToRegisteredTool(): RegisteredTool {
|
||||
|
||||
EditorFactory.getInstance().eventMulticaster.addDocumentListener(documentListener, this.asDisposable())
|
||||
|
||||
val sideEffectEvents = mutableListOf<McpToolSideEffectEvent>()
|
||||
@Suppress("IncorrectCancellationExceptionHandling")
|
||||
try {
|
||||
application.messageBus.syncPublisher(ToolCallListener.TOPIC).beforeMcpToolCall(this@mcpToolToRegisteredTool.descriptor)
|
||||
|
||||
logger.trace { "Start calling tool '${this@mcpToolToRegisteredTool.descriptor.name}'. Arguments: ${request.arguments}" }
|
||||
val result = withContext(ProjectContextElement(project)) {
|
||||
val result = withContext(ProjectContextElement(project) + McpToolDescriptorElement(this@mcpToolToRegisteredTool.descriptor)) {
|
||||
this@mcpToolToRegisteredTool.call(request.arguments)
|
||||
}
|
||||
|
||||
logger.trace { "Tool call successful '${this@mcpToolToRegisteredTool.descriptor.name}'. Result: ${result.content.joinToString("\n") { it.toString() }}" }
|
||||
try {
|
||||
val processedChangedFiles = mutableSetOf<VirtualFile>()
|
||||
val events = mutableListOf<McpToolSideEffectEvent>()
|
||||
|
||||
for ((doc, oldContent) in initialDocumentContents) {
|
||||
val virtualFile = FileDocumentManager.getInstance().getFile(doc) ?: continue
|
||||
val newContent = readAction { doc.text }
|
||||
events.add(FileContentChangeEvent(virtualFile, oldContent, newContent))
|
||||
sideEffectEvents.add(FileContentChangeEvent(virtualFile, oldContent, newContent))
|
||||
processedChangedFiles.add(virtualFile)
|
||||
}
|
||||
|
||||
for (event in vfsEvent) {
|
||||
when (event) {
|
||||
is VFileMoveEvent -> {
|
||||
events.add(FileMovedEvent(event.file, event.oldParent, event.newParent))
|
||||
sideEffectEvents.add(FileMovedEvent(event.file, event.oldParent, event.newParent))
|
||||
}
|
||||
is VFileCreateEvent -> {
|
||||
val virtualFile = event.file ?: continue
|
||||
val newContent = readAction { FileDocumentManager.getInstance().getDocument(virtualFile)?.text } ?: continue
|
||||
events.add(FileCreatedEvent(virtualFile, newContent))
|
||||
sideEffectEvents.add(FileCreatedEvent(virtualFile, newContent))
|
||||
}
|
||||
is VFileDeleteEvent -> {
|
||||
val virtualFile = event.file
|
||||
val document = readAction { FileDocumentManager.getInstance().getDocument(virtualFile) } ?: continue
|
||||
val oldContent = initialDocumentContents[document]
|
||||
events.add(FileDeletedEvent(virtualFile, oldContent))
|
||||
sideEffectEvents.add(FileDeletedEvent(virtualFile, oldContent))
|
||||
}
|
||||
is VFileCopyEvent -> {
|
||||
val createdFile = event.findCreatedFile() ?: continue
|
||||
val newContent = readAction { FileDocumentManager.getInstance().getDocument(createdFile)?.text } ?: continue
|
||||
events.add(FileCreatedEvent(createdFile, newContent))
|
||||
sideEffectEvents.add(FileCreatedEvent(createdFile, newContent))
|
||||
}
|
||||
is VFileContentChangeEvent -> {
|
||||
// reported in documents loop
|
||||
@@ -267,12 +267,11 @@ private fun McpTool.mcpToolToRegisteredTool(): RegisteredTool {
|
||||
// Important: there may be a case when file is changed via low level change (like File.replaceText).
|
||||
// in this case we don't track the old content, because it may be heavy, it requires loading the file in
|
||||
// AsyncFileListener above and decoding with encoding etc. The file can be binary etc.
|
||||
events.add(FileContentChangeEvent(virtualFile, oldContent = null, newContent = newContent))
|
||||
sideEffectEvents.add(FileContentChangeEvent(virtualFile, oldContent = null, newContent = newContent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application.messageBus.syncPublisher(ToolCallListener.TOPIC).afterMcpToolCall(this@mcpToolToRegisteredTool.descriptor, events)
|
||||
}
|
||||
catch (ce: CancellationException) {
|
||||
throw ce
|
||||
@@ -280,20 +279,24 @@ private fun McpTool.mcpToolToRegisteredTool(): RegisteredTool {
|
||||
catch (t: Throwable) {
|
||||
logger.error("Failed to process changed documents after calling MCP tool ${this@mcpToolToRegisteredTool.descriptor.name}", t)
|
||||
}
|
||||
application.messageBus.syncPublisher(ToolCallListener.TOPIC).afterMcpToolCall(this@mcpToolToRegisteredTool.descriptor, sideEffectEvents, null)
|
||||
result
|
||||
}
|
||||
catch (ce: CancellationException) {
|
||||
val message = "MCP tool call has been cancelled: ${ce.message}"
|
||||
logger.traceThrowable { CancellationException(message, ce) }
|
||||
application.messageBus.syncPublisher(ToolCallListener.TOPIC).afterMcpToolCall(this@mcpToolToRegisteredTool.descriptor, sideEffectEvents, ce)
|
||||
McpToolCallResult.error(message)
|
||||
}
|
||||
catch (mcpException: McpExpectedError) {
|
||||
logger.traceThrowable { mcpException }
|
||||
application.messageBus.syncPublisher(ToolCallListener.TOPIC).afterMcpToolCall(this@mcpToolToRegisteredTool.descriptor, sideEffectEvents, mcpException)
|
||||
McpToolCallResult.error(mcpException.mcpErrorText)
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
val errorMessage = "MCP tool call has been failed: ${t.message}"
|
||||
logger.error(t)
|
||||
application.messageBus.syncPublisher(ToolCallListener.TOPIC).afterMcpToolCall(this@mcpToolToRegisteredTool.descriptor, sideEffectEvents, t)
|
||||
McpToolCallResult.error(errorMessage)
|
||||
}
|
||||
finally {
|
||||
|
||||
@@ -51,7 +51,7 @@ class AnalysisToolset : McpToolset {
|
||||
@McpDescription(Constants.TIMEOUT_MILLISECONDS_DESCRIPTION)
|
||||
timeout: Int = Constants.MEDIUM_TIMEOUT_MILLISECONDS_VALUE,
|
||||
): FileProblemsResult {
|
||||
reportToolActivity("Collecting problems in file $filePath")
|
||||
currentCoroutineContext().reportToolActivity("Collecting problems in file $filePath")
|
||||
val project = currentCoroutineContext().project
|
||||
val projectDir = project.projectDirectory
|
||||
|
||||
@@ -107,6 +107,7 @@ class AnalysisToolset : McpToolset {
|
||||
@McpDescription(Constants.TIMEOUT_MILLISECONDS_DESCRIPTION)
|
||||
timeout: Int = Constants.LONG_TIMEOUT_MILLISECONDS_VALUE,
|
||||
): ProjectProblemsResult {
|
||||
currentCoroutineContext().reportToolActivity("Checking project issues")
|
||||
val project = currentCoroutineContext().project
|
||||
|
||||
val problems = CopyOnWriteArrayList<ProjectProblem>()
|
||||
@@ -144,6 +145,7 @@ class AnalysisToolset : McpToolset {
|
||||
|Returns structured information about each module including name and type.
|
||||
""")
|
||||
suspend fun get_project_modules(): ProjectModulesResult {
|
||||
currentCoroutineContext().reportToolActivity("Listing modules")
|
||||
val project = currentCoroutineContext().project
|
||||
|
||||
val modules = readAction {
|
||||
@@ -165,6 +167,7 @@ class AnalysisToolset : McpToolset {
|
||||
|Returns structured information about project library names.
|
||||
""")
|
||||
suspend fun get_project_dependencies(): ProjectDependenciesResult {
|
||||
currentCoroutineContext().reportToolActivity("Checking dependencies")
|
||||
val project = currentCoroutineContext().project
|
||||
|
||||
val dependencies = readAction {
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.intellij.mcpserver.annotations.McpDescription
|
||||
import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.mcpFail
|
||||
import com.intellij.mcpserver.project
|
||||
import com.intellij.mcpserver.reportToolActivity
|
||||
import com.intellij.mcpserver.toolsets.Constants
|
||||
import com.intellij.mcpserver.util.SymbolInfo
|
||||
import com.intellij.mcpserver.util.convertHtmlToMarkdown
|
||||
@@ -45,6 +46,7 @@ class CodeInsightToolset : McpToolset {
|
||||
@McpDescription("1-based column number")
|
||||
column: Int,
|
||||
): SymbolInfoResult {
|
||||
currentCoroutineContext().reportToolActivity("Getting symbol info at '$filePath:$line:$column'")
|
||||
val project = currentCoroutineContext().project
|
||||
|
||||
val resolvedPath = project.resolveInProject(filePath)
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.intellij.mcpserver.annotations.McpDescription
|
||||
import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.mcpFail
|
||||
import com.intellij.mcpserver.project
|
||||
import com.intellij.mcpserver.reportToolActivity
|
||||
import com.intellij.mcpserver.toolsets.Constants
|
||||
import com.intellij.mcpserver.util.TruncateMode
|
||||
import com.intellij.mcpserver.util.checkUserConfirmationIfNeeded
|
||||
@@ -43,6 +44,7 @@ class ExecutionToolset : McpToolset {
|
||||
|Use this tool to query the list of available run configurations in the current project.
|
||||
""")
|
||||
suspend fun get_run_configurations(): RunConfigurationsList {
|
||||
currentCoroutineContext().reportToolActivity("Getting run configurations")
|
||||
val project = currentCoroutineContext().project
|
||||
val runManager = RunManager.getInstance(project)
|
||||
|
||||
@@ -78,6 +80,7 @@ class ExecutionToolset : McpToolset {
|
||||
@McpDescription(Constants.TRUNCATE_MODE_DESCRIPTION)
|
||||
truncateMode: TruncateMode = Constants.TRUCATE_MODE_VALUE,
|
||||
): RunConfigurationResult {
|
||||
currentCoroutineContext().reportToolActivity("Executing run configuration '$configurationName'")
|
||||
val project = currentCoroutineContext().project
|
||||
val runManager = RunManager.getInstance(project)
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
package com.intellij.mcpserver.toolsets.general
|
||||
|
||||
import com.intellij.mcpserver.McpServerBundle
|
||||
import com.intellij.mcpserver.McpToolset
|
||||
import com.intellij.mcpserver.*
|
||||
import com.intellij.mcpserver.annotations.McpDescription
|
||||
import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.mcpFail
|
||||
@@ -31,6 +30,7 @@ import com.intellij.platform.ide.progress.withBackgroundProgress
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.io.IOException
|
||||
import kotlinx.serialization.EncodeDefault
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -56,6 +56,7 @@ class FileToolset : McpToolset {
|
||||
@McpDescription("Maximum recursion depth") maxDepth: Int = 5,
|
||||
@McpDescription(Constants.TIMEOUT_MILLISECONDS_DESCRIPTION) timeout: Int = Constants.MEDIUM_TIMEOUT_MILLISECONDS_VALUE,
|
||||
): DirectoryTreeInfo {
|
||||
currentCoroutineContext().reportToolActivity("Traversing folder tree for '$directoryPath'")
|
||||
val project = currentCoroutineContext().project
|
||||
val resolvedPath = project.resolveInProject(directoryPath)
|
||||
if (!resolvedPath.exists()) mcpFail("No such directory: $resolvedPath")
|
||||
@@ -93,6 +94,7 @@ class FileToolset : McpToolset {
|
||||
@McpDescription("Timeout in milliseconds")
|
||||
timeout: Int = Constants.MEDIUM_TIMEOUT_MILLISECONDS_VALUE,
|
||||
): FilesListResult {
|
||||
currentCoroutineContext().reportToolActivity("Finding files with name containing '$nameKeyword'")
|
||||
val project = currentCoroutineContext().project
|
||||
val projectDir = project.projectDirectory
|
||||
|
||||
@@ -143,6 +145,7 @@ class FileToolset : McpToolset {
|
||||
@McpDescription(Constants.TIMEOUT_MILLISECONDS_DESCRIPTION)
|
||||
timeout: Int = Constants.MEDIUM_TIMEOUT_MILLISECONDS_VALUE
|
||||
) : FilesListResult {
|
||||
currentCoroutineContext().reportToolActivity("Finding files by glob '$globPattern'")
|
||||
val project = currentCoroutineContext().project
|
||||
val projectDirPath = project.projectDirectory
|
||||
val fileIndex = ProjectRootManager.getInstance(project).getFileIndex()
|
||||
@@ -202,6 +205,7 @@ class FileToolset : McpToolset {
|
||||
@McpDescription(Constants.RELATIVE_PATH_IN_PROJECT_DESCRIPTION)
|
||||
filePath: String,
|
||||
) {
|
||||
currentCoroutineContext().reportToolActivity("Opening file '$filePath'")
|
||||
val project = currentCoroutineContext().project
|
||||
val resolvedPath = project.resolveInProject(filePath)
|
||||
|
||||
@@ -222,6 +226,7 @@ class FileToolset : McpToolset {
|
||||
|Use this tool to explore current open editors.
|
||||
""")
|
||||
suspend fun get_all_open_file_paths(): OpenFilesInfo {
|
||||
currentCoroutineContext().reportToolActivity("Getting open files")
|
||||
val project = currentCoroutineContext().project
|
||||
val projectDir = project.projectDirectory
|
||||
|
||||
@@ -250,10 +255,21 @@ class FileToolset : McpToolset {
|
||||
@McpDescription("Content to write into the new file")
|
||||
text: String? = null,
|
||||
) {
|
||||
currentCoroutineContext().reportToolActivity("Creating file '$pathInProject'")
|
||||
val project = currentCoroutineContext().project
|
||||
|
||||
val path = project.resolveInProject(pathInProject)
|
||||
val newFile = LocalFileSystem.getInstance().createChildFile(null, VfsUtil.createDirectories(path.parent.pathString), path.name)
|
||||
val newFile = try {
|
||||
LocalFileSystem.getInstance().createChildFile(null, VfsUtil.createDirectories(path.parent.pathString), path.name)
|
||||
}
|
||||
catch (io: IOException) {
|
||||
mcpFail("Can't create file: $path: ${io.message}")
|
||||
}
|
||||
val refreshed = CompletableDeferred<Unit>()
|
||||
LocalFileSystem.getInstance().refreshFiles(listOf(newFile), true, false) {
|
||||
refreshed.complete(Unit)
|
||||
}
|
||||
refreshed.await()
|
||||
writeAction {
|
||||
val document = FileDocumentManager.getInstance().getDocument(newFile, project) ?: mcpFail("Can't get document for created file: $newFile")
|
||||
if (text != null) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.intellij.mcpserver.annotations.McpDescription
|
||||
import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.mcpFail
|
||||
import com.intellij.mcpserver.project
|
||||
import com.intellij.mcpserver.reportToolActivity
|
||||
import com.intellij.mcpserver.toolsets.Constants
|
||||
import com.intellij.mcpserver.util.resolveInProject
|
||||
import com.intellij.openapi.application.EDT
|
||||
@@ -29,6 +30,7 @@ class FormattingToolset : McpToolset {
|
||||
@McpDescription(Constants.RELATIVE_PATH_IN_PROJECT_DESCRIPTION)
|
||||
path: String,
|
||||
): String {
|
||||
currentCoroutineContext().reportToolActivity("Formatting file '$path'")
|
||||
val project = currentCoroutineContext().project
|
||||
val resolvedFilePath = project.resolveInProject(path)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.mcpserver.annotations.McpDescription
|
||||
import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.mcpFail
|
||||
import com.intellij.mcpserver.project
|
||||
import com.intellij.mcpserver.reportToolActivity
|
||||
import com.intellij.mcpserver.toolsets.Constants
|
||||
import com.intellij.mcpserver.util.resolveInProject
|
||||
import com.intellij.openapi.application.EDT
|
||||
@@ -44,6 +45,7 @@ class RefactoringToolset : McpToolset {
|
||||
@McpDescription("New name for the symbol")
|
||||
newName: String,
|
||||
): String {
|
||||
currentCoroutineContext().reportToolActivity("Renaming '$symbolName' to '$newName' in '$pathInProject'")
|
||||
val project = currentCoroutineContext().project
|
||||
val resolvedPath = project.resolveInProject(pathInProject)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.intellij.mcpserver.annotations.McpDescription
|
||||
import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.mcpFail
|
||||
import com.intellij.mcpserver.project
|
||||
import com.intellij.mcpserver.reportToolActivity
|
||||
import com.intellij.mcpserver.toolsets.Constants
|
||||
import com.intellij.mcpserver.util.*
|
||||
import com.intellij.openapi.application.readAction
|
||||
@@ -51,6 +52,7 @@ class TextToolset : McpToolset {
|
||||
@McpDescription("Max number of lines to return. Truncation will be performed depending on truncateMode.")
|
||||
maxLinesCount: Int = 1000,
|
||||
): String {
|
||||
currentCoroutineContext().reportToolActivity("Reading file '$pathInProject'")
|
||||
val project = currentCoroutineContext().project
|
||||
val resolvedPath = project.resolveInProject(pathInProject)
|
||||
|
||||
@@ -102,6 +104,7 @@ class TextToolset : McpToolset {
|
||||
@McpDescription("Case-sensitive search")
|
||||
caseSensitive: Boolean = true,
|
||||
) {
|
||||
currentCoroutineContext().reportToolActivity("Replacing text in '$pathInProject': '$oldText' → '$newText'")
|
||||
val project = currentCoroutineContext().project
|
||||
val resolvedPath = project.resolveInProject(pathInProject)
|
||||
val (document, text) = readAction {
|
||||
@@ -153,7 +156,10 @@ class TextToolset : McpToolset {
|
||||
maxUsageCount: Int = 1000,
|
||||
@McpDescription(Constants.TIMEOUT_MILLISECONDS_DESCRIPTION)
|
||||
timeout: Int = Constants.MEDIUM_TIMEOUT_MILLISECONDS_VALUE,
|
||||
): UsageInfoResult = search_in_files(searchText, false, directoryToSearch, fileMask, caseSensitive, maxUsageCount, timeout)
|
||||
): UsageInfoResult {
|
||||
currentCoroutineContext().reportToolActivity("Searching project files for '$searchText'")
|
||||
return search_in_files(searchText, false, directoryToSearch, fileMask, caseSensitive, maxUsageCount, timeout)
|
||||
}
|
||||
|
||||
@McpTool
|
||||
@McpDescription("""
|
||||
@@ -175,7 +181,10 @@ class TextToolset : McpToolset {
|
||||
maxUsageCount: Int = 1000,
|
||||
@McpDescription(Constants.TIMEOUT_MILLISECONDS_DESCRIPTION)
|
||||
timeout: Int = Constants.MEDIUM_TIMEOUT_MILLISECONDS_VALUE,
|
||||
): UsageInfoResult = search_in_files(regexPattern, true, directoryToSearch, fileMask, caseSensitive, maxUsageCount, timeout)
|
||||
): UsageInfoResult {
|
||||
currentCoroutineContext().reportToolActivity("Searching content with regex '$regexPattern'")
|
||||
return search_in_files(regexPattern, true, directoryToSearch, fileMask, caseSensitive, maxUsageCount, timeout)
|
||||
}
|
||||
|
||||
private suspend fun search_in_files(
|
||||
searchTextOrRegex: String,
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.mcpserver.McpToolset
|
||||
import com.intellij.mcpserver.annotations.McpDescription
|
||||
import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.project
|
||||
import com.intellij.mcpserver.reportToolActivity
|
||||
import com.intellij.mcpserver.toolsets.Constants
|
||||
import com.intellij.mcpserver.util.TruncateMode
|
||||
import com.intellij.mcpserver.util.checkUserConfirmationIfNeeded
|
||||
@@ -53,6 +54,7 @@ class TerminalToolset : McpToolset {
|
||||
@McpDescription(Constants.TRUNCATE_MODE_DESCRIPTION)
|
||||
truncateMode: TruncateMode = Constants.TRUCATE_MODE_VALUE,
|
||||
): CommandExecutionResult {
|
||||
currentCoroutineContext().reportToolActivity("Running command: '$command'")
|
||||
val project = currentCoroutineContext().project
|
||||
checkUserConfirmationIfNeeded(McpServerBundle.message("label.do.you.want.to.execute.command.in.terminal"), command, project)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.intellij.mcpserver.McpToolset
|
||||
import com.intellij.mcpserver.annotations.McpDescription
|
||||
import com.intellij.mcpserver.annotations.McpTool
|
||||
import com.intellij.mcpserver.project
|
||||
import com.intellij.mcpserver.reportToolActivity
|
||||
import com.intellij.mcpserver.util.projectDirectory
|
||||
import com.intellij.openapi.vcs.ProjectLevelVcsManager
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager
|
||||
@@ -26,6 +27,7 @@ class VcsToolset : McpToolset {
|
||||
@McpDescription("Text or keywords to search for in commit messages")
|
||||
text: String
|
||||
): String {
|
||||
currentCoroutineContext().reportToolActivity("Searching commits for '$text'")
|
||||
val project = currentCoroutineContext().project
|
||||
val queryText = text
|
||||
val matchingCommits = mutableListOf<String>()
|
||||
@@ -77,6 +79,7 @@ class VcsToolset : McpToolset {
|
||||
Note: Works with any VCS supported by the IDE, but is most commonly used with Git
|
||||
""")
|
||||
suspend fun get_project_vcs_status(): String {
|
||||
currentCoroutineContext().reportToolActivity("Checking VCS status")
|
||||
val project = currentCoroutineContext().project
|
||||
val projectDir = project.projectDirectory
|
||||
|
||||
|
||||
Reference in New Issue
Block a user