Skip to content

Commit c362fa7

Browse files
committed
feat(devti): improve MCP server configuration handling and UI #371
- Add getServerConfigs function to parse server configurations - Implement per-server tool loading with error handling - Update UI to dynamically show loading state and errors - Improve tool card layout and add scroll support for long descriptions - Refactor addTools to update UI incrementally
1 parent 72d1b82 commit c362fa7

File tree

2 files changed

+54
-19
lines changed

2 files changed

+54
-19
lines changed

core/src/main/kotlin/cc/unitmesh/devti/mcp/client/CustomMcpServerManager.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ class CustomMcpServerManager(val project: Project) {
4747
return toolsMap
4848
}
4949

50+
fun getServerConfigs(content: String): Map<String, McpServer>? {
51+
val mcpConfig = McpServer.load(content)
52+
return mcpConfig?.mcpServers
53+
}
54+
5055
suspend fun collectServerInfo(serverKey: String, serverConfig: McpServer): List<Tool> {
5156
val resolvedCommand = resolveCommand(serverConfig.command)
5257
logger<CustomMcpServerManager>().info("Found MCP command for $serverKey: $resolvedCommand")

core/src/main/kotlin/cc/unitmesh/devti/mcp/editor/McpPreviewEditor.kt

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import java.beans.PropertyChangeListener
3535
import javax.swing.*
3636
import javax.swing.border.CompoundBorder
3737
import cc.unitmesh.devti.sketch.ui.patch.readText
38+
import kotlinx.coroutines.Job
3839

3940
/**
4041
* Display shire file render prompt and have a sample file as view
@@ -54,6 +55,7 @@ open class McpPreviewEditor(
5455

5556
private val mcpServerManager = CustomMcpServerManager.instance(project)
5657
private val allTools = mutableMapOf<String, List<Tool>>()
58+
private var loadingJob: Job? = null
5759

5860
data class ChatbotConfig(
5961
var temperature: Double = 0.7,
@@ -70,6 +72,10 @@ open class McpPreviewEditor(
7072
private val borderColor = JBColor(0xE5E7EB, 0x3C3F41) // Equivalent to Tailwind gray-200
7173
private val primaryBlue = JBColor(0x3B82F6, 0x589DF6) // Equivalent to Tailwind blue-500
7274
private val textGray = JBColor(0x6B7280, 0x9DA0A8) // Equivalent to Tailwind gray-500
75+
76+
// Constants for UI sizing
77+
private val MAX_TOOL_CARD_HEIGHT = 180
78+
private val TOOL_CARD_WIDTH = 300
7379

7480
init {
7581
createUI()
@@ -78,14 +84,28 @@ open class McpPreviewEditor(
7884

7985
private fun loadTools() {
8086
val content = runReadAction { virtualFile.readText() }
81-
CoroutineScope(Dispatchers.IO).launch {
82-
allTools.putAll(mcpServerManager.collectServerInfos(content))
83-
SwingUtilities.invokeLater {
84-
addTools()
87+
loadingJob?.cancel()
88+
loadingJob = CoroutineScope(Dispatchers.IO).launch {
89+
val serverConfigs = mcpServerManager.getServerConfigs(content)
90+
serverConfigs?.forEach { (serverName, serverConfig) ->
91+
try {
92+
val tools = mcpServerManager.collectServerInfo(serverName, serverConfig)
93+
if (tools.isNotEmpty()) {
94+
allTools[serverName] = tools
95+
// Update UI after each server's tools are loaded
96+
SwingUtilities.invokeLater {
97+
updateToolsContainer()
98+
}
99+
}
100+
} catch (e: Exception) {
101+
// Handle exception for this server but continue with others
102+
println("Error loading tools from server $serverName: ${e.message}")
103+
}
85104
}
86105
}
87106
}
88107

108+
89109
private fun createUI() {
90110
val headerPanel = panel {
91111
row {
@@ -203,27 +223,24 @@ open class McpPreviewEditor(
203223
mainPanel.add(bottomPanel, BorderLayout.SOUTH)
204224
}
205225

206-
private fun addTools() {
226+
private fun updateToolsContainer() {
207227
toolsContainer.removeAll()
208-
228+
209229
if (allTools.isEmpty()) {
210230
val noToolsLabel = JBLabel("No tools available. Please check MCP server configuration.").apply {
211231
font = JBUI.Fonts.label(14.0f)
212232
foreground = textGray
213233
}
214234
toolsContainer.add(noToolsLabel)
215-
toolsContainer.revalidate()
216-
toolsContainer.repaint()
217-
return
218-
}
219-
220-
allTools.forEach { (serverName, tools) ->
221-
tools.forEach { tool ->
222-
val toolCard = createToolCard(serverName, tool)
223-
toolsContainer.add(toolCard)
235+
} else {
236+
allTools.forEach { (serverName, tools) ->
237+
tools.forEach { tool ->
238+
val toolCard = createToolCard(serverName, tool)
239+
toolsContainer.add(toolCard)
240+
}
224241
}
225242
}
226-
243+
227244
toolsContainer.revalidate()
228245
toolsContainer.repaint()
229246
}
@@ -235,6 +252,9 @@ open class McpPreviewEditor(
235252
BorderFactory.createLineBorder(borderColor),
236253
JBUI.Borders.empty(16)
237254
)
255+
// Set preferred width and maximum height
256+
preferredSize = Dimension(TOOL_CARD_WIDTH, MAX_TOOL_CARD_HEIGHT)
257+
maximumSize = Dimension(Integer.MAX_VALUE, MAX_TOOL_CARD_HEIGHT)
238258
}
239259

240260
// Card header with icon placeholder and title
@@ -269,14 +289,22 @@ open class McpPreviewEditor(
269289
add(titleWrapper, BorderLayout.CENTER)
270290
}
271291

292+
// Make description scrollable if it's too long
272293
val descriptionText = tool.description ?: "No description available"
273-
val descLabel = JBLabel("$descriptionText (from $serverName)").apply {
294+
val descLabel = JBLabel("<html><body style='width: ${TOOL_CARD_WIDTH - 50}px'>$descriptionText (from $serverName)</body></html>").apply {
274295
font = JBUI.Fonts.label(14.0f)
275296
foreground = textGray
276297
}
298+
299+
val descScrollPane = JBScrollPane(descLabel).apply {
300+
border = BorderFactory.createEmptyBorder()
301+
verticalScrollBar.unitIncrement = 8
302+
background = UIUtil.getPanelBackground()
303+
preferredSize = Dimension(TOOL_CARD_WIDTH - 32, 70) // Control description height
304+
}
277305

278306
headerPanel.add(titleRow, BorderLayout.NORTH)
279-
headerPanel.add(descLabel, BorderLayout.SOUTH)
307+
headerPanel.add(descScrollPane, BorderLayout.CENTER)
280308

281309
// Card footer with button
282310
val footerPanel = JPanel(BorderLayout()).apply {
@@ -494,5 +522,7 @@ open class McpPreviewEditor(
494522
override fun getPreferredFocusedComponent(): JComponent? = chatInput
495523
override fun addPropertyChangeListener(listener: PropertyChangeListener) {}
496524
override fun removePropertyChangeListener(listener: PropertyChangeListener) {}
497-
override fun dispose() {}
525+
override fun dispose() {
526+
loadingJob?.cancel()
527+
}
498528
}

0 commit comments

Comments
 (0)