跪拜 Guibai
← Back to the summary

A Claude Code GUI for IntelliJ IDEA That Actually Feels Native

Hello everyone, I'm Brother Er.

Previously, a reader recommended an IntelliJ IDEA plugin called CC GUI in the comments.

You can use Claude Code smoothly in IDEA.

Just when DeepSeek V4 pro had a big price cut, I thought, could I configure the DeepSeek V4 API key in CC GUI and use it smoothly?

75% off, indeed much cheaper than a 0% discount 😄

From the content I've posted before, there are quite a lot of people using IDEA, especially backend developers, who can't live without IntelliJ IDEA. It's convenient for viewing code and debugging.

So today let's just practice it, a hands-on guide to experience it.

Plugin address: https://plugins.jetbrains.com/plugin/29342-cc-gui-claude-or-codex-

It supports many features, such as image parsing, Skill commands, MCP servers, etc.

01. How to Install and Configure?

Installation is straightforward.

In IDEA, press Cmd+Shift+A to bring up the search, type Plugins to enter the plugin marketplace, and search for "CC GUI". I've already installed it here.

After installation, you don't even need to restart the IDE. A "CC GUI" icon appears in the right sidebar; click it to open the operation panel.

When you open it for the first time, a prompt will appear asking you to install the Claude Code SDK first. This is the underlying dependency for the Agent to run. Follow the guide, and it takes about half a minute.

After the SDK is in place, the next step is to configure the model. Open the provider settings interface:

It supports directly reading the local existing settings.json to complete authentication.

In other words, if your Claude Code is already running, just point the configuration path to it. Using domestic Coding Plans (like GLM-5.1, Kimi 2.6, etc.) is fine, no need to log into an Anthropic account.

Besides settings.json, it also supports adding providers.

Click [+ Add], select DeepSeek.

Scroll down, fill in deepseek-v4-pro for Sonnet and Opus, and deepseek-v4-flash for Haiku.

Then click [Confirm Add] and it's done.

Essentially, it helps you rewrite the settings.json file.

After configuration, you can see the token consumption details in the statistics panel, showing how much was spent per conversation round and the cumulative total, clear at a glance.

MCP can mount various external tools to the Agent.

First, I recommend the Chrome Devtools MCP. Once mounted, the Agent can directly control the browser for automated testing and debugging of front-end pages. Especially for bugs that require login to reproduce, or issues that need a specific browser environment to trigger, it's much more convenient.

There's also an idea MCP, which allows Claude Code to connect with IntelliJ IDEA itself.

Additionally, a git commit-related MCP is worth installing; it automatically writes commit messages for you, saving effort.

I previously used the built-in Copilot in GitHub Desktop for this feature.

Next, let's talk about Skills. This can be understood as capability extension packs for Claude Code. Installing different Skills unlocks specialized capabilities in corresponding domains.

I personally think two are must-haves: web-access and frontend-design.

The role of web-access is to open a browser channel. It's particularly handy for scenarios requiring access to pages with login credentials or simulating real user behavior.

frontend-design is a front-end page generator. Throw a requirement description at it, and it outputs HTML/CSS. The overall design sense is better than I expected, at least much faster than writing from scratch.

CC GUI has accumulated 3.1k stars on GitHub, with a very active community. The author iterates quickly, pushing new versions almost every week, fixing bugs and adding features promptly.

https://github.com/zhukunpenglinyutong/idea-claude-code-gui/blob/main/README.zh-CN.md

02. Practical Case 1: Article Read Count Feature Optimization

With the tool installed, let's get to work. The first scenario uses real business logic from the Tech-Pie project.

Tech-Pie is an open-source community we built ourselves.

Tutorials for the PaiAgent open-source project will also be synchronized there.

The current read count update logic is written in ArticleReadService.java. Every time a reader views an article, it directly executes an SQL statement to the database. I plan to replace it with a Redis caching strategy, accumulating counts in Redis first and then periodically syncing them back to the database. This change will span multiple files, including Service, Controller, and scheduled tasks.

In the CC GUI dialog box, I entered: "Help me optimize the article read count feature for the Tech-Pie project, changing it to a Redis caching solution. Accumulate Redis counts first, then sync to the database periodically."

The first thing the Agent did was review the existing code structure and check project dependencies, confirming that Redis-related configurations were already in place.

It located the bottleneck and then provided a complete refactoring plan.

First, it modified CountServiceImpl.incrArticleReadCount(), removing the direct database code and leaving only the Redis count increment logic:

Immediately after, it added a scheduled sync task to flush the read counts from Redis to the database every 5 minutes.

The third step was the batch update logic.

The final step was adding unit tests.

The plan was sound, so I clicked confirm to let the Agent execute it.

Throughout the entire process, I only needed to make decisions at key points. As for how to write the specific code, how to create files, and how to connect methods, the Agent handled it all.

Looking closely at the generated Redis caching solution, I think several details were handled quite well:

First, it used Redis's incr for atomic increments, preventing count confusion under high concurrency.

@Override
public void incrArticleReadCount(Long authorUserId, Long articleId) {
    // Removed: articleDao.incrReadCount(articleId);
    // Only update Redis counter
    RedisClient.pipelineAction()
            .add(CountConstants.ARTICLE_STATISTIC_INFO + articleId, CountConstants.READ_COUNT,
                    (connection, key, value) -> connection.hIncrBy(key, value, 1))
            .add(CountConstants.USER_STATISTIC_INFO + authorUserId, CountConstants.READ_COUNT,
                    (connection, key, value) -> connection.hIncrBy(key, value, 1))
            .execute();
}

Second, it set an expiration time for the keys, so counts for unpopular articles won't linger in memory forever. Furthermore, the scheduled sync used a batch mode, flushing multiple records at once, reducing database connection overhead.

/**
 * Execute every 5 minutes, sync article read counts from Redis to the database
 */
@Scheduled(cron = "0 */5 * * * ?")
public void syncArticleReadCountToDb() {
    Long start = System.currentTimeMillis();
    log.info("Start syncing article read counts to database");

    // Scan all article statistics keys
    Set<String> keys = scanKeys(CountConstants.ARTICLE_STATISTIC_INFO + "*");

    int batchSize = 100;
    int synced = 0;

    for (String key : keys) {
        try {
            // Extract articleId
            Long articleId = Long.parseLong(key.replace(CountConstants.ARTICLE_STATISTIC_INFO, ""));

            // Get read count from Redis
            Integer readCount = RedisClient.hGet(key, CountConstants.READ_COUNT, Integer.class);
            if (readCount != null && readCount > 0) {
                // Batch update database
                batchUpdateReadCount(articleId, readCount);
                synced++;

                if (synced % batchSize == 0) {
                    log.info("Synced {} articles' read counts", synced);
                }
            }
        } catch (Exception e) {
            log.error("Failed to sync read count, key: {}", key, e);
        }
    }

    log.info("Finished syncing article read counts, total synced: {} articles, time taken: {}ms", synced, System.currentTimeMillis() - start);
}

The Agent thought very comprehensively for me.

After the changes, I let it run verification directly, going through the test process in the browser via the Chrome Devtools MCP.

03. Practical Case 2: Comment System Pagination Query Refactoring

The second case also comes from Tech-Pie, this time tackling the pagination query of the comment system.

The effect after the change is similar to the comment section of WeChat Official Accounts, which I think is quite good.

If there's more than 1 reply, there's an expand function.

After expanding, it also supports markdown. I think the user experience is quite good, 😄

The previous approach in CommentReadService.java was a one-shot deal—when querying article comments, it fetched all top-level comments and their replies in one go. It's fine when the data volume is small, but once there are many comments, it wastes network bandwidth, especially since I added underline comments, and the LLM reply content is substantial.

If a popular article has 100 comments, each with 10 replies underneath, fetching 1000 records at once makes the frontend rendering very laggy.

My idea was to implement lazy loading: when the page opens, only show top-level comments first. When the user manually clicks "View Replies", it asynchronously fetches the secondary content. This improves the first-screen speed and makes the experience smoother.

This change requires modifications on both the frontend and backend.

The backend needs to split the query logic and create a new API endpoint specifically for fetching reply lists; the frontend needs to add interaction events and loading transition animations. In the past, I'd have to coordinate the API format back and forth between the two sides, taking a lot of time.

Now, I just said directly in CC GUI: "Help me refactor the comment system of the Tech-Pie project, separating the loading of top-level comments and secondary replies. Query top-level comments with pagination first, and asynchronously load replies when clicking expand."

The Agent first scanned the relevant code.

After scanning, it provided a complete refactoring blueprint:

Watching the Agent modify backend code in IDEA has a natural advantage: you can follow along wherever it changes and review in time.

This is something terminal or web-based Agents cannot do.

The specific execution steps are as follows:

  1. Modify TopCommentDTO, adding two fields: childCommentCount (total number of child comments) and hasMoreChild (whether there are more replies)
  2. Add a new method getSubComments(Long topCommentId, PageParam page) in CommentReadService
  3. Create a new /comment/api/subcomments endpoint in CommentRestController
  4. Add click-to-expand and asynchronous loading logic in the frontend template

The workload this time was significantly larger, broken down into 9 sub-tasks. The list of files that needed to be touched was also clearly laid out:

Module File Operation
paicoding-api TopCommentDTO.java Add fields
paicoding-api SubCommentListVO.java Create new
paicoding-service CommentReadService.java Add interface method
paicoding-service CommentReadServiceImpl.java Modify + Add methods
paicoding-service CommentDao.java Add method
paicoding-web CommentRestController.java Add endpoint
paicoding-ui comment-item.html Modify template

Let's see the actual effect after running it.

Looks quite decent 😄

05. How CC GUI Works

After using it, let's talk about the principles and figure out how CC GUI actually brings Claude Code into IDEA.

Some might first think: Does it just wrap a terminal inside IDEA and then type the claude command?

Definitely not.

CC GUI does not call the Claude Code CLI (the claude command you type in the terminal) at all. Instead, it directly loads the Claude Agent SDK (@anthropic-ai/claude-agent-sdk) officially released by Anthropic.

This is also why, when you first open CC GUI, it asks you to install the "Claude Code SDK". This SDK is installed in the ~/.codemoss/dependencies/claude-sdk/node_modules/ directory.

The entire communication architecture is divided into three layers. I'll draw a diagram to make it clear:

The core is the middle layer, daemon.js.

After CC GUI starts, it spawns a Node.js child process in the background, running a script called daemon.js. This process runs persistently in the background, not starting a new process for each conversation.

When daemon.js starts, it preloads the Claude Agent SDK into memory. The benefit of this is that SDK initialization happens only once, and subsequent requests reuse it directly, saving 2~5 seconds of cold start overhead. This is why sending messages in CC GUI feels fast.

The communication between the Java side (IDEA plugin) and daemon.js uses the NDJSON protocol (Newline-Delimited JSON). Simply put, they send JSON messages to each other through the child process's stdin/stdout pipes, one message per line.

A request sent from the Java side looks like this:

{"id":"req-001","method":"claude.send","params":{"prompt":"Help me optimize read statistics..."}}

After daemon.js processes it, it streams the response to stdout:

{"id":"req-001","line":"[CONTENT_DELTA] \"First, let me analyze the existing code...\""}
{"id":"req-001","line":"[CONTENT_DELTA] \"Suggest changing to a Redis caching solution...\""}

Each response carries the request ID, allowing the Java side to accurately route the response to the corresponding conversation window. CC GUI supports sending multiple requests in parallel, relying on this ID marking mechanism.

So, what's the deal with settings.json?

CC GUI directly reads ~/.claude/settings.json, which is the configuration file for the Claude Code CLI. This means if you've already configured Claude Code in the terminal (whether with an official account or a third-party API), CC GUI can use it right away without reconfiguration.

This also explains why clicking "Add" in CC GUI's provider settings essentially helps you rewrite settings.json.

The configuration is shared; the CLI and the plugin use the same file.

The same principle applies to MCP and Skills. daemon.js reads the MCP server configurations from settings.json and passes the list of available MCP tools to the SDK when executing requests. So, the Chrome Devtools MCP configured in CC GUI is the same set used in the terminal Claude Code.

Finally, there's a small detail: daemon.js sends a heartbeat check every 15 seconds and checks the parent process (IDEA) every 10 seconds. If IDEA is closed, the daemon automatically exits, preventing zombie processes from occupying resources. Conversely, if the daemon crashes unexpectedly, the Java side automatically restarts it, retrying up to 3 times.

04. Ending

The core value of a good tool is turning troublesome things into convenient things.

Claude Code and Codex are both very capable on their own, but they natively run in the terminal.

What CC GUI does,

is move this capability from the command line into the IDE,

making it a natural part of the daily coding workflow.

A good tool is an extension of the hand, not a replacement for the hand.

The combination of CC GUI + Claude Code is the smoothest way I've found so far for backend development in IntelliJ IDEA.

I must give credit here, Claude Code is very open, supporting both official authorization and API key methods, allowing us to configure various models, including GLM-5.1, Kimi 2.6, and DeepSeek V4.

See you next time~