众所周知,cursor是基于vs code魔改的一个独立应用,并不象copilot、通义灵码 这类采用plugin机制的AI辅助编码工具。在cursor-cli 与 idea MCP出来之前,只能通过idea里安装 Switch2Cursor Plugin for JetBrains IDEs | JetBrains Marketplace 插件,从idea 跳到cursor
反过来,cursor中也有1个类似插件Switch2IDEA – Open VSX Registry 可以跳回到IDEA。虽然也能凑合用吧,但是同时开着2个独立的重型应用,机器配置不够高的话,很容易卡顿,而且割裂感太强。
一、IDEA MCP server
最新版本的IDEA里,新增了 MCP 服务器 | IntelliJ IDEA 文档
127.0.0.1:64342/sse 可以访问看看,了解MCP的同学应该知道,这种sse的MCP 里通常会有一系列可用的工具(参见:spring-ai 学习系列(5)-MCP(webflux sse )
我们用cherry studio 查看一下 (不熟悉cherry studio的同学,可参见 spring-ai 学习系列(3)-MCP(stdio)
切换到工具
这就有点意思了,从工具名称来看,idea把众多对项目编码相关的能力暴露出来了。 接下来,做个实验:
- 在idea里随便打开1个项目
- 在cherry studio里随便打开1个对话,使用idea的这个mcp,要求列出当前项目的结构
cherry studio的会话中,我们使用deepseek模型,有了idea mcp的加持,deepseek正确列出了idea中当前项目的目录。
继续,我们让deepseek为com.cnblogs.yjmyzz.mcp.server.OrderService 这个类生成单元测试,看看能不能生成代码
还真可以(只是限于cherry studio的交互,代码全都挤在 json 的text字段中输出,但格式化后,生成的测试代码大致长这样:

1 package com.cnblogs.yjmyzz.mcp.server; 2 3 import org.junit.jupiter.api.BeforeEach; 4 import org.junit.jupiter.api.DisplayName; 5 import org.junit.jupiter.api.Test; 6 import org.junit.jupiter.api.extension.ExtendWith; 7 import org.mockito.InjectMocks; 8 import org.mockito.junit.jupiter.MockitoExtension; 9 10 import static org.junit.jupiter.api.Assertions.*; 11 12 /** 13 * OrderService单元测试类 14 * 使用Mockito框架进行测试 15 */ 16 @ExtendWith(MockitoExtension.class) 17 @DisplayName("OrderService单元测试") 18 class OrderServiceTest { 19 20 @InjectMocks 21 private OrderService orderService; 22 23 @BeforeEach 24 void setUp() { 25 // 由于OrderService没有依赖其他服务,直接使用@InjectMocks即可 26 // 这里可以设置一些测试前的准备工作 27 } 28 29 @Test 30 @DisplayName("查询已发货订单状态 - 成功") 31 void queryOrderStatus_WhenOrderShipped_ShouldReturnShippedStatus() { 32 // Arrange 33 String orderNo = "25070601"; 34 String expected = "订单号:25070601,订单状态:已发货"; 35 36 // Act 37 String actual = orderService.queryOrderStatus(orderNo); 38 39 // Assert 40 assertEquals(expected, actual); 41 } 42 43 @Test 44 @DisplayName("查询已完成订单状态 - 成功") 45 void queryOrderStatus_WhenOrderCompleted_ShouldReturnCompletedStatus() { 46 // Arrange 47 String orderNo = "25070602"; 48 String expected = "订单号:25070602,订单状态:已完成"; 49 50 // Act 51 String actual = orderService.queryOrderStatus(orderNo); 52 53 // Assert 54 assertEquals(expected, actual); 55 } 56 57 @Test 58 @DisplayName("查询已取消订单状态 - 成功") 59 void queryOrderStatus_WhenOrderCancelled_ShouldReturnCancelledStatus() { 60 // Arrange 61 String orderNo = "25070603"; 62 String expected = "订单号:25070603,订单状态:已取消"; 63 64 // Act 65 String actual = orderService.queryOrderStatus(orderNo); 66 67 // Assert 68 assertEquals(expected, actual); 69 } 70 71 @Test 72 @DisplayName("查询未知订单状态 - 返回未知状态") 73 void queryOrderStatus_WhenOrderUnknown_ShouldReturnUnknownStatus() { 74 // Arrange 75 String orderNo = "99999999"; 76 String expected = "订单号:99999999,订单状态:未知"; 77 78 // Act 79 String actual = orderService.queryOrderStatus(orderNo); 80 81 // Assert 82 assertEquals(expected, actual); 83 } 84 85 @Test 86 @DisplayName("查询空订单号 - 返回未知状态") 87 void queryOrderStatus_WhenOrderNoIsEmpty_ShouldReturnUnknownStatus() { 88 // Arrange 89 String orderNo = ""; 90 String expected = "订单号:,订单状态:未知"; 91 92 // Act 93 String actual = orderService.queryOrderStatus(orderNo); 94 95 // Assert 96 assertEquals(expected, actual); 97 } 98 99 @Test 100 @DisplayName("查询null订单号 - 返回未知状态") 101 void queryOrderStatus_WhenOrderNoIsNull_ShouldReturnUnknownStatus() { 102 // Arrange 103 String orderNo = null; 104 String expected = "订单号:null,订单状态:未知"; 105 106 // Act 107 String actual = orderService.queryOrderStatus(orderNo); 108 109 // Assert 110 assertEquals(expected, actual); 111 } 112 113 @Test 114 @DisplayName("查询非8位数字订单号 - 返回未知状态") 115 void queryOrderStatus_WhenOrderNoIsNot8Digits_ShouldReturnUnknownStatus() { 116 // Arrange 117 String orderNo = "1234567"; // 7位数字 118 String expected = "订单号:1234567,订单状态:未知"; 119 120 // Act 121 String actual = orderService.queryOrderStatus(orderNo); 122 123 // Assert 124 assertEquals(expected, actual); 125 } 126 127 @Test 128 @DisplayName("查询包含字母的订单号 - 返回未知状态") 129 void queryOrderStatus_WhenOrderNoContainsLetters_ShouldReturnUnknownStatus() { 130 // Arrange 131 String orderNo = "2507060A"; 132 String expected = "订单号:2507060A,订单状态:未知"; 133 134 // Act 135 String actual = orderService.queryOrderStatus(orderNo); 136 137 // Assert 138 assertEquals(expected, actual); 139 } 140 141 @Test 142 @DisplayName("边界测试 - 最小订单号") 143 void queryOrderStatus_WhenOrderNoIsMinValue_ShouldReturnUnknownStatus() { 144 // Arrange 145 String orderNo = "00000000"; 146 String expected = "订单号:00000000,订单状态:未知"; 147 148 // Act 149 String actual = orderService.queryOrderStatus(orderNo); 150 151 // Assert 152 assertEquals(expected, actual); 153 } 154 155 @Test 156 @DisplayName("边界测试 - 最大订单号") 157 void queryOrderStatus_WhenOrderNoIsMaxValue_ShouldReturnUnknownStatus() { 158 // Arrange 159 String orderNo = "99999999"; 160 String expected = "订单号:99999999,订单状态:未知"; 161 162 // Act 163 String actual = orderService.queryOrderStatus(orderNo); 164 165 // Assert 166 assertEquals(expected, actual); 167 } 168 }
这说明:有了IDEA开放的MCP能力,任何1个有推理能力的LLM,再挂1套MCP host和UI,理论上都可以手搓成1个AI编码辅助神器!
二、Cursor CLI
命令行界面(CLI) · Cursor 根据这份cursor官网的介绍,cursor可以在任何终端以cli 方式运行。
curl https://cursor.com/install -fsS | bash
只要这一行命令即可。注:windows上必须借助WSL (不熟悉wsl的同学可参考windows WSL2避坑指南 ),在wsl环境中使用该命令安装(比如:先wsl安装ubuntu,然后在ubuntu虚拟子环境中安装)
安装完后,就可以直接问一些编码相关的问题了
cursor也是支持MCP的,我们在cursor中,把IDEA的mcp地址也配置上
三、idea中设置terminal 为wsl
注:只有windows上需要设置成wsl.exe (mac / linux下,正常设置默认terminal即可)
最终运行效果:idea中切换到terminal时,自动启动wsl环境,然后输入cursor-agent,自动将上下文切换到当前目录
唯一美中不足:虽然wsl的cursor-cli 也正确添加了mcp server,也通curl 访问到,但是cursor-cli 连接时总是报405,看上去是授权问题,如有园友知晓解法,请回复,感谢