Skip to content

Commit dd62eb2

Browse files
committed
feat(mcp): add cached tool management and lookup
Implement tool caching mechanism to improve performance: - Add findToolByName method to locate tools by server/name - Store Tool objects in McpConfigService for persistent access - Enhance tool selection tracking with object references - Update UI to maintain tool object mapping alongside checkbox state
1 parent a0e4256 commit dd62eb2

File tree

3 files changed

+74
-2
lines changed

3 files changed

+74
-2
lines changed

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cc.unitmesh.devti.mcp.client
22

33
import cc.unitmesh.devti.settings.customize.customizeSetting
4+
import cc.unitmesh.devti.mcp.ui.McpConfigService
45
import com.intellij.openapi.components.Service
56
import com.intellij.openapi.diagnostic.logger
67
import com.intellij.openapi.project.Project
@@ -122,6 +123,26 @@ class CustomMcpServerManager(val project: Project) {
122123
return "No such tool: ${tool.name} or failed to execute"
123124
}
124125

126+
/**
127+
* Find a Tool by server name and tool name
128+
* @param serverName The name of the MCP server
129+
* @param toolName The name of the tool
130+
* @return The Tool object if found, null otherwise
131+
*/
132+
suspend fun findToolByName(serverName: String, toolName: String): Tool? {
133+
// First try to get from cached tools
134+
val tools = cached[project.customizeSetting.mcpServerConfig]?.get(serverName) ?: emptyList()
135+
val tool = tools.find { it.name == toolName }
136+
if (tool != null) {
137+
return tool
138+
}
139+
140+
// If not found in cache, try to get from config service
141+
val configService = McpConfigService.getInstance(project)
142+
val selectedTools = configService.getSelectedToolObjects()
143+
return selectedTools[serverName]?.find { it.name == toolName }
144+
}
145+
125146
companion object {
126147
fun instance(project: Project): CustomMcpServerManager {
127148
return project.getService(CustomMcpServerManager::class.java)

core/src/main/kotlin/cc/unitmesh/devti/mcp/ui/McpConfigPopup.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.intellij.ui.components.JBLoadingPanel
77
import com.intellij.ui.components.JBScrollPane
88
import com.intellij.ui.components.JBTextField
99
import com.intellij.util.ui.JBUI
10+
import io.modelcontextprotocol.kotlin.sdk.Tool
1011
import kotlinx.coroutines.CoroutineScope
1112
import kotlinx.coroutines.Dispatchers
1213
import kotlinx.coroutines.launch
@@ -32,6 +33,7 @@ class McpConfigPopup {
3233
layout = BoxLayout(this, BoxLayout.Y_AXIS)
3334
}
3435
private val toolCheckboxMap = mutableMapOf<Pair<String, String>, JCheckBox>() // Stores serverName/toolName to JCheckBox
36+
private val toolObjectMap = mutableMapOf<Pair<String, String>, Tool>() // Maps serverName/toolName to Tool object
3537

3638
private fun createAndShow(component: JComponent?, project: Project, configService: McpConfigService) {
3739
val mainPanel = JPanel(BorderLayout()).apply {
@@ -121,6 +123,7 @@ class McpConfigPopup {
121123
) {
122124
toolsPanel.removeAll()
123125
toolCheckboxMap.clear()
126+
toolObjectMap.clear()
124127

125128
loadToolsIntoPanel(project, configService, loadingPanel)
126129
}
@@ -132,6 +135,7 @@ class McpConfigPopup {
132135
) {
133136
toolsPanel.removeAll() // Clear previous content
134137
toolCheckboxMap.clear()
138+
toolObjectMap.clear()
135139

136140
val loadingLabel = JLabel("Loading tools...")
137141
loadingLabel.alignmentX = Component.LEFT_ALIGNMENT
@@ -177,6 +181,7 @@ class McpConfigPopup {
177181
border = JBUI.Borders.emptyLeft(10)
178182
}
179183
toolCheckboxMap[Pair(serverName, tool.name)] = checkBox
184+
toolObjectMap[Pair(serverName, tool.name)] = tool
180185
toolsPanel.add(checkBox)
181186
}
182187
}
@@ -224,7 +229,14 @@ class McpConfigPopup {
224229
}
225230
}
226231

227-
configService.setSelectedTools(selectedTools)
232+
toolObjectMap.forEach { (key, tool) ->
233+
val (serverName, toolName) = key
234+
if (selectedTools[serverName]?.contains(toolName) == true) {
235+
configService.addSelectedTool(serverName, tool)
236+
}
237+
}
238+
239+
configService.setSelectedTools(selectedTools, false) // Don't clear cache as we just populated it
228240
}
229241

230242
private fun filterToolsList(searchText: String) {

core/src/main/kotlin/cc/unitmesh/devti/mcp/ui/McpConfigService.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import kotlinx.coroutines.withContext
1717
)
1818
class McpConfigService(private val project: Project) : PersistentStateComponent<McpConfigService.State> {
1919
private val selectedTools = mutableMapOf<String, MutableSet<String>>()
20+
private val cachedTools = mutableMapOf<Pair<String, String>, Tool>() // Cache of (serverName, toolName) -> Tool
2021
data class State(
2122
var toolSelections: Map<String, Set<String>> = emptyMap()
2223
)
@@ -41,12 +42,21 @@ class McpConfigService(private val project: Project) : PersistentStateComponent<
4142
fun addSelectedTool(serverName: String, toolName: String) {
4243
selectedTools.getOrPut(serverName) { mutableSetOf() }.add(toolName)
4344
}
45+
46+
/**
47+
* Add a selected tool and cache the Tool object
48+
*/
49+
fun addSelectedTool(serverName: String, tool: Tool) {
50+
addSelectedTool(serverName, tool.name)
51+
cachedTools[Pair(serverName, tool.name)] = tool
52+
}
4453

4554
fun removeSelectedTool(serverName: String, toolName: String) {
4655
selectedTools[serverName]?.remove(toolName)
4756
if (selectedTools[serverName]?.isEmpty() == true) {
4857
selectedTools.remove(serverName)
4958
}
59+
cachedTools.remove(Pair(serverName, toolName))
5060
}
5161

5262
fun isToolSelected(serverName: String, toolName: String): Boolean {
@@ -57,11 +67,33 @@ class McpConfigService(private val project: Project) : PersistentStateComponent<
5767
return selectedTools.mapValues { it.value.toSet() }
5868
}
5969

70+
/**
71+
* Gets the selected tools as actual Tool objects
72+
* If tools haven't been cached, it will attempt to retrieve them
73+
*/
74+
suspend fun getSelectedToolObjects(): Map<String, Set<Tool>> {
75+
val result = mutableMapOf<String, MutableSet<Tool>>()
76+
// First try to get from cache
77+
selectedTools.forEach { (serverName, toolNames) ->
78+
val toolsForServer = mutableSetOf<Tool>()
79+
toolNames.forEach { toolName ->
80+
cachedTools[Pair(serverName, toolName)]?.let {
81+
toolsForServer.add(it)
82+
}
83+
}
84+
if (toolsForServer.isNotEmpty()) {
85+
result[serverName] = toolsForServer
86+
}
87+
}
88+
return result.mapValues { it.value.toSet() }
89+
}
90+
6091
fun clearSelectedTools() {
6192
selectedTools.clear()
93+
cachedTools.clear()
6294
}
6395

64-
fun setSelectedTools(tools: Map<String, MutableSet<String>>) {
96+
fun setSelectedTools(tools: Map<String, MutableSet<String>>, clearCache: Boolean = true) {
6597
selectedTools.clear()
6698
selectedTools.putAll(tools)
6799
// Update Sketch systemPromptPanel when tools change
@@ -75,6 +107,9 @@ class McpConfigService(private val project: Project) : PersistentStateComponent<
75107
// }
76108
// }
77109
// }
110+
if (clearCache) {
111+
cachedTools.clear()
112+
}
78113
}
79114

80115
/**
@@ -96,6 +131,10 @@ class McpConfigService(private val project: Project) : PersistentStateComponent<
96131
try {
97132
val tools = mcpServerManager.collectServerInfo(serverName, serverConfig)
98133
allTools[serverName] = tools
134+
// Cache the tools for later use
135+
tools.forEach { tool ->
136+
cachedTools[Pair(serverName, tool.name)] = tool
137+
}
99138
} catch (e: Exception) {
100139
// Log error but continue with other servers
101140
allTools[serverName] = emptyList()

0 commit comments

Comments
 (0)