diff --git a/CHANGELOG.md b/CHANGELOG.md index 323dcf69..4b6493eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- Added Advanced tab in Tools → Options → Claude Code with Debug mode, Hang timeout, and a new "Enable MCP integration" toggle (disabling it skips the --mcp-config flag while hooks remain active) - Fixed process start errors and hang timeouts now logged to the IDE log file at SEVERE level - Added hang detection: configurable timeout (default 60 s) kills the process and shows an error if no PTY output is received after launch; set to 0 to disable - Fixed authentication for profiles with the "Claude API" connection type: the API key is now correctly recognised by Claude Code on session start diff --git a/docs/screenshots/settings-advanced.png b/docs/screenshots/settings-advanced.png new file mode 100644 index 00000000..59885b08 Binary files /dev/null and b/docs/screenshots/settings-advanced.png differ diff --git a/docs/screenshots/settings-favorites.png b/docs/screenshots/settings-favorites.png new file mode 100644 index 00000000..3395b3ef Binary files /dev/null and b/docs/screenshots/settings-favorites.png differ diff --git a/docs/screenshots/settings-general.png b/docs/screenshots/settings-general.png new file mode 100644 index 00000000..d786910d Binary files /dev/null and b/docs/screenshots/settings-general.png differ diff --git a/docs/user-manual.md b/docs/user-manual.md index 843978c5..b02ba6be 100644 --- a/docs/user-manual.md +++ b/docs/user-manual.md @@ -333,6 +333,8 @@ Open **Tools → Options → Claude Code** in NetBeans. ### General tab +![Settings — General tab](screenshots/settings-general.png) + | Setting | Default | Description | |---------|---------|-------------| | Claude CLI path | (empty) | Absolute path to the `claude` executable. Leave empty to use the system `PATH`. | @@ -341,14 +343,25 @@ Open **Tools → Options → Claude Code** in NetBeans. | History TTL (days) | 0 | Number of days after which history entries expire. 0 = keep forever. | | Send prompt key | Ctrl+Enter | Key combination that sends the prompt from the input area. | | Insert newline key | Enter | Key combination that inserts a newline in the input area. The send and newline keys are configured independently but cannot be set to the same value. | -| Debug mode | Off | Enables verbose logging of all Claude I/O to the NetBeans log file and the Output window. | | Open diff in a separate tab | Off | Opens the diff panel in a new IDE tab instead of embedding it in the session tab. | | Show markdown preview for .md files in diff | On | Shows a rendered markdown preview alongside the raw diff for `.md` files. | | Start new session when opening with Claude | Off (Continue last) | Checked → always start a **New session** when opening with the project context menu or when NetBeans restores a closed session tab on restart. Unchecked → **Continue last**. | | Session list limit | 30 | Maximum number of past sessions shown in the session selector table. | +### Advanced tab + +![Settings — Advanced tab](screenshots/settings-advanced.png) + +| Setting | Default | Description | +|---------|---------|-------------| +| Debug mode | Off | Enables verbose logging of all Claude I/O to the NetBeans log file and the Output window. | +| Hang timeout (seconds) | 60 | Kill the process if no PTY output is received within this many seconds after launch. Set to 0 to disable. | +| Enable MCP integration | On | When unchecked, the `--mcp-config` flag is not passed to `claude`. Hooks (PreToolUse) are always configured regardless of this setting. Disable only if MCP causes issues. | + ### Favorites tab +![Settings — Favorites tab](screenshots/settings-favorites.png) + Manages global favorites — prompts available in every project. The table shows all global favorites with columns: ☐ (checkbox for multi-select), **Text**, **Shortcut**. @@ -365,6 +378,10 @@ The table shows all global favorites with columns: ☐ (checkbox for multi-selec The search field filters entries by text. +### Profiles tab + +See [Section 12 — Profiles](#12-profiles) for full details on creating and managing profiles. + --- ## 12. Profiles @@ -651,14 +668,14 @@ As you move the cursor or change the selection in any open editor, the plugin au If Claude says a tool is unavailable (e.g. "I don't have access to `getWorkspaceFolders`"): 1. Check that no other application is using the MCP server port (**Tools → Options → Claude Code → General → MCP server port**, default: 28991). Change the port if needed and restart the IDE. -2. Enable **Debug mode** and check the log file (`~/.netbeans//var/log/messages.log`) for `MCP SSE server started on port`. +2. Enable **Debug mode** (**Tools → Options → Claude Code → Advanced**) and check the log file (`~/.netbeans//var/log/messages.log`) for `MCP SSE server started on port`. --- ## 15. Troubleshooting ### Enable debug mode -Go to **Tools → Options → Claude Code → General** and check **Debug mode**. This writes detailed logs of all Claude I/O (PTY bytes, MCP messages, hook calls) to the NetBeans log file. +Go to **Tools → Options → Claude Code → Advanced** and check **Debug mode**. This writes detailed logs of all Claude I/O (PTY bytes, MCP messages, hook calls) to the NetBeans log file. ### Log file location ``` diff --git a/pom.xml b/pom.xml index 574403db..84b47bf7 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.github.nbclaudecodegui netbeans-claude-code-gui - 0.21.34-SNAPSHOT + 0.21.36-SNAPSHOT nbm Netbeans Claude Code GUI NetBeans plugin that provides a GUI for Claude Code CLI — run claude code sessions directly inside the IDE. diff --git a/src/main/java/io/github/nbclaudecodegui/process/ClaudeProcess.java b/src/main/java/io/github/nbclaudecodegui/process/ClaudeProcess.java index 92c5f9c4..813dae4b 100644 --- a/src/main/java/io/github/nbclaudecodegui/process/ClaudeProcess.java +++ b/src/main/java/io/github/nbclaudecodegui/process/ClaudeProcess.java @@ -163,11 +163,15 @@ public PtyProcess start(String workingDir, ClaudeProfile profile, String extraCl + ", port=" + (mcp != null ? mcp.getServerPort() : -1)); if (mcp != null && mcp.isServerRunning()) { int port = mcp.getServerPort(); - // Pass MCP server config via --mcp-config (Claude 2.x no longer reads - // mcpServers from settings.local.json; --mcp-config works in TUI mode). - cmd.add("--mcp-config"); - cmd.add(buildMcpConfigJson(port)); - LOG.info("Passing --mcp-config with netbeans SSE server on port " + port); + if (io.github.nbclaudecodegui.settings.ClaudeCodePreferences.isMcpEnabled()) { + // Pass MCP server config via --mcp-config (Claude 2.x no longer reads + // mcpServers from settings.local.json; --mcp-config works in TUI mode). + cmd.add("--mcp-config"); + cmd.add(buildMcpConfigJson(port)); + LOG.info("Passing --mcp-config with netbeans SSE server on port " + port); + } else { + LOG.info("MCP integration disabled by user preference; skipping --mcp-config"); + } try { writeSettingsLocalJson(workingDir, port, profile); } catch (IOException e) { diff --git a/src/main/java/io/github/nbclaudecodegui/settings/ClaudeCodeOptionsPanel.java b/src/main/java/io/github/nbclaudecodegui/settings/ClaudeCodeOptionsPanel.java index f9ed6149..86d62941 100644 --- a/src/main/java/io/github/nbclaudecodegui/settings/ClaudeCodeOptionsPanel.java +++ b/src/main/java/io/github/nbclaudecodegui/settings/ClaudeCodeOptionsPanel.java @@ -70,6 +70,8 @@ public final class ClaudeCodeOptionsPanel extends JPanel { private JSpinner sessionListLimitSpinner; /** Spinner for the hang-detection timeout in seconds (0 = disabled). */ private JSpinner hangTimeoutSpinner; + /** Checkbox to enable MCP integration (pass --mcp-config flag). */ + private javax.swing.JCheckBox mcpEnabledCheckBox; /** send-key radio buttons: value → button */ private final Map sendRadios = new LinkedHashMap<>(); @@ -99,6 +101,7 @@ private void initComponents() { JTabbedPane tabs = new JTabbedPane(); tabs.addTab("General", buildGeneralPanel()); + tabs.addTab("Advanced", buildAdvancedPanel()); tabs.addTab("Favorites", favoritesPanel); tabs.addTab("Profiles", profilesPanel); add(tabs, BorderLayout.CENTER); @@ -181,16 +184,6 @@ private JPanel buildGeneralPanel() { newlineRadios.get(val).addActionListener(e -> syncExclusion(val, false)); } - // --- debug mode --- - debugCheckBox = new javax.swing.JCheckBox("Debug mode (log all claude I/O to NetBeans log and output area)"); - GridBagConstraints dbgGbc = new GridBagConstraints(); - dbgGbc.gridx = 0; dbgGbc.gridy = row; - dbgGbc.gridwidth = 3; - dbgGbc.anchor = GridBagConstraints.WEST; - dbgGbc.insets = new Insets(4, 8, 4, 8); - form.add(debugCheckBox, dbgGbc); - row++; - // --- diff in session --- diffInSessionCheck = new javax.swing.JCheckBox("Open diff in a separate tab"); GridBagConstraints diffGbc = new GridBagConstraints(); @@ -229,18 +222,68 @@ private JPanel buildGeneralPanel() { form.add(sessionListLimitSpinner, gbc(1, row, false)); row++; + // spacer + GridBagConstraints spacer = new GridBagConstraints(); + spacer.gridx = 0; spacer.gridy = row; + spacer.gridwidth = 3; spacer.weighty = 1.0; + spacer.fill = GridBagConstraints.VERTICAL; + form.add(new JPanel(), spacer); + + return form; + } + + /** + * Builds the Advanced tab containing debug and hang-timeout settings. + * + * @return the Advanced tab panel + */ + private JPanel buildAdvancedPanel() { + JPanel form = new JPanel(new GridBagLayout()); + int row = 0; + + // --- debug mode --- + debugCheckBox = new javax.swing.JCheckBox("Debug mode (log all claude I/O to NetBeans log and output area)"); + GridBagConstraints dbgGbc = new GridBagConstraints(); + dbgGbc.gridx = 0; dbgGbc.gridy = row; + dbgGbc.gridwidth = 2; + dbgGbc.anchor = GridBagConstraints.WEST; + dbgGbc.insets = new Insets(8, 8, 4, 8); + form.add(debugCheckBox, dbgGbc); + row++; + // --- hang timeout --- form.add(new JLabel("Hang timeout (seconds, 0=disabled):"), gbc(0, row, false)); hangTimeoutSpinner = new JSpinner(new SpinnerNumberModel( ClaudeCodePreferences.DEFAULT_HANG_TIMEOUT_SECONDS, 0, 3600, 5)); hangTimeoutSpinner.setToolTipText("Kill the process if no PTY output is received within this many seconds after launch (0 = disabled)"); - form.add(hangTimeoutSpinner, gbc(1, row, false)); + GridBagConstraints hangSpinnerGbc = gbc(1, row, false); + hangSpinnerGbc.weightx = 1.0; + form.add(hangTimeoutSpinner, hangSpinnerGbc); + row++; + + // --- MCP enabled --- + mcpEnabledCheckBox = new javax.swing.JCheckBox("Enable MCP integration"); + GridBagConstraints mcpGbc = new GridBagConstraints(); + mcpGbc.gridx = 0; mcpGbc.gridy = row; + mcpGbc.gridwidth = 2; + mcpGbc.anchor = GridBagConstraints.WEST; + mcpGbc.insets = new Insets(12, 8, 0, 8); + form.add(mcpEnabledCheckBox, mcpGbc); + row++; + + JLabel mcpDesc = new JLabel("When disabled, the --mcp-config flag is not passed to claude. Hooks are always configured."); + GridBagConstraints mcpDescGbc = new GridBagConstraints(); + mcpDescGbc.gridx = 0; mcpDescGbc.gridy = row; + mcpDescGbc.gridwidth = 2; + mcpDescGbc.anchor = GridBagConstraints.WEST; + mcpDescGbc.insets = new Insets(0, 28, 4, 8); + form.add(mcpDesc, mcpDescGbc); row++; // spacer GridBagConstraints spacer = new GridBagConstraints(); spacer.gridx = 0; spacer.gridy = row; - spacer.gridwidth = 3; spacer.weighty = 1.0; + spacer.gridwidth = 2; spacer.weighty = 1.0; spacer.fill = GridBagConstraints.VERTICAL; form.add(new JPanel(), spacer); @@ -315,6 +358,7 @@ void load() { == io.github.nbclaudecodegui.model.SessionMode.NEW); sessionListLimitSpinner.setValue(ClaudeCodePreferences.getSessionListLimit()); hangTimeoutSpinner.setValue(ClaudeCodePreferences.getHangTimeoutSeconds()); + mcpEnabledCheckBox.setSelected(ClaudeCodePreferences.isMcpEnabled()); String sendVal = ClaudeCodePreferences.getSendKey(); String newlineVal = ClaudeCodePreferences.getNewlineKey(); @@ -348,6 +392,7 @@ void store() { : io.github.nbclaudecodegui.model.SessionMode.CONTINUE_LAST); ClaudeCodePreferences.setSessionListLimit((Integer) sessionListLimitSpinner.getValue()); ClaudeCodePreferences.setHangTimeoutSeconds((Integer) hangTimeoutSpinner.getValue()); + ClaudeCodePreferences.setMcpEnabled(mcpEnabledCheckBox.isSelected()); ClaudeCodePreferences.setSendKey(selectedValue(sendRadios)); ClaudeCodePreferences.setNewlineKey(selectedValue(newlineRadios)); diff --git a/src/main/java/io/github/nbclaudecodegui/settings/ClaudeCodePreferences.java b/src/main/java/io/github/nbclaudecodegui/settings/ClaudeCodePreferences.java index 01d9b9d0..80f01caf 100644 --- a/src/main/java/io/github/nbclaudecodegui/settings/ClaudeCodePreferences.java +++ b/src/main/java/io/github/nbclaudecodegui/settings/ClaudeCodePreferences.java @@ -525,6 +525,32 @@ public static void setHangTimeoutSeconds(int v) { .putInt(KEY_HANG_TIMEOUT_SECONDS, Math.max(0, Math.min(3600, v))); } + // ------------------------------------------------------------------------- + // mcpEnabled + // ------------------------------------------------------------------------- + + /** Preference key: pass {@code --mcp-config} flag to claude CLI. */ + private static final String MCP_ENABLED = "mcpEnabled"; + + /** + * Returns whether MCP integration is enabled (default {@code true}). + * + * @return {@code true} to pass {@code --mcp-config}; {@code false} to skip it + */ + public static boolean isMcpEnabled() { + return NbPreferences.forModule(ClaudeCodePreferences.class) + .getBoolean(MCP_ENABLED, true); + } + + /** + * Persists the MCP-enabled flag. + * + * @param v {@code true} to enable MCP integration + */ + public static void setMcpEnabled(boolean v) { + NbPreferences.forModule(ClaudeCodePreferences.class).putBoolean(MCP_ENABLED, v); + } + private static String validated(String value, String fallback) { return ENTER.equals(value) || SHIFT_ENTER.equals(value) || CTRL_ENTER.equals(value) || ALT_ENTER.equals(value)