Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,10 @@ class A2ASketchToolchainProvider : SketchToolchainProvider {

private fun convertAgentCardToTool(agentCard: AgentCard): AgentTool {
val name = agentCard.name() ?: "unknown_agent"

val description = agentCard.description() ?: "A2A Agent"

val skills = agentCard.skills()?.joinToString(", ") { it.name } ?: ""

val fullDescription = if (skills.isNotEmpty()) {
"$description. Available skills: $skills"
} else {
description
}
val fullDescription = agentCard.description() ?: "A2A Agent"

val example = generateExampleUsage(name)

return AgentTool(
name = name,
description = fullDescription,
Expand Down
41 changes: 8 additions & 33 deletions core/src/main/kotlin/cc/unitmesh/devti/a2a/ui/A2AAgentListPanel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import io.a2a.spec.AgentCard
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import java.awt.BorderLayout
import java.awt.FlowLayout
Expand All @@ -21,39 +22,13 @@ import javax.swing.JPanel
import javax.swing.SwingConstants
import javax.swing.SwingUtilities

class A2AAgentListPanel(
private val project: Project
) : JPanel(BorderLayout()) {
class A2AAgentListPanel(private val project: Project) : JPanel(BorderLayout()) {
private val a2aClientConsumer = A2AClientConsumer()
private val textGray = JBColor(0x6B7280, 0x9DA0A8)

private fun getAgentName(agent: AgentCard): String = try {
agent.name() ?: ""
} catch (e: Exception) {
""
}

private fun getAgentDescription(agent: AgentCard): String? = try {
agent.description()
} catch (e: Exception) {
null
}

private fun getProviderName(agent: AgentCard): String? = try {
val provider = agent.provider()
provider?.organization()
} catch (e: Exception) {
null
}

private fun getFieldValue(obj: Any, fieldName: String): Any? = try {
// For backward compatibility with reflection-based access
val field = obj.javaClass.getDeclaredField(fieldName)
field.isAccessible = true
field.get(obj)
} catch (e: Exception) {
null
}
private fun getAgentName(agent: AgentCard): String = agent.name()
private fun getAgentDescription(agent: AgentCard): String? = agent.description()
private fun getProviderName(agent: AgentCard): String? = agent.provider()?.organization()

private var loadingJob: Job? = null
private val serverLoadingStatus = mutableMapOf<String, Boolean>()
Expand Down Expand Up @@ -125,7 +100,7 @@ class A2AAgentListPanel(
}
}

jobs.forEach { it.join() }
jobs.joinAll()
onAgentsLoaded(allA2AAgents)
} catch (e: Exception) {
SwingUtilities.invokeLater {
Expand All @@ -144,8 +119,8 @@ class A2AAgentListPanel(
allA2AAgents.forEach { (serverUrl, agents) ->
val filtered = agents.filter { agent ->
getAgentName(agent).contains(searchText, ignoreCase = true) ||
getAgentDescription(agent)?.contains(searchText, ignoreCase = true) == true ||
getProviderName(agent)?.contains(searchText, ignoreCase = true) == true
getAgentDescription(agent)?.contains(searchText, ignoreCase = true) == true ||
getProviderName(agent)?.contains(searchText, ignoreCase = true) == true
}
if (filtered.isNotEmpty()) {
currentFilteredAgents[serverUrl] = filtered
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package cc.unitmesh.devti.mcp.client

import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.util.SystemInfo
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader

fun resolveCommand(command: String): String {
if (SystemInfo.isWindows) {
try {
val pb = ProcessBuilder("where", command)
val process = pb.start()
val reader = BufferedReader(InputStreamReader(process.inputStream))
val resolved = reader.readLine() // take first non-null output
if (!resolved.isNullOrBlank()) return resolved.trim()
} catch (e: Exception) {
logger<CustomMcpServerManager>().warn("Failed to resolve command using where: $e")
}
} else {
val homeDir = System.getProperty("user.home")
if (command == "npx") {
val knownPaths = listOf(
"/opt/homebrew/bin/npx",
"/usr/local/bin/npx",
"/usr/bin/npx",
"$homeDir/.volta/bin/npx",
"$homeDir/.nvm/current/bin/npx",
"$homeDir/.npm-global/bin/npx"
)
knownPaths.forEach { path ->
if (File(path).exists()) return path
}
}
try {
val pb = ProcessBuilder("which", command)
val currentPath = System.getenv("PATH") ?: ""
val additionalPaths = if (command == "npx") {
listOf(
"/opt/homebrew/bin",
"/opt/homebrew/sbin",
"/usr/local/bin",
"$homeDir/.volta/bin",
"$homeDir/.nvm/current/bin",
"$homeDir/.npm-global/bin"
).joinToString(":")
} else ""
pb.environment()["PATH"] =
if (additionalPaths.isNotBlank()) "$additionalPaths:$currentPath" else currentPath
val process = pb.start()
val reader = BufferedReader(InputStreamReader(process.inputStream))
val resolved = reader.readLine()
if (!resolved.isNullOrBlank()) return resolved.trim()
} catch (e: Exception) {
logger<CustomMcpServerManager>().warn("Failed to resolve command using which: $e")
}
}
return command
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import com.intellij.openapi.components.Service
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.util.SystemInfo
import io.ktor.client.*
import io.ktor.client.engine.cio.*
import io.ktor.client.plugins.sse.SSE
Expand All @@ -15,7 +14,6 @@ import io.modelcontextprotocol.kotlin.sdk.Tool
import io.modelcontextprotocol.kotlin.sdk.client.Client
import io.modelcontextprotocol.kotlin.sdk.client.StdioClientTransport
import io.modelcontextprotocol.kotlin.sdk.client.SseClientTransport
import io.modelcontextprotocol.kotlin.sdk.client.StreamableHttpClientTransport
import kotlinx.coroutines.runBlocking
import kotlinx.io.asSink
import kotlinx.io.asSource
Expand All @@ -24,9 +22,7 @@ import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import kotlin.time.Duration.Companion.seconds
Expand Down Expand Up @@ -153,55 +149,3 @@ class CustomMcpServerManager(val project: Project) {
}
}

fun resolveCommand(command: String): String {
if (SystemInfo.isWindows) {
try {
val pb = ProcessBuilder("where", command)
val process = pb.start()
val reader = BufferedReader(InputStreamReader(process.inputStream))
val resolved = reader.readLine() // take first non-null output
if (!resolved.isNullOrBlank()) return resolved.trim()
} catch (e: Exception) {
logger<CustomMcpServerManager>().warn("Failed to resolve command using where: $e")
}
} else {
val homeDir = System.getProperty("user.home")
if (command == "npx") {
val knownPaths = listOf(
"/opt/homebrew/bin/npx",
"/usr/local/bin/npx",
"/usr/bin/npx",
"$homeDir/.volta/bin/npx",
"$homeDir/.nvm/current/bin/npx",
"$homeDir/.npm-global/bin/npx"
)
knownPaths.forEach { path ->
if (File(path).exists()) return path
}
}
try {
val pb = ProcessBuilder("which", command)
val currentPath = System.getenv("PATH") ?: ""
val additionalPaths = if (command == "npx") {
listOf(
"/opt/homebrew/bin",
"/opt/homebrew/sbin",
"/usr/local/bin",
"$homeDir/.volta/bin",
"$homeDir/.nvm/current/bin",
"$homeDir/.npm-global/bin"
).joinToString(":")
} else ""
pb.environment()["PATH"] =
if (additionalPaths.isNotBlank()) "$additionalPaths:$currentPath" else currentPath
val process = pb.start()
val reader = BufferedReader(InputStreamReader(process.inputStream))
val resolved = reader.readLine()
if (!resolved.isNullOrBlank()) return resolved.trim()
} catch (e: Exception) {
logger<CustomMcpServerManager>().warn("Failed to resolve command using which: $e")
}
}
return command
}

Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,13 @@ class McpServicesTestDialog(private val project: Project) : DialogWrapper(projec
private val job = SupervisorJob()
private val isLoading = AtomicBoolean(false)

// Track server panels and their expansion state
private val serverPanels = mutableMapOf<String, ServerPanel>()
private val expandedServers = mutableSetOf<String>()

init {
title = AutoDevBundle.message("sketch.mcp.testMcp")

// Setup content panel with vertical BoxLayout
contentPanel.layout = BoxLayout(contentPanel, BoxLayout.Y_AXIS)

// Setup search functionality
setupSearch()

init()
Expand Down Expand Up @@ -159,7 +155,6 @@ class McpServicesTestDialog(private val project: Project) : DialogWrapper(projec
}
}

// Revalidate and repaint the main content panel
this@McpServicesTestDialog.contentPanel.revalidate()
this@McpServicesTestDialog.contentPanel.repaint()
}
Expand All @@ -185,7 +180,8 @@ class McpServicesTestDialog(private val project: Project) : DialogWrapper(projec
val description = tableModel.getValueAt(i, 1)?.toString() ?: ""

if (toolName.lowercase().contains(searchText) ||
description.lowercase().contains(searchText)) {
description.lowercase().contains(searchText)
) {
anyToolMatches = true
break
}
Expand Down Expand Up @@ -225,17 +221,14 @@ class McpServicesTestDialog(private val project: Project) : DialogWrapper(projec
override fun createCenterPanel(): JComponent {
val mainPanel = JPanel(BorderLayout())

// Add search field at the top
val searchPanel = JPanel(BorderLayout())
searchPanel.border = JBUI.Borders.emptyBottom(8)
searchPanel.add(searchField, BorderLayout.CENTER)

// Add scroll pane for content
val scrollPane = JBScrollPane(contentPanel)
scrollPane.border = JBUI.Borders.empty()
scrollPane.preferredSize = Dimension(800, 400)

// Add components to loading panel
loadingPanel.add(searchPanel, BorderLayout.NORTH)
loadingPanel.add(scrollPane, BorderLayout.CENTER)

Expand Down Expand Up @@ -267,7 +260,6 @@ class McpServicesTestDialog(private val project: Project) : DialogWrapper(projec
loadingPanel.stopLoading()
isLoading.set(false)
} catch (e: Exception) {
// Clear existing panels
contentPanel.removeAll()
serverPanels.clear()

Expand All @@ -291,9 +283,7 @@ class McpServicesTestDialog(private val project: Project) : DialogWrapper(projec
}

private fun updateServerPanels(serverInfos: Map<String, List<Tool>>) {
// Run on UI thread
SwingUtilities.invokeLater {
// Clear existing panels
contentPanel.removeAll()
serverPanels.clear()

Expand All @@ -309,14 +299,12 @@ class McpServicesTestDialog(private val project: Project) : DialogWrapper(projec
return@invokeLater
}

// Create panel for each server
serverInfos.forEach { (server, tools) ->
val serverPanel = ServerPanel(server, tools)
serverPanels[server] = serverPanel
contentPanel.add(serverPanel.panel)
}

// Revalidate and repaint
contentPanel.revalidate()
contentPanel.repaint()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ object ShadowWorker {
val templateRender = TemplateRender(GENIUS_CODE)
val template = templateRender.getTemplate("sketch.vm")
val customContext = SketchRunContext.create(project, null, "")
var systemPrompt = templateRender.renderTemplate(template, customContext)
return systemPrompt
return templateRender.renderTemplate(template, customContext)
}
}
Loading