Skip to content

Commit 5b955c3

Browse files
committed
feat(mcp): enhance MCP tool execution and documentation #330
- Update `execute` method in `CustomMcpServerManager` to return JSON-encoded results. - Add `commandName` parameter to `ToolchainFunctionProvider` interface and implementations. - Add new documentation files for MCP client and server usage. - Remove redundant MCP usage instructions from `mcp.md`.
1 parent 4acdadd commit 5b955c3

File tree

18 files changed

+107
-48
lines changed

18 files changed

+107
-48
lines changed

core/src/main/kotlin/cc/unitmesh/devti/bridge/archview/ComponentViewFunctionProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import cc.unitmesh.devti.bridge.archview.model.UiComponent
66
import cc.unitmesh.devti.bridge.provider.ComponentViewMode
77
import cc.unitmesh.devti.provider.toolchain.ToolchainFunctionProvider
88
import com.intellij.openapi.project.Project
9+
import com.intellij.openapi.util.NlsSafe
910

1011
class ComponentViewFunctionProvider : ToolchainFunctionProvider {
1112
override fun funcNames(): List<String> = listOf(ArchViewCommand.ComponentView.name)
@@ -16,7 +17,8 @@ class ComponentViewFunctionProvider : ToolchainFunctionProvider {
1617
project: Project,
1718
prop: String,
1819
args: List<Any>,
19-
allVariables: Map<String, Any?>
20+
allVariables: Map<String, Any?>,
21+
commandName: @NlsSafe String
2022
): String {
2123
val uiComponents = ComponentViewProvider.collect(project, ComponentViewMode.DEFAULT)
2224
val transform = if (prop == "all") {

core/src/main/kotlin/cc/unitmesh/devti/bridge/archview/ContainerViewFunctionProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import cc.unitmesh.devti.bridge.ArchViewCommand
44
import cc.unitmesh.devti.provider.toolchain.ToolchainFunctionProvider
55
import com.intellij.openapi.module.ModuleManager
66
import com.intellij.openapi.project.Project
7+
import com.intellij.openapi.util.NlsSafe
78

89
class ContainerViewFunctionProvider : ToolchainFunctionProvider {
910
override fun isApplicable(project: Project, funcName: String) = funcName == ArchViewCommand.ContainerView.name
@@ -14,7 +15,8 @@ class ContainerViewFunctionProvider : ToolchainFunctionProvider {
1415
project: Project,
1516
prop: String,
1617
args: List<Any>,
17-
allVariables: Map<String, Any?>
18+
allVariables: Map<String, Any?>,
19+
commandName: @NlsSafe String
1820
): String {
1921
val modules = ModuleManager.getInstance(project).modules
2022
return "Here is current project modules:\n```\n" + modules.joinToString("\n") {

core/src/main/kotlin/cc/unitmesh/devti/bridge/assessment/SccFunctionProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import cc.unitmesh.devti.provider.toolchain.ToolchainFunctionProvider
77
import com.intellij.openapi.application.ApplicationManager
88
import com.intellij.openapi.project.Project
99
import com.intellij.openapi.project.guessProjectDir
10+
import com.intellij.openapi.util.NlsSafe
1011

1112
class SccFunctionProvider : ToolchainFunctionProvider {
1213
override fun isApplicable(project: Project, funcName: String): Boolean = funcName == Assessment.SCC.name
@@ -17,7 +18,8 @@ class SccFunctionProvider : ToolchainFunctionProvider {
1718
project: Project,
1819
prop: String,
1920
args: List<Any>,
20-
allVariables: Map<String, Any?>
21+
allVariables: Map<String, Any?>,
22+
commandName: @NlsSafe String
2123
): Any {
2224
val baseDir = project.guessProjectDir()!!.path
2325
val path = if (prop.isEmpty()) { baseDir } else "$baseDir/$prop"

core/src/main/kotlin/cc/unitmesh/devti/bridge/knowledge/HistoryFunctionProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import cc.unitmesh.devti.provider.RevisionProvider
55
import cc.unitmesh.devti.provider.toolchain.ToolchainFunctionProvider
66
import com.intellij.openapi.project.Project
77
import com.intellij.openapi.project.guessProjectDir
8+
import com.intellij.openapi.util.NlsSafe
89
import com.intellij.openapi.vfs.VirtualFile
910
import com.intellij.openapi.vfs.VirtualFileManager
1011

@@ -17,7 +18,8 @@ class HistoryFunctionProvider : ToolchainFunctionProvider {
1718
project: Project,
1819
prop: String,
1920
args: List<Any>,
20-
allVariables: Map<String, Any?>
21+
allVariables: Map<String, Any?>,
22+
commandName: @NlsSafe String
2123
): Any {
2224
val path = project.lookupFile(prop) ?: return "File not found"
2325
return RevisionProvider.provide()?.let {

core/src/main/kotlin/cc/unitmesh/devti/bridge/knowledge/KnowledgeFunctionProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import cc.unitmesh.devti.util.relativePath
99
import com.intellij.lang.LanguageCommenters
1010
import com.intellij.openapi.application.runReadAction
1111
import com.intellij.openapi.project.Project
12+
import com.intellij.openapi.util.NlsSafe
1213
import com.intellij.psi.PsiElement
1314
import com.intellij.psi.PsiManager
1415

@@ -53,7 +54,8 @@ class KnowledgeFunctionProvider : ToolchainFunctionProvider {
5354
project: Project,
5455
prop: String,
5556
args: List<Any>,
56-
allVariables: Map<String, Any?>
57+
allVariables: Map<String, Any?>,
58+
commandName: @NlsSafe String
5759
): Any {
5860
val split = prop.split("#")
5961
if (split.size != 2) {

core/src/main/kotlin/cc/unitmesh/devti/devin/dataprovider/BuiltinCommand.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ enum class BuiltinCommand(
149149
return TOOLCHAIN_COMMAND
150150
}
151151

152+
ToolchainFunctionProvider.all().forEach {
153+
if (it.funcNames().contains(commandName)) {
154+
return TOOLCHAIN_COMMAND
155+
}
156+
}
157+
152158
val project = ProjectManager.getInstance().openProjects.first()
153159
AutoDevNotifications.warn(project, "Command not found: $commandName")
154160
return null

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

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,19 @@ import cc.unitmesh.devti.settings.customize.customizeSetting
44
import com.intellij.openapi.components.Service
55
import com.intellij.openapi.diagnostic.logger
66
import com.intellij.openapi.project.Project
7-
import kotlinx.serialization.Serializable
8-
import kotlinx.serialization.json.Json
9-
import io.modelcontextprotocol.kotlin.sdk.client.Client
107
import io.modelcontextprotocol.kotlin.sdk.Implementation
118
import io.modelcontextprotocol.kotlin.sdk.Tool
9+
import io.modelcontextprotocol.kotlin.sdk.client.Client
1210
import io.modelcontextprotocol.kotlin.sdk.client.StdioClientTransport
1311
import kotlinx.io.asSink
1412
import kotlinx.io.asSource
1513
import kotlinx.io.buffered
16-
import kotlinx.serialization.json.JsonElement
14+
import kotlinx.serialization.Serializable
15+
import kotlinx.serialization.json.Json
1716
import kotlinx.serialization.json.JsonObject
17+
import kotlinx.serialization.encodeToString
1818
import kotlinx.serialization.json.jsonObject
1919
import java.util.concurrent.CompletableFuture
20-
import kotlin.text.get
2120

2221
@Serializable
2322
data class McpConfig(
@@ -123,9 +122,9 @@ class CustomMcpServerManager(val project: Project) {
123122
return tools
124123
}
125124

126-
fun execute(project: Project, tool: Tool, map: String): Any {
125+
fun execute(project: Project, tool: Tool, map: String): String {
127126
toolClientMap[tool]?.let {
128-
val future = CompletableFuture<Any>()
127+
val future = CompletableFuture<String>()
129128
kotlinx.coroutines.runBlocking {
130129
try {
131130
val arguments = try {
@@ -136,7 +135,11 @@ class CustomMcpServerManager(val project: Project) {
136135
}
137136

138137
val result = it.callTool(tool.name, arguments, true, null)
139-
future.complete(result)
138+
if (result?.content.isNullOrEmpty()) {
139+
future.complete("No result from tool ${tool.name}")
140+
} else {
141+
future.complete(Json.encodeToString(result.content))
142+
}
140143
} catch (e: Error) {
141144
logger<CustomMcpServerManager>().warn("Failed to execute tool ${tool.name}: $e")
142145
future.complete("Failed to execute tool ${tool.name}: $e")

core/src/main/kotlin/cc/unitmesh/devti/mcp/provider/McpFunctionProvider.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import cc.unitmesh.devti.mcp.CustomMcpServerManager
55
import cc.unitmesh.devti.provider.toolchain.ToolchainFunctionProvider
66
import com.intellij.openapi.project.Project
77
import com.intellij.openapi.project.ProjectManager
8+
import com.intellij.openapi.util.NlsSafe
89
import io.modelcontextprotocol.kotlin.sdk.Tool.Input
910
import kotlinx.serialization.json.Json
1011
import kotlinx.serialization.encodeToString
@@ -37,11 +38,12 @@ class McpFunctionProvider : ToolchainFunctionProvider {
3738
project: Project,
3839
prop: String,
3940
args: List<Any>,
40-
allVariables: Map<String, Any?>
41+
allVariables: Map<String, Any?>,
42+
commandName: @NlsSafe String
4143
): Any {
42-
val tool = CustomMcpServerManager.instance(project).collectServerInfos().firstOrNull { it.name == prop }
44+
val tool = CustomMcpServerManager.instance(project).collectServerInfos().firstOrNull { it.name == commandName }
4345
if (tool == null) {
44-
return "No such tool: $prop"
46+
return "No MCP such tool: $prop"
4547
}
4648

4749
return CustomMcpServerManager.instance(project).execute(project, tool, args.firstOrNull().toString())

core/src/main/kotlin/cc/unitmesh/devti/provider/toolchain/ToolchainFunctionProvider.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cc.unitmesh.devti.provider.toolchain
33
import cc.unitmesh.devti.agenttool.AgentTool
44
import com.intellij.openapi.extensions.ExtensionPointName
55
import com.intellij.openapi.project.Project
6+
import com.intellij.openapi.util.NlsSafe
67

78
interface ToolchainFunctionProvider {
89
fun toolInfos(): List<AgentTool> = emptyList()
@@ -11,7 +12,7 @@ interface ToolchainFunctionProvider {
1112

1213
fun isApplicable(project: Project, funcName: String): Boolean
1314

14-
fun execute(project: Project, prop: String, args: List<Any>, allVariables: Map<String, Any?>): Any
15+
fun execute(project: Project, prop: String, args: List<Any>, allVariables: Map<String, Any?>, commandName: String): Any
1516

1617
companion object {
1718
private val EP_NAME: ExtensionPointName<ToolchainFunctionProvider> =

docs/mcp/mcp-client.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
layout: default
3+
title: AutoDev as MCP Client
4+
nav_order: 2
5+
parent: MCP
6+
---
7+
8+
9+
## How to use
10+
11+
1. Enable the MCP server in AutoDev settings
12+
2. Use the MCP client to connect to the AutoDev server (We use JetBrains MCP Proxy Server to keep same protocol)
13+
14+
```json
15+
{
16+
"mcpServers": {
17+
"AutoDev": {
18+
"command": "npx",
19+
"args": [
20+
"-y",
21+
"@jetbrains/mcp-proxy"
22+
],
23+
"disabled": false,
24+
"autoApprove": []
25+
}
26+
}
27+
}
28+
```
29+
30+

docs/mcp/mcp-server.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
layout: default
3+
title: AutoDev as MCP Server
4+
nav_order: 3
5+
parent: MCP
6+
---
7+
8+
9+
The MCP will be convert to DevIns instruction like
10+
11+
/list_directory
12+
```json
13+
{
14+
"path": "/Users/phodal/Downloads"
15+
}
16+
```

docs/mcp/mcp.md

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,3 @@ This document will discuss the implementation details.
1919
Requirements: Install Node.js with Npx
2020

2121
- https://nodejs.org/en/download/package-manager
22-
23-
## How to use
24-
25-
1. Enable the MCP server in AutoDev settings
26-
2. Use the MCP client to connect to the AutoDev server (We use JetBrains MCP Proxy Server to keep same protocol)
27-
28-
```json
29-
{
30-
"mcpServers": {
31-
"AutoDev": {
32-
"command": "npx",
33-
"args": [
34-
"-y",
35-
"@jetbrains/mcp-proxy"
36-
],
37-
"disabled": false,
38-
"autoApprove": []
39-
}
40-
}
41-
}
42-
```
43-
44-
## Supported Action (Todo)
45-
46-

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/compiler/DevInsCompiler.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,14 @@ class DevInsCompiler(
298298
if (provider != null) {
299299
executeExtensionFunction(used, prop, provider)
300300
} else {
301-
PrintInsCommand("/" + commandNode.commandName + ":" + prop)
301+
var cmd = PrintInsCommand("/" + commandNode.commandName + ":" + prop)
302+
ToolchainFunctionProvider.all().forEach {
303+
if (it.funcNames().contains(originCmdName)) {
304+
cmd = executeExtensionFunction(used, prop, it)
305+
}
306+
}
307+
308+
cmd
302309
}
303310
} catch (e: Exception) {
304311
PrintInsCommand("/" + commandNode.commandName + ":" + prop)
@@ -351,7 +358,8 @@ class DevInsCompiler(
351358
val task = object : Task.Backgroundable(myProject, "Processing context", false) {
352359
override fun run(indicator: ProgressIndicator) {
353360
val result = try {
354-
provider.execute(myProject!!, prop, args, emptyMap())
361+
val cmd = runReadAction { used.text.removePrefix("/") }
362+
provider.execute(myProject!!, prop, args, emptyMap(), cmd)
355363
} catch (e: Exception) {
356364
logger<DevInsCompiler>().warn(e)
357365
val text = runReadAction { used.text }

exts/devins-lang/src/main/kotlin/cc/unitmesh/devti/language/compiler/exec/DatabaseInsCommand.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class DatabaseInsCommand(val myProject: Project, private val prop: String, priva
2525

2626
val result = try {
2727
ToolchainFunctionProvider.lookup("DatabaseFunctionProvider")
28-
?.execute(myProject, prop, args, emptyMap())
28+
?.execute(myProject, prop, args, emptyMap(), "")
2929
} catch (e: Exception) {
3030
AutoDevNotifications.notify(myProject, "Error: ${e.message}")
3131
return "Error: ${e.message}"

exts/ext-database/src/main/kotlin/cc/unitmesh/database/provider/DatabaseFunctionProvider.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.intellij.database.model.DasTable
1010
import com.intellij.database.model.RawDataSource
1111
import com.intellij.openapi.diagnostic.logger
1212
import com.intellij.openapi.project.Project
13+
import com.intellij.openapi.util.NlsSafe
1314

1415
class DatabaseFunctionProvider : ToolchainFunctionProvider {
1516
override fun toolInfos(): List<AgentTool> {
@@ -26,6 +27,7 @@ class DatabaseFunctionProvider : ToolchainFunctionProvider {
2627
prop: String,
2728
args: List<Any>,
2829
allVariables: Map<String, Any?>,
30+
commandName: @NlsSafe String,
2931
): Any {
3032
val databaseFunction = DatabaseFunction.fromString(prop)
3133
?: throw IllegalArgumentException("[Database]: Invalid Database function name")

exts/ext-dependencies/src/233/main/kotlin/cc/unitmesh/dependencies/DependenciesFunctionProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.intellij.openapi.progress.ProgressManager
99
import com.intellij.openapi.progress.Task
1010
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator
1111
import com.intellij.openapi.project.Project
12+
import com.intellij.openapi.util.NlsSafe
1213
import com.intellij.packageChecker.model.ProjectDependenciesModel
1314
import org.jetbrains.security.`package`.Package
1415
import java.util.concurrent.CompletableFuture
@@ -22,7 +23,8 @@ class DependenciesFunctionProvider : ToolchainFunctionProvider {
2223
project: Project,
2324
prop: String,
2425
args: List<Any>,
25-
allVariables: Map<String, Any?>
26+
allVariables: Map<String, Any?>,
27+
commandName: @NlsSafe String
2628
): Any {
2729
val modules = ModuleManager.getInstance(project).modules
2830
val future = CompletableFuture<String>()

exts/ext-endpoints/src/233/main/kotlin/cc/unitmesh/endpoints/bridge/WebApiViewFunctionProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.intellij.openapi.progress.ProgressManager
99
import com.intellij.openapi.progress.Task
1010
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator
1111
import com.intellij.openapi.project.Project
12+
import com.intellij.openapi.util.NlsSafe
1213
import com.intellij.spring.mvc.mapping.UrlMappingElement
1314
import java.util.concurrent.CompletableFuture
1415

@@ -21,7 +22,8 @@ class WebApiViewFunctionProvider : ToolchainFunctionProvider {
2122
project: Project,
2223
prop: String,
2324
args: List<Any>,
24-
allVariables: Map<String, Any?>
25+
allVariables: Map<String, Any?>,
26+
commandName: @NlsSafe String
2527
): Any {
2628
val future = CompletableFuture<String>()
2729
val task = object : Task.Backgroundable(project, "Processing context", false) {

javascript/src/main/kotlin/cc/unitmesh/ide/javascript/bridge/StylingViewFunctionProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import cc.unitmesh.devti.provider.toolchain.ToolchainFunctionProvider
66
import com.intellij.openapi.application.runReadAction
77
import com.intellij.openapi.fileTypes.ex.FileTypeManagerEx
88
import com.intellij.openapi.project.Project
9+
import com.intellij.openapi.util.NlsSafe
910
import com.intellij.openapi.vfs.VirtualFile
1011
import com.intellij.psi.PsiManager
1112
import com.intellij.psi.css.CssFileType
@@ -22,7 +23,8 @@ class StylingViewFunctionProvider : ToolchainFunctionProvider {
2223
project: Project,
2324
prop: String,
2425
args: List<Any>,
25-
allVariables: Map<String, Any?>
26+
allVariables: Map<String, Any?>,
27+
commandName: @NlsSafe String
2628
): Any {
2729
val searchScope: GlobalSearchScope = ProjectScope.getContentScope(project)
2830
val scssType = FileTypeManagerEx.getInstanceEx().getFileTypeByExtension("scss")

0 commit comments

Comments
 (0)