Skip to content

Commit a1193ae

Browse files
committed
feat(mcp): improve server tool loading and error handling in UI #371
1 parent ae59b16 commit a1193ae

File tree

1 file changed

+134
-58
lines changed

1 file changed

+134
-58
lines changed

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

Lines changed: 134 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ open class McpPreviewEditor(
5454
private val mcpServerManager = CustomMcpServerManager.instance(project)
5555
private val allTools = mutableMapOf<String, List<Tool>>()
5656
private var loadingJob: Job? = null
57-
private var isLoading = false
57+
private val serverLoadingStatus = mutableMapOf<String, Boolean>()
58+
private val serverPanels = mutableMapOf<String, JPanel>()
5859

5960
private lateinit var toolsContainer: JPanel
6061
private lateinit var chatbotSelector: JComboBox<String>
@@ -65,6 +66,7 @@ open class McpPreviewEditor(
6566
private val config = McpLlmConfig()
6667
private val borderColor = JBColor(0xE5E7EB, 0x3C3F41) // Equivalent to Tailwind gray-200
6768
private val textGray = JBColor(0x6B7280, 0x9DA0A8) // Equivalent to Tailwind gray-500
69+
private val headerColor = JBColor(0xF3F4F6, 0x2B2D30) // Light gray for section headers
6870

6971
init {
7072
createUI()
@@ -74,52 +76,152 @@ open class McpPreviewEditor(
7476
private fun loadTools() {
7577
val content = runReadAction { virtualFile.readText() }
7678
loadingJob?.cancel()
77-
isLoading = true
78-
showLoadingState()
79+
serverLoadingStatus.clear()
80+
serverPanels.clear()
81+
allTools.clear()
82+
83+
SwingUtilities.invokeLater {
84+
toolsContainer.removeAll()
85+
toolsContainer.revalidate()
86+
toolsContainer.repaint()
87+
}
7988

8089
loadingJob = CoroutineScope(Dispatchers.IO).launch {
8190
val serverConfigs = mcpServerManager.getServerConfigs(content)
82-
serverConfigs?.forEach { (serverName, serverConfig) ->
91+
92+
if (serverConfigs.isNullOrEmpty()) {
93+
SwingUtilities.invokeLater {
94+
showNoServersMessage()
95+
}
96+
return@launch
97+
}
98+
99+
SwingUtilities.invokeLater {
100+
serverConfigs.keys.forEach { serverName ->
101+
serverLoadingStatus[serverName] = true
102+
createServerSection(serverName)
103+
}
104+
}
105+
106+
serverConfigs.forEach { (serverName, serverConfig) ->
83107
try {
84108
val tools = mcpServerManager.collectServerInfo(serverName, serverConfig)
85-
if (tools.isNotEmpty()) {
86-
allTools[serverName] = tools
87-
SwingUtilities.invokeLater {
88-
updateToolsContainer()
89-
}
109+
allTools[serverName] = tools
110+
111+
SwingUtilities.invokeLater {
112+
updateServerSection(serverName, tools)
113+
serverLoadingStatus[serverName] = false
90114
}
91115
} catch (e: Exception) {
92-
// Handle exception for this server but continue with others
93-
println("Error loading tools from server $serverName: ${e.message}")
116+
SwingUtilities.invokeLater {
117+
showServerError(serverName, e.message ?: "Unknown error")
118+
serverLoadingStatus[serverName] = false
119+
}
94120
}
95121
}
96-
97-
isLoading = false
98-
SwingUtilities.invokeLater {
99-
updateToolsContainer()
100-
}
101122
}
102123
}
103-
104-
private fun showLoadingState() {
105-
SwingUtilities.invokeLater {
106-
toolsContainer.removeAll()
107-
val loadingLabel = JBLabel("Loading tools... Please wait").apply {
108-
font = JBUI.Fonts.label(14.0f)
124+
125+
private fun createServerSection(serverName: String) {
126+
val serverPanel = JPanel(BorderLayout()).apply {
127+
background = UIUtil.getPanelBackground()
128+
border = BorderFactory.createCompoundBorder(
129+
BorderFactory.createMatteBorder(0, 0, 1, 0, borderColor),
130+
JBUI.Borders.empty(4, 0)
131+
)
132+
}
133+
134+
val headerPanel = JPanel(BorderLayout()).apply {
135+
background = headerColor
136+
border = JBUI.Borders.empty(4, 8)
137+
}
138+
139+
val serverLabel = JBLabel(serverName).apply {
140+
font = JBUI.Fonts.label(14.0f).asBold()
141+
foreground = UIUtil.getLabelForeground()
142+
}
143+
144+
headerPanel.add(serverLabel, BorderLayout.WEST)
145+
serverPanel.add(headerPanel, BorderLayout.NORTH)
146+
147+
val toolsPanel = JPanel(GridLayout(0, 3, 4, 4)).apply {
148+
background = UIUtil.getPanelBackground()
149+
border = JBUI.Borders.empty()
150+
}
151+
152+
val loadingLabel = JBLabel("Loading tools from $serverName...").apply {
153+
font = JBUI.Fonts.label(12.0f)
154+
foreground = textGray
155+
horizontalAlignment = SwingConstants.LEFT
156+
icon = AutoDevIcons.LOADING
157+
iconTextGap = JBUI.scale(8)
158+
}
159+
160+
toolsPanel.add(loadingLabel)
161+
serverPanel.add(toolsPanel, BorderLayout.CENTER)
162+
163+
serverPanels[serverName] = toolsPanel
164+
165+
toolsContainer.add(serverPanel)
166+
toolsContainer.revalidate()
167+
toolsContainer.repaint()
168+
}
169+
170+
private fun updateServerSection(serverName: String, tools: List<Tool>) {
171+
val toolsPanel = serverPanels[serverName] ?: return
172+
toolsPanel.removeAll()
173+
174+
if (tools.isEmpty()) {
175+
val noToolsLabel = JBLabel("No tools available for $serverName").apply {
109176
foreground = textGray
110-
horizontalAlignment = SwingConstants.CENTER
111-
icon = AutoDevIcons.LOADING
112-
iconTextGap = JBUI.scale(8)
177+
horizontalAlignment = SwingConstants.LEFT
113178
}
114-
115-
toolsContainer.add(loadingLabel)
116-
toolsContainer.revalidate()
117-
toolsContainer.repaint()
179+
toolsPanel.add(noToolsLabel)
180+
} else {
181+
tools.forEach { tool ->
182+
val panel = McpToolDetailPanel(project, serverName, tool)
183+
toolsPanel.add(panel)
184+
}
185+
}
186+
187+
toolsPanel.revalidate()
188+
toolsPanel.repaint()
189+
}
190+
191+
private fun showServerError(serverName: String, errorMessage: String) {
192+
val toolsPanel = serverPanels[serverName] ?: return
193+
toolsPanel.removeAll()
194+
195+
val errorLabel = JBLabel("Error loading tools: $errorMessage").apply {
196+
foreground = JBColor.RED
197+
horizontalAlignment = SwingConstants.LEFT
198+
}
199+
200+
toolsPanel.add(errorLabel)
201+
toolsPanel.revalidate()
202+
toolsPanel.repaint()
203+
}
204+
205+
private fun showNoServersMessage() {
206+
toolsContainer.removeAll()
207+
208+
val noServersPanel = JPanel(BorderLayout()).apply {
209+
background = UIUtil.getPanelBackground()
210+
border = JBUI.Borders.empty(16)
211+
}
212+
213+
val noServersLabel = JBLabel("No MCP servers configured. Please check your configuration.").apply {
214+
foreground = textGray
215+
horizontalAlignment = SwingConstants.CENTER
118216
}
217+
218+
noServersPanel.add(noServersLabel, BorderLayout.CENTER)
219+
toolsContainer.add(noServersPanel)
220+
toolsContainer.revalidate()
221+
toolsContainer.repaint()
119222
}
120223

121224
fun refreshMcpTool() {
122-
allTools.clear()
123225
loadTools()
124226
}
125227

@@ -142,7 +244,8 @@ open class McpPreviewEditor(
142244
border = JBUI.Borders.empty(4)
143245
}
144246

145-
toolsContainer = JPanel(GridLayout(0, 2, 16, 16)).apply {
247+
toolsContainer = JPanel().apply {
248+
layout = BoxLayout(this, BoxLayout.Y_AXIS)
146249
background = UIUtil.getPanelBackground()
147250
}
148251

@@ -244,33 +347,6 @@ open class McpPreviewEditor(
244347
mainPanel.add(bottomPanel, BorderLayout.SOUTH)
245348
}
246349

247-
private fun updateToolsContainer() {
248-
toolsContainer.removeAll()
249-
250-
if (isLoading) {
251-
showLoadingState()
252-
return
253-
}
254-
255-
if (allTools.isEmpty()) {
256-
val noToolsLabel = JBLabel("No tools available. Please check MCP server configuration.").apply {
257-
foreground = textGray
258-
horizontalAlignment = SwingConstants.CENTER
259-
}
260-
toolsContainer.add(noToolsLabel)
261-
} else {
262-
allTools.forEach { (serverName, tools) ->
263-
tools.forEach { tool ->
264-
val panel = McpToolDetailPanel(project, serverName, tool)
265-
toolsContainer.add(panel)
266-
}
267-
}
268-
}
269-
270-
toolsContainer.revalidate()
271-
toolsContainer.repaint()
272-
}
273-
274350
private fun showConfigDialog() {
275351
val dialog = McpLlmConfigDialog(project, config, allTools)
276352

0 commit comments

Comments
 (0)