[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
This commit is contained in:
Artem.Bukhonov
2025-06-05 23:11:04 +00:00
committed by intellij-monorepo-bot
parent 0948645483
commit 58f847285d
3 changed files with 35 additions and 19 deletions
@@ -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<ListToolsRequest>(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))
}
@@ -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) {
@@ -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
}