Skip to content

Commit 0559e74

Browse files
authored
fix: target AI Assistant MCP settings page correctly, improve fallback dialog (#2)
Navigate to Settings by Configurable id via the predicate overload instead of the display-name overload, which silently failed to preselect anything. Fill the JFrog platform URL from JFROG_PLATFORM_URL when set, and replace the notification balloon with a dialog showing the copied JSON and clear next steps, since the settings page cannot accept a remote MCP server.
1 parent 639e530 commit 0559e74

1 file changed

Lines changed: 102 additions & 16 deletions

File tree

src/main/kotlin/com/jfrog/jetbrains/actions/ConfigureJfrogMcpAction.kt

Lines changed: 102 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,129 @@
44

55
package com.jfrog.jetbrains.actions
66

7-
import com.intellij.notification.NotificationGroupManager
8-
import com.intellij.notification.NotificationType
97
import com.intellij.openapi.actionSystem.AnAction
108
import com.intellij.openapi.actionSystem.AnActionEvent
119
import com.intellij.openapi.ide.CopyPasteManager
10+
import com.intellij.openapi.options.Configurable
11+
import com.intellij.openapi.options.ConfigurableWithId
1212
import com.intellij.openapi.options.ShowSettingsUtil
13+
import com.intellij.openapi.ui.DialogWrapper
14+
import com.intellij.ui.components.JBScrollPane
15+
import com.intellij.ui.components.JBTextArea
16+
import com.intellij.util.ui.JBUI
17+
import java.awt.Dimension
1318
import java.awt.datatransfer.StringSelection
19+
import java.util.function.Consumer
20+
import java.util.function.Predicate
21+
import javax.swing.JComponent
22+
import javax.swing.JLabel
1423

1524
// Fallback path (see CONTRIBUTING.md "Known open risk"): if the mcpTool
1625
// extensions in plugin.xml turn out not to reach Junie's own tool-calling,
17-
// this copies the JFrog MCP server JSON to the clipboard and opens Settings
18-
// so the user can paste it into Settings | Tools | AI Assistant | MCP by
19-
// hand - the same one-time manual step every other JFrog IDE integration
20-
// already asks for around the platform URL.
26+
// this copies an MCP server entry to the clipboard and opens Settings so
27+
// the user can paste it into Settings | Tools | AI Assistant | Model
28+
// Context Protocol (MCP) by hand. That page (Configurable id "ml.llm.mcp",
29+
// confirmed by decompiling com.intellij.ml.llm.mcp.client.settings.
30+
// McpConfigurable.getId() in the bundled AI Assistant plugin's ml-llm.jar)
31+
// belongs to the AI Assistant/Junie plugin, not the separate
32+
// com.intellij.mcpServer plugin (Settings | Tools | MCP Server) that this
33+
// plugin's own mcpTool extensions register against - those are two
34+
// different, unrelated MCP integrations that happen to sit under the same
35+
// Tools settings group.
36+
//
37+
// We navigate with the (Project, Predicate, Consumer) overload rather than
38+
// the (Project, String) one, which matches by *display name*, not id
39+
// (confirmed by decompiling ShowSettingsUtilImpl.showSettingsDialog(Project,
40+
// String), which calls findPreselectedByDisplayName) - passing "ml.llm.mcp"
41+
// there never matched anything and silently fell back to the top of the
42+
// settings tree. The predicate overload throws IllegalStateException if
43+
// nothing matches instead of failing silently, so a missing AI Assistant
44+
// plugin is handled explicitly below rather than degrading to a no-op.
45+
//
46+
// JFrog's MCP server is remote (see github.com/jfrog/jfrog-mcp-server),
47+
// so its documented client config is a plain "url" entry - no local
48+
// binary. This settings page's server entries only support
49+
// command/args/env, though: confirmed by decompiling
50+
// mcp/client/settings/McpServerBean.class (no url field anywhere) and by
51+
// the literal string ml.llm.mcp.server.json.config.invalid = "Failed to
52+
// parse configuration" in the plugin's own McpBundle.properties. There is
53+
// no way to make this JSON paste-able on this page in this IDE build; it
54+
// is JFrog's real format, kept as-is for use in a client that does
55+
// support remote/url servers, and the dialog below says so explicitly
56+
// rather than sending the user looking for a paste option that isn't there.
2157
class ConfigureJfrogMcpAction : AnAction() {
2258
override fun actionPerformed(e: AnActionEvent) {
59+
val rawUrl = System.getenv("JFROG_PLATFORM_URL")?.trimEnd('/')
60+
val platformUrl = when {
61+
rawUrl == null -> "<JFROG_PLATFORM_URL>"
62+
rawUrl.startsWith("http://") || rawUrl.startsWith("https://") -> rawUrl
63+
else -> "https://$rawUrl"
64+
}
2365
val json = """
2466
{
2567
"mcpServers": {
2668
"jfrog": {
27-
"url": "https://${'$'}{JFROG_PLATFORM_URL}/mcp"
69+
"url": "$platformUrl/mcp"
2870
}
2971
}
3072
}
3173
""".trimIndent()
3274

3375
CopyPasteManager.getInstance().setContents(StringSelection(json))
3476

35-
NotificationGroupManager.getInstance()
36-
.getNotificationGroup("JFrog")
37-
.createNotification(
38-
"JFrog MCP server JSON copied to clipboard",
39-
"Paste it into Settings | Tools | AI Assistant | MCP, replacing \${JFROG_PLATFORM_URL} with your JFrog platform URL.",
40-
NotificationType.INFORMATION,
41-
)
42-
.notify(e.project)
77+
val instructions = if (rawUrl == null) {
78+
"Set the JFROG_PLATFORM_URL environment variable to your JFrog platform URL and rerun this action, or replace <JFROG_PLATFORM_URL> yourself before pasting."
79+
} else {
80+
"The URL was filled in from your JFROG_PLATFORM_URL environment variable."
81+
}
4382

44-
e.project?.let { ShowSettingsUtil.getInstance().showSettingsDialog(it) }
83+
JfrogMcpConfigDialog(json, instructions).show()
84+
85+
e.project?.let { project ->
86+
try {
87+
ShowSettingsUtil.getInstance().showSettingsDialog(
88+
project,
89+
Predicate<Configurable> { it is ConfigurableWithId && it.id == "ml.llm.mcp" },
90+
Consumer<Configurable> {},
91+
)
92+
} catch (_: IllegalStateException) {
93+
// AI Assistant plugin not installed - the config is already
94+
// on the clipboard and explained in the dialog above.
95+
}
96+
}
97+
}
98+
}
99+
100+
private class JfrogMcpConfigDialog(
101+
private val json: String,
102+
private val instructions: String,
103+
) : DialogWrapper(null, false) {
104+
init {
105+
title = "JFrog MCP Server Config Copied to Clipboard"
106+
setOKButtonText("Close")
107+
init()
108+
}
109+
110+
override fun createCenterPanel(): JComponent {
111+
val message = JLabel(
112+
"<html>This is JFrog's remote MCP server config. Settings | Tools | AI Assistant | " +
113+
"Model Context Protocol (MCP) only accepts local command-based servers, so pasting " +
114+
"it there via + | As JSON will not launch anything - use this JSON in an MCP client " +
115+
"that supports remote/url servers instead.<br><br>$instructions</html>",
116+
)
117+
val textArea = JBTextArea(json).apply {
118+
isEditable = false
119+
lineWrap = false
120+
}
121+
val scrollPane = JBScrollPane(textArea).apply {
122+
preferredSize = Dimension(520, 160)
123+
}
124+
125+
val panel = com.intellij.util.ui.FormBuilder.createFormBuilder()
126+
.addComponent(message)
127+
.addComponentFillVertically(scrollPane, JBUI.scale(8))
128+
.panel
129+
panel.preferredSize = Dimension(560, 320)
130+
return panel
45131
}
46132
}

0 commit comments

Comments
 (0)