1
1
package cc.unitmesh.devti.mcp.editor
2
2
3
+ import cc.unitmesh.devti.mcp.client.CustomMcpServerManager
3
4
import com.intellij.openapi.editor.Editor
4
5
import com.intellij.openapi.fileEditor.FileEditor
5
6
import com.intellij.openapi.fileEditor.FileEditorState
@@ -19,7 +20,12 @@ import com.intellij.ui.dsl.builder.TopGap
19
20
import com.intellij.ui.dsl.builder.panel
20
21
import com.intellij.util.ui.JBUI
21
22
import com.intellij.util.ui.UIUtil
23
+ import io.modelcontextprotocol.kotlin.sdk.Tool
24
+ import kotlinx.coroutines.CoroutineScope
25
+ import kotlinx.coroutines.Dispatchers
22
26
import kotlinx.coroutines.flow.MutableStateFlow
27
+ import kotlinx.coroutines.launch
28
+ import kotlinx.serialization.json.JsonElement
23
29
import java.awt.BorderLayout
24
30
import java.awt.Dimension
25
31
import java.awt.FlowLayout
@@ -44,14 +50,13 @@ open class McpPreviewEditor(
44
50
ScrollPaneConstants .HORIZONTAL_SCROLLBAR_NEVER
45
51
)
46
52
47
- private var editorPanel: JPanel ? = null
48
-
49
- data class Tool (val id : String , val name : String , val description : String )
53
+ private val mcpServerManager = CustomMcpServerManager .instance(project)
54
+ private val allTools = mutableMapOf<String , List <Tool >>()
50
55
51
56
data class ChatbotConfig (
52
57
var temperature : Double = 0.7 ,
53
58
var maxTokens : Int = 2000 ,
54
- var enabledTools : MutableList <String > = mutableListOf("database", "file", "web" )
59
+ var enabledTools : MutableList <String > = mutableListOf()
55
60
)
56
61
57
62
private lateinit var toolsContainer: JPanel
@@ -66,6 +71,16 @@ open class McpPreviewEditor(
66
71
67
72
init {
68
73
createUI()
74
+ loadTools()
75
+ }
76
+
77
+ private fun loadTools () {
78
+ CoroutineScope (Dispatchers .IO ).launch {
79
+ allTools.putAll(mcpServerManager.collectServerInfos())
80
+ SwingUtilities .invokeLater {
81
+ addTools()
82
+ }
83
+ }
69
84
}
70
85
71
86
private fun createUI () {
@@ -75,18 +90,11 @@ open class McpPreviewEditor(
75
90
fontColor = UIUtil .FontColor .BRIGHTER
76
91
background = JBColor (0xF5F5F5 , 0x2B2D30 )
77
92
font = JBUI .Fonts .label(18.0f ).asBold()
78
- border = JBUI .Borders .empty(16 )
93
+ border = JBUI .Borders .empty(8 )
79
94
isOpaque = true
80
95
}
81
96
82
97
cell(label).align(Align .FILL ).resizableColumn()
83
-
84
- val subtitle = JBLabel (" Available tools and suggested actions" ).apply {
85
- fontColor = UIUtil .FontColor .BRIGHTER
86
- font = JBUI .Fonts .label(12.0f )
87
- foreground = textGray
88
- }
89
- cell(subtitle).align(Align .FILL ).resizableColumn()
90
98
}
91
99
}.apply {
92
100
border = BorderFactory .createMatteBorder(0 , 0 , 1 , 0 , borderColor)
@@ -109,11 +117,6 @@ open class McpPreviewEditor(
109
117
}
110
118
111
119
toolsWrapper.add(toolsScrollPane, BorderLayout .CENTER )
112
-
113
- // Add sample tools
114
- addTools()
115
-
116
- // Bottom panel for chatbot and input
117
120
val bottomPanel = JPanel (BorderLayout ()).apply {
118
121
background = UIUtil .getPanelBackground()
119
122
border = CompoundBorder (
@@ -198,20 +201,31 @@ open class McpPreviewEditor(
198
201
}
199
202
200
203
private fun addTools () {
201
- val tools = listOf (
202
- Tool (" database" , " Database Tools" , " Query and manage databases" ),
203
- Tool (" file" , " File Operations" , " Read, write, and manage files" ),
204
- Tool (" web" , " Web Tools" , " Fetch data from web sources" ),
205
- Tool (" ai" , " AI Tools" , " Image generation and text analysis" )
206
- )
204
+ toolsContainer.removeAll()
207
205
208
- for (tool in tools) {
209
- val toolCard = createToolCard(tool)
210
- toolsContainer.add(toolCard)
206
+ if (allTools.isEmpty()) {
207
+ val noToolsLabel = JBLabel (" No tools available. Please check MCP server configuration." ).apply {
208
+ font = JBUI .Fonts .label(14.0f )
209
+ foreground = textGray
210
+ }
211
+ toolsContainer.add(noToolsLabel)
212
+ toolsContainer.revalidate()
213
+ toolsContainer.repaint()
214
+ return
215
+ }
216
+
217
+ allTools.forEach { (serverName, tools) ->
218
+ tools.forEach { tool ->
219
+ val toolCard = createToolCard(serverName, tool)
220
+ toolsContainer.add(toolCard)
221
+ }
211
222
}
223
+
224
+ toolsContainer.revalidate()
225
+ toolsContainer.repaint()
212
226
}
213
227
214
- private fun createToolCard (tool : Tool ): JPanel {
228
+ private fun createToolCard (serverName : String , tool : Tool ): JPanel {
215
229
val card = JPanel (BorderLayout (0 , 8 )).apply {
216
230
background = UIUtil .getPanelBackground()
217
231
border = CompoundBorder (
@@ -252,7 +266,8 @@ open class McpPreviewEditor(
252
266
add(titleWrapper, BorderLayout .CENTER )
253
267
}
254
268
255
- val descLabel = JBLabel (tool.description).apply {
269
+ val descriptionText = tool.description ? : " No description available"
270
+ val descLabel = JBLabel (" $descriptionText (from $serverName )" ).apply {
256
271
font = JBUI .Fonts .label(14.0f )
257
272
foreground = textGray
258
273
}
@@ -268,7 +283,7 @@ open class McpPreviewEditor(
268
283
val detailsButton = JButton (" Details" ).apply {
269
284
font = JBUI .Fonts .label(14.0f )
270
285
isFocusPainted = false
271
- addActionListener { showToolDetails(tool) }
286
+ addActionListener { showToolDetails(serverName, tool) }
272
287
}
273
288
274
289
footerPanel.add(detailsButton, BorderLayout .CENTER )
@@ -279,7 +294,7 @@ open class McpPreviewEditor(
279
294
return card
280
295
}
281
296
282
- private fun showToolDetails (tool : Tool ) {
297
+ private fun showToolDetails (serverName : String , tool : Tool ) {
283
298
val dialog = object : DialogWrapper (project) {
284
299
init {
285
300
title = " Tool Details"
@@ -294,16 +309,34 @@ open class McpPreviewEditor(
294
309
}
295
310
}
296
311
row {
297
- label(tool.description ).applyToComponent {
312
+ label(" From server: $serverName " ).applyToComponent {
298
313
font = JBUI .Fonts .label(14.0f )
314
+ foreground = textGray
299
315
}
300
316
}
301
317
row {
302
- label(" This tool doesn't require any parameters. " ).applyToComponent {
318
+ label(tool.description ? : " No description available " ).applyToComponent {
303
319
font = JBUI .Fonts .label(14.0f )
304
- foreground = textGray
305
320
}
306
- }.topGap(TopGap .SMALL )
321
+ }
322
+
323
+ group(" Parameters" ) {
324
+ tool.inputSchema.properties?.forEach { param: Map .Entry <String , JsonElement > ->
325
+ row {
326
+ label(" ${param.key} " )
327
+ .applyToComponent {
328
+ font = JBUI .Fonts .label(14.0f )
329
+ }
330
+ }
331
+ row {
332
+ label(param.value.toString())
333
+ .applyToComponent {
334
+ font = JBUI .Fonts .label(12.0f )
335
+ foreground = textGray
336
+ }
337
+ }
338
+ }
339
+ }
307
340
}.withPreferredSize(400 , 200 )
308
341
}
309
342
@@ -315,7 +348,7 @@ open class McpPreviewEditor(
315
348
val executeButton = JButton (" Execute" ).apply {
316
349
font = JBUI .Fonts .label(14.0f )
317
350
addActionListener {
318
- println ( " Executing tool: ${tool.id} " )
351
+ executeTool(serverName, tool)
319
352
close(OK_EXIT_CODE )
320
353
}
321
354
}
@@ -328,6 +361,16 @@ open class McpPreviewEditor(
328
361
dialog.show()
329
362
}
330
363
364
+ private fun executeTool (serverName : String , tool : Tool ) {
365
+ val result = mcpServerManager.execute(project, tool, " {}" )
366
+ JOptionPane .showMessageDialog(
367
+ component,
368
+ result,
369
+ " Tool Execution Result" ,
370
+ JOptionPane .INFORMATION_MESSAGE
371
+ )
372
+ }
373
+
331
374
private fun showConfigDialog () {
332
375
val dialog = object : DialogWrapper (project) {
333
376
private lateinit var temperatureSlider: JSlider
@@ -388,23 +431,22 @@ open class McpPreviewEditor(
388
431
}
389
432
390
433
group(" Enabled Tools" ) {
391
- val toolNames = arrayOf(" Database Tools" , " File Operations" , " Web Tools" , " AI Tools" )
392
- val toolIds = arrayOf(" database" , " file" , " web" , " ai" )
393
-
394
- for (i in toolNames.indices) {
395
- val toolId = toolIds[i]
396
- row {
397
- label(toolNames[i])
398
- checkBox(" " ).apply {
399
- component.isSelected = config.enabledTools.contains(toolId)
400
- toolCheckboxes[toolId] = component
401
- component.addActionListener {
402
- if (component.isSelected) {
403
- if (! config.enabledTools.contains(toolId)) {
404
- config.enabledTools.add(toolId)
434
+ allTools.forEach { (serverName, tools) ->
435
+ tools.forEach { tool ->
436
+ val toolId = " ${serverName} :${tool.name} "
437
+ row {
438
+ label(" ${tool.name} (${serverName} )" )
439
+ checkBox(" " ).apply {
440
+ component.isSelected = config.enabledTools.contains(toolId)
441
+ toolCheckboxes[toolId] = component
442
+ component.addActionListener {
443
+ if (component.isSelected) {
444
+ if (! config.enabledTools.contains(toolId)) {
445
+ config.enabledTools.add(toolId)
446
+ }
447
+ } else {
448
+ config.enabledTools.remove(toolId)
405
449
}
406
- } else {
407
- config.enabledTools.remove(toolId)
408
450
}
409
451
}
410
452
}
0 commit comments