MCP Integration in JetBrains Plugins – Recommended Approach & Future Support

Hi everyone,

I’m working on integrating MCP (Model Context Protocol) into an existing Couchbase plugin for JetBrains IDEs, and I wanted to ask what the recommended approach is for doing this cleanly within the JetBrains ecosystem.

Specifically:

* How should a plugin manage MCP server configuration and connections?

* Is there a standard or preferred way to expose MCP capabilities from a plugin?

* If multiple plugins need to work with MCP, is there any built-in mechanism for sharing configuration or should each AI plugin handle it independently?

I’m trying to design this in a way that aligns well with JetBrains plugin architecture and avoids reinventing patterns that may already exist.

Any guidance, best practices, or pointers to relevant APIs would be really helpful.

Also wanted to know if there are any plans for upcoming JetBrains versions to provide more explicit support for MCP integration at the plugin level?

Thanks!

You could integrate to the built-in MCP server MCP Server | IntelliJ IDEA Documentation

dependencies {
    ...
    bundledPlugin("com.intellij.mcpServer")
}

plugin.xml

<depends>com.intellij.mcpServer</depends>

Implement com.intellij.mcpserver.McpToolset

<mcpServer.mcpToolset implementation="com.intellij.mcpserver.toolsets.general.FileToolset" />

Example:

class FileToolset : McpToolset {
  @McpToolHints(readOnlyHint = TRUE, openWorldHint = FALSE)
  @McpTool
  @McpDescription("""
        |Provides a tree representation of the specified directory in the pseudo graphic format like `tree` utility does.
        |Use this tool to explore the contents of a directory or the whole project.
        |You MUST prefer this tool over listing directories via command line utilities like `ls` or `dir`.
    """)
  suspend fun list_directory_tree(
    @McpDescription(Constants.RELATIVE_PATH_IN_PROJECT_DESCRIPTION) directoryPath: String,
    @McpDescription("Maximum recursion depth") maxDepth: Int = 5,
    @McpDescription(Constants.TIMEOUT_MILLISECONDS_DESCRIPTION) timeout: Int = Constants.MEDIUM_TIMEOUT_MILLISECONDS_VALUE,
  ): DirectoryTreeInfo {
    val project = currentCoroutineContext().project
    val resolvedPath = project.resolveInProject(directoryPath)
    if (!resolvedPath.exists()) mcpFail("No such directory: $resolvedPath")
    if (!resolvedPath.isDirectory()) mcpFail("Not a directory: $resolvedPath")

    val result = StringBuilder()
    val errors = mutableListOf<String>()
    val timedOut = withTimeoutOrNull(timeout.milliseconds) { renderDirectoryTree(resolvedPath.toFile(), result, errors, maxDepth = maxDepth) } == null
    return DirectoryTreeInfo(directoryPath, result.toString(), errors, timedOut)
  }

  @Serializable
  class DirectoryTreeInfo(
    val traversedDirectory: String,
    val tree: String,
    val errors: List<String>,
    @property:McpDescription(Constants.TIMED_OUT_DESCRIPTION)
    @EncodeDefault(mode = EncodeDefault.Mode.NEVER)
    val listingTimedOut: Boolean? = false,
  )

You would need kotlinx-serialization applied in Gradle for @Serializable

Hi @yuriy.artamonov ! Thanks for your reply. We already have an MCP Server in python, is there a way to just automatically register/run it or do we need to reimplement the tools again in Java?

As far as I understand there is nothing to do if you already have it, you can plug it to AI of your choice directly

My understanding from the docs is that this is creating a new MCP Server from scratch, and I need to define all tools there instead of embedding an already existing MCP Server. In our case, this is a deal breaker, as I would require us to reimplement our Python MCP in Java. VsCode had some ways of providing an existing MCP Server via configs.

If we are talking about AI Assistant then it is just a setting