Skip to content

Commit 934c1db

Browse files
committed
feat(mcp): integrate with MCP server and enhance tool management #371
- Connect to MCP server to fetch available tools - Display tools from multiple servers - Add tool details and execution functionality - Update tool selection interface in settings - Refactor UI layout and improve user experience
1 parent efcaabb commit 934c1db

File tree

1 file changed

+93
-51
lines changed

1 file changed

+93
-51
lines changed

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

Lines changed: 93 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package cc.unitmesh.devti.mcp.editor
22

3+
import cc.unitmesh.devti.mcp.client.CustomMcpServerManager
34
import com.intellij.openapi.editor.Editor
45
import com.intellij.openapi.fileEditor.FileEditor
56
import com.intellij.openapi.fileEditor.FileEditorState
@@ -19,7 +20,12 @@ import com.intellij.ui.dsl.builder.TopGap
1920
import com.intellij.ui.dsl.builder.panel
2021
import com.intellij.util.ui.JBUI
2122
import com.intellij.util.ui.UIUtil
23+
import io.modelcontextprotocol.kotlin.sdk.Tool
24+
import kotlinx.coroutines.CoroutineScope
25+
import kotlinx.coroutines.Dispatchers
2226
import kotlinx.coroutines.flow.MutableStateFlow
27+
import kotlinx.coroutines.launch
28+
import kotlinx.serialization.json.JsonElement
2329
import java.awt.BorderLayout
2430
import java.awt.Dimension
2531
import java.awt.FlowLayout
@@ -44,14 +50,13 @@ open class McpPreviewEditor(
4450
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER
4551
)
4652

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>>()
5055

5156
data class ChatbotConfig(
5257
var temperature: Double = 0.7,
5358
var maxTokens: Int = 2000,
54-
var enabledTools: MutableList<String> = mutableListOf("database", "file", "web")
59+
var enabledTools: MutableList<String> = mutableListOf()
5560
)
5661

5762
private lateinit var toolsContainer: JPanel
@@ -66,6 +71,16 @@ open class McpPreviewEditor(
6671

6772
init {
6873
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+
}
6984
}
7085

7186
private fun createUI() {
@@ -75,18 +90,11 @@ open class McpPreviewEditor(
7590
fontColor = UIUtil.FontColor.BRIGHTER
7691
background = JBColor(0xF5F5F5, 0x2B2D30)
7792
font = JBUI.Fonts.label(18.0f).asBold()
78-
border = JBUI.Borders.empty(16)
93+
border = JBUI.Borders.empty(8)
7994
isOpaque = true
8095
}
8196

8297
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()
9098
}
9199
}.apply {
92100
border = BorderFactory.createMatteBorder(0, 0, 1, 0, borderColor)
@@ -109,11 +117,6 @@ open class McpPreviewEditor(
109117
}
110118

111119
toolsWrapper.add(toolsScrollPane, BorderLayout.CENTER)
112-
113-
// Add sample tools
114-
addTools()
115-
116-
// Bottom panel for chatbot and input
117120
val bottomPanel = JPanel(BorderLayout()).apply {
118121
background = UIUtil.getPanelBackground()
119122
border = CompoundBorder(
@@ -198,20 +201,31 @@ open class McpPreviewEditor(
198201
}
199202

200203
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()
207205

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+
}
211222
}
223+
224+
toolsContainer.revalidate()
225+
toolsContainer.repaint()
212226
}
213227

214-
private fun createToolCard(tool: Tool): JPanel {
228+
private fun createToolCard(serverName: String, tool: Tool): JPanel {
215229
val card = JPanel(BorderLayout(0, 8)).apply {
216230
background = UIUtil.getPanelBackground()
217231
border = CompoundBorder(
@@ -252,7 +266,8 @@ open class McpPreviewEditor(
252266
add(titleWrapper, BorderLayout.CENTER)
253267
}
254268

255-
val descLabel = JBLabel(tool.description).apply {
269+
val descriptionText = tool.description ?: "No description available"
270+
val descLabel = JBLabel("$descriptionText (from $serverName)").apply {
256271
font = JBUI.Fonts.label(14.0f)
257272
foreground = textGray
258273
}
@@ -268,7 +283,7 @@ open class McpPreviewEditor(
268283
val detailsButton = JButton("Details").apply {
269284
font = JBUI.Fonts.label(14.0f)
270285
isFocusPainted = false
271-
addActionListener { showToolDetails(tool) }
286+
addActionListener { showToolDetails(serverName, tool) }
272287
}
273288

274289
footerPanel.add(detailsButton, BorderLayout.CENTER)
@@ -279,7 +294,7 @@ open class McpPreviewEditor(
279294
return card
280295
}
281296

282-
private fun showToolDetails(tool: Tool) {
297+
private fun showToolDetails(serverName: String, tool: Tool) {
283298
val dialog = object : DialogWrapper(project) {
284299
init {
285300
title = "Tool Details"
@@ -294,16 +309,34 @@ open class McpPreviewEditor(
294309
}
295310
}
296311
row {
297-
label(tool.description).applyToComponent {
312+
label("From server: $serverName").applyToComponent {
298313
font = JBUI.Fonts.label(14.0f)
314+
foreground = textGray
299315
}
300316
}
301317
row {
302-
label("This tool doesn't require any parameters.").applyToComponent {
318+
label(tool.description ?: "No description available").applyToComponent {
303319
font = JBUI.Fonts.label(14.0f)
304-
foreground = textGray
305320
}
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+
}
307340
}.withPreferredSize(400, 200)
308341
}
309342

@@ -315,7 +348,7 @@ open class McpPreviewEditor(
315348
val executeButton = JButton("Execute").apply {
316349
font = JBUI.Fonts.label(14.0f)
317350
addActionListener {
318-
println("Executing tool: ${tool.id}")
351+
executeTool(serverName, tool)
319352
close(OK_EXIT_CODE)
320353
}
321354
}
@@ -328,6 +361,16 @@ open class McpPreviewEditor(
328361
dialog.show()
329362
}
330363

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+
331374
private fun showConfigDialog() {
332375
val dialog = object : DialogWrapper(project) {
333376
private lateinit var temperatureSlider: JSlider
@@ -388,23 +431,22 @@ open class McpPreviewEditor(
388431
}
389432

390433
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)
405449
}
406-
} else {
407-
config.enabledTools.remove(toolId)
408450
}
409451
}
410452
}

0 commit comments

Comments
 (0)