From 58f847285d022f02403dea1533400e724d44e094 Mon Sep 17 00:00:00 2001 From: "Artem.Bukhonov" Date: Thu, 5 Jun 2025 23:10:37 +0200 Subject: [PATCH] [MCP Server] Pass project path through env var and headers to MCP server to identify a project on request (cherry picked from commit 90ee5826e3d8d32b912da30b03e6bea776c57a90) GitOrigin-RevId: d6dd878af851fe4b9ce1f82e39e6481a9e0b53a8 --- .../mcpserver.stdio/src/McpStdioRunner.kt | 31 ++++++++++--------- .../com/intellij/mcpserver/MCPService.kt | 12 +++++-- .../com/intellij/mcpserver/stdioRunnerUtil.kt | 11 +++++-- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/plugins/mcp-server/mcpserver.stdio/src/McpStdioRunner.kt b/plugins/mcp-server/mcpserver.stdio/src/McpStdioRunner.kt index 19b9a2c38858..46f709b8a533 100644 --- a/plugins/mcp-server/mcpserver.stdio/src/McpStdioRunner.kt +++ b/plugins/mcp-server/mcpserver.stdio/src/McpStdioRunner.kt @@ -1,15 +1,6 @@ package com.intellij.mcpserver.stdio -import com.intellij.mcpserver.stdio.mcpProto.CallToolRequest -import com.intellij.mcpserver.stdio.mcpProto.CallToolResult -import com.intellij.mcpserver.stdio.mcpProto.Implementation -import com.intellij.mcpserver.stdio.mcpProto.ListToolsRequest -import com.intellij.mcpserver.stdio.mcpProto.ListToolsResult -import com.intellij.mcpserver.stdio.mcpProto.Method -import com.intellij.mcpserver.stdio.mcpProto.Response -import com.intellij.mcpserver.stdio.mcpProto.ServerCapabilities -import com.intellij.mcpserver.stdio.mcpProto.TextContent -import com.intellij.mcpserver.stdio.mcpProto.Tool +import com.intellij.mcpserver.stdio.mcpProto.* import com.intellij.mcpserver.stdio.mcpProto.server.Server import com.intellij.mcpserver.stdio.mcpProto.server.ServerOptions import com.intellij.mcpserver.stdio.mcpProto.server.StdioServerTransport @@ -28,9 +19,10 @@ import kotlinx.coroutines.CompletableDeferred import kotlinx.io.asSink import kotlinx.io.buffered -const val IJ_MCP_SERVER_PORT: String = "IJ_MCP_SERVER_PORT" -const val IJ_MCP_SERVER_NAME: String = "IJ_MCP_SERVER_NAME" -const val IJ_MCP_SERVER_VERSION: String = "IJ_MCP_SERVER_VERSION" +val IJ_MCP_SERVER_PORT: String = ::IJ_MCP_SERVER_PORT.name +val IJ_MCP_SERVER_NAME: String = ::IJ_MCP_SERVER_NAME.name +val IJ_MCP_SERVER_VERSION: String = ::IJ_MCP_SERVER_VERSION.name +val IJ_MCP_SERVER_PROJECT_PATH: String = ::IJ_MCP_SERVER_PROJECT_PATH.name suspend fun main() { val inputStream = System.`in` @@ -56,7 +48,14 @@ suspend fun main() { val stdioServerTransport = StdioServerTransport(inputStream.asInput(), outputStream.asSink().buffered()) server.setRequestHandler(Method.Defined.ToolsList) { request, extra -> - val response = httpClient.get(urlString = endpoint + "list_tools") + val response = httpClient.get(urlString = endpoint + "list_tools") { + headers { + val projectPath = System.getenv(IJ_MCP_SERVER_PROJECT_PATH) + if (!projectPath.isNullOrBlank()) { + this.append(IJ_MCP_SERVER_PROJECT_PATH, projectPath) + } + } + } when (response.status) { HttpStatusCode.OK -> { val responseText = response.bodyAsText() @@ -74,6 +73,10 @@ suspend fun main() { val response = httpClient.post(urlString = endpoint + request.name) { headers { contentType(ContentType.Application.Json) + val projectPath = System.getenv(IJ_MCP_SERVER_PROJECT_PATH) + if (!projectPath.isNullOrBlank()) { + this.append(IJ_MCP_SERVER_PROJECT_PATH, projectPath) + } } setBody(McpJson.encodeToString(request.arguments)) } diff --git a/plugins/mcp-server/src/main/kotlin/com/intellij/mcpserver/MCPService.kt b/plugins/mcp-server/src/main/kotlin/com/intellij/mcpserver/MCPService.kt index e5c60d21762e..018bc469a817 100644 --- a/plugins/mcp-server/src/main/kotlin/com/intellij/mcpserver/MCPService.kt +++ b/plugins/mcp-server/src/main/kotlin/com/intellij/mcpserver/MCPService.kt @@ -1,10 +1,11 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.mcpserver +import com.intellij.mcpserver.stdio.IJ_MCP_SERVER_PROJECT_PATH import com.intellij.openapi.components.Service import com.intellij.openapi.diagnostic.logger -import com.intellij.openapi.progress.runBlockingCancellable import com.intellij.openapi.project.Project +import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream import io.netty.channel.ChannelHandlerContext import io.netty.handler.codec.http.FullHttpRequest @@ -39,7 +40,14 @@ class MCPService : RestService() { override fun execute(urlDecoder: QueryStringDecoder, request: FullHttpRequest, context: ChannelHandlerContext): String? { val path = urlDecoder.path().split(serviceName).last().trimStart('/') - val project = getLastFocusedOrOpenedProject() ?: return null + val projectPath = request.headers().get(IJ_MCP_SERVER_PROJECT_PATH) + val project = if (!projectPath.isNullOrBlank()) { + ProjectManager.getInstance().openProjects.find { it.basePath == projectPath } ?: getLastFocusedOrOpenedProject() + } + else { + getLastFocusedOrOpenedProject() + } ?: return null + val tools = McpToolManager.getAllTools() val result = when (path) { diff --git a/plugins/mcp-server/src/main/kotlin/com/intellij/mcpserver/stdioRunnerUtil.kt b/plugins/mcp-server/src/main/kotlin/com/intellij/mcpserver/stdioRunnerUtil.kt index d0019c4c2ef4..288275fbf8b6 100644 --- a/plugins/mcp-server/src/main/kotlin/com/intellij/mcpserver/stdioRunnerUtil.kt +++ b/plugins/mcp-server/src/main/kotlin/com/intellij/mcpserver/stdioRunnerUtil.kt @@ -3,7 +3,7 @@ package com.intellij.mcpserver import com.intellij.execution.configurations.GeneralCommandLine -import com.intellij.mcpserver.stdio.main +import com.intellij.mcpserver.stdio.* import com.intellij.openapi.application.PathManager import java.io.File import kotlin.io.path.pathString @@ -12,13 +12,18 @@ import kotlin.reflect.jvm.javaMethod /** * Build a commandline to run MCP IDE server in a separate process */ -fun createStdioMcpServerCommandLine(): GeneralCommandLine { +fun createStdioMcpServerCommandLine(ideServerPort: Int, serverName: String?, serverVersion: String?, projectBasePath: String?): GeneralCommandLine { val classpaths = McpStdioRunnerClasspath.CLASSPATH_CLASSES.map { (PathManager.getJarForClass(it) ?: error("No path for class $it")).pathString }.toSet() - return GeneralCommandLine() + val commandLine = GeneralCommandLine() .withExePath("${System.getProperty("java.home")}${File.separator}bin${File.separator}java") .withParameters("-classpath", classpaths.joinToString(File.pathSeparator)) .withParameters(::main.javaMethod!!.declaringClass.name) + .withEnvironment(IJ_MCP_SERVER_PORT, ideServerPort.toString()) + if (serverName != null) commandLine.withEnvironment(IJ_MCP_SERVER_NAME, serverName) + if (serverVersion != null) commandLine.withEnvironment(IJ_MCP_SERVER_VERSION, serverVersion) + if (projectBasePath != null) commandLine.withEnvironment(IJ_MCP_SERVER_PROJECT_PATH, projectBasePath) + return commandLine } \ No newline at end of file