MCP实现CSDN自动发帖
随着 AI 与自动化工具的发展,实现自动发布文章到 CSDN 的能力可以大大提升内容创作者的工作效率。本文将围绕以下三部分展开:
- 服务端:构建 CSDN 发帖 API 接口服务
- 客户端:集成 MCP 工具调用发帖服务
- 测试端:联动 AI 流程实现自动生成并发帖
🚀 一、服务端:构建 CSDN 发帖 API 接口服务
1. 引入依赖
首先在 Spring Boot 项目中引入所需依赖,主要包含 MCP Server、Hutool HTTP 工具包、Flexmark 富文本转换器等。
<dependencies>
<!--MCP服务端-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.38</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-all</artifactId>
<version>0.64.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
2. 抓取 CSDN 提交文章请求信息
我们通过浏览器抓包获取 CSDN 提交文章所需的请求地址和结构,打开发布文章页面:
📌 打开 CSDN 编辑器
点击“发布”按钮时抓取请求(注意要立刻停止录制,避免页面跳转):

请求地址为:https://bizapi.csdn.net/blog-console-api/v3/mdeditor/saveArticle

我们可以复制请求体用于本地调试。

curl 'https://bizapi.csdn.net/blog-console-api/v1/postedit/saveArticle' \
-H 'authority: bizapi.csdn.net' \
-H 'accept: application/json, text/plain, */*' \
-H 'accept-language: zh-CN,zh;q=0.9' \
-H 'content-type: application/json;' \
-H 'cookie: xxxxx\
-H 'origin: https://mpbeta.csdn.net' \
-H 'referer: https://mpbeta.csdn.net/' \
-H 'sec-ch-ua: "Chromium";v="9", "Not?A_Brand";v="8"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "Windows"' \
-H 'sec-fetch-dest: empty' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-site: same-site' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 SLBrowser/9.0.3.5211 SLBChan/111' \
-H 'x-ca-key: xxxx' \
-H 'x-ca-nonce: xxxxx' \
-H 'x-ca-signature: xxxxx' \
-H 'x-ca-signature-headers: x-ca-key,x-ca-nonce' \
--data-raw '{"title":"我是测试标题","description":"测试内容。","content":"<p>测试内容</p>\n","tags":"java","categories":"","type":"original","status":0,"read_type":"private","reason":"","original_link":"","authorized_status":false,"check_original":false,"source":"pc_postedit","not_auto_saved":1,"creator_activity_id":"","cover_images":[],"cover_type":1,"vote_id":0,"resource_id":"","scheduled_time":0,"markdowncontent":"","resource_url":"","editor_type":0,"plan":[],"level":"0","is_new":1,"sync_git_code":0}' \
--compressed
我们可以借助AI生成如下代码
现在我有这些依赖,我现在想要实现往CSDN实现本地发帖传送到CSDN服务器上,curl格式请求如下curl 'https://bizapi.csdn.net/blog-console-api/v3/mdeditor/saveArticle' \
-H 'authority: bizapi.csdn.net' \
-H 'accept: */*' \
-H 'accept-language: zh-CN,zh;q=0.9' \
-H 'content-type: application/json' \
-H 'cookie: xxxxx' \
-H 'origin: https://editor.csdn.net' \
-H 'referer: https://editor.csdn.net/' \
-H 'sec-ch-ua: "Chromium";v="9", "Not?A_Brand";v="8"' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'sec-ch-ua-platform: "Windows"' \
-H 'sec-fetch-dest: empty' \
-H 'sec-fetch-mode: cors' \
-H 'sec-fetch-site: same-site' \
-H 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 SLBrowser/9.0.3.5211 SLBChan/111' \
-H 'x-ca-key: xxxx' \
-H 'x-ca-nonce: xxxxx' \
-H 'x-ca-signature: xxxxxxx' \
-H 'x-ca-signature-headers: x-ca-key,x-ca-nonce' \
--data-raw '{"title":"我是测试标题","markdowncontent":"测试内容\n","content":"<p>测试内容</p>\n\n","readType":"private","level":0,"tags":"java","status":0,"categories":"","type":"original","original_link":"","authorized_status":false,"Description":"","not_auto_saved":"1","source":"pc_mdeditor","cover_images":[],"cover_type":1,"is_new":1,"vote_id":0,"resource_id":"","pubStatus":"publish"}' \
--compressed
我的需求如下
1. 请按照标准项目格式,所有的包都在com.flycode内,生成Domain包,里面是对应实体类,为CSDNArticle,生成service包里面提供服务,有接口ICsdnService和CSDNServiceImpl服务实现类 ,里面有接口,需要传入文章内容实体类以及当前的Cookie,并且文章内容需要使用flexmark-all将普通文本转换为富文本
2. 发送网络请求,使用Hutool工具类发送请求 ,将所有的请求头都
3. 适当加入log日志
4. 添加主启动类Application.java
5. 生成ICsdnService的测试类到test文件夹内和ICsdnService同一个软件包位置
6. 响应格式如下{
"code": 200,
"traceId": "xxx",
"data": {
"url": "xxxx",
"id": xxxx,
"qrcode": "xxxx",
"title": "xxx",
"description": "xxx。"
},
"msg": "success"
}

3. 文章实体类定义
@Data
public class CSDNArticle {
private String title;
private String markdowncontent;
private String content;
private String readType;
private int level;
private String tags;
private int status;
private String categories;
private String type;
private String original_link;
private boolean authorized_status;
private String Description;
private String not_auto_saved;
private String source;
private List<String> cover_images;
private int cover_type;
private int is_new;
private int vote_id;
private String resource_id;
private String pubStatus;
}
4. 响应结构体
@Data
public class CSDNResponse {
private int code;
private String traceId;
private Data data;
private String msg;
@Data
public static class Data {
private String url;
private long id;
private String qrcode;
private String title;
private String description;
}
}
5. 服务接口与实现
接口定义:
public interface ICsdnService {
CSDNResponse postArticle(CSDNArticle article, String cookie);
}
实现类:
@Service
public class CSDNServiceImpl implements ICsdnService {
private static final Logger log = LoggerFactory.getLogger(CSDNServiceImpl.class);
private static final String URL = "https://bizapi.csdn.net/blog-console-api/v3/mdeditor/saveArticle";
private final ObjectMapper objectMapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
@Override
public CSDNResponse postArticle(CSDNArticle article, String cookie) {
try {
// markdown 转 html
Parser parser = Parser.builder().build();
HtmlRenderer renderer = HtmlRenderer.builder().build();
Node document = parser.parse(article.getMarkdowncontent());
String html = renderer.render(document);
article.setContent(html);
String body = objectMapper.writeValueAsString(article);
log.info("请求体: {}", body);
HttpRequest request = HttpRequest.post(URL)
.header("Accept", "*/*")
.header("Accept-Language", "zh-CN,zh;q=0.9")
.header("Content-Type", "application/json")
.header("Dnt", "1")
.header("Origin", "https://editor.csdn.net")
.header("Priority", " u=1, i")
.header("Referer", "https://editor.csdn.net/")
.header("Sec-Ch-Ua", "\"Chromium\";v=\"9\", \"Not?A_Brand\";v=\"8\"")
.header("Sec-Ch-Ua-Mobile", "?0")
.header("Sec-Ch-Ua-Platform", "\"Windows\"")
.header("Sec-Fetch-Dest", "empty")
.header("Sec-Fetch-Mode", "cors")
.header("Sec-Fetch-Site", "same-site")
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 SLBrowser/9.0.3.5211 SLBChan/111")
.header("x-ca-key", "xxxx")
.header("x-ca-nonce", "xxxxx")
.header("x-ca-signature", "xxxxxxx")
.header("x-ca-signature-headers", "x-ca-key,x-ca-nonce")
.header("cookie", cookie)
.body(body)
.timeout(10000);
HttpResponse response = request.execute();
log.info("完整请求对象: {}", request.toString());
String respStr = response.body();
log.info("CSDN响应: {}", respStr);
return objectMapper.readValue(respStr, CSDNResponse.class);
} catch (Exception e) {
log.error("发帖失败", e);
CSDNResponse error = new CSDNResponse();
error.setCode(500);
error.setMsg("发帖异常: " + e.getMessage());
return error;
}
}
}
6. 测试类
@Test
public void testPostArticle() {
CSDNArticle article = new CSDNArticle();
article.setTitle("我是测试标题");
article.setMarkdowncontent("测试内容1111\n");
article.setContent("<p>测试内容</p><p>helloworld</p>\\n\\n");
article.setReadType("private");
article.setLevel(0);
article.setTags("java");
article.setStatus(0);
article.setCategories("");
article.setType("original");
article.setOriginal_link("");
article.setAuthorized_status(false);
article.setDescription("");
article.setNot_auto_saved("1");
article.setSource("pc_mdeditor");
article.setCover_images(Collections.emptyList());
article.setCover_type(1);
article.setIs_new(1);
article.setVote_id(0);
article.setResource_id("");
article.setPubStatus("publish");
String cookie = "xxxxx";
CSDNResponse response = csdnService.postArticle(article, cookie);
assertNotNull(response);
assertTrue(response.getCode() == 200 || response.getCode() == 500);
}

7. 简化和抽象代码
将cookie抽象出来,便于修改
@Data
@Component
@ConfigurationProperties(prefix = "csdn.api")
public class CSDNProperties {
private String cookie;
}
编写实体类Dto、简化参数
@Data
public class CSDNArticleDto {
@ToolParam(required = true,description = "CSDN文章标题,不得小于5个字符")
private String title;
@ToolParam(required = true,description = "CSDN文章内容Markdown格式")
private String markdowncontent;
@ToolParam(required = true,description = "文章标签,多个标签用逗号分隔")
private String tags;
@ToolParam(required = true,description = "文章描述")
private String description;
}
重载CSDN发帖方法,我们只需要重新调用下之前的发帖方法即可,不需要重写方法
@Override
@Tool(description = "CSDN发帖")
public CSDNResponse postArticle(CSDNArticleDto csdnArticleDto) {
CSDNArticle csdnArticle = new CSDNArticle();
csdnArticle.setReadType("private");
csdnArticle.setLevel(0);
csdnArticle.setTags("java");
csdnArticle.setStatus(0);
csdnArticle.setCategories("");
csdnArticle.setType("original");
csdnArticle.setOriginal_link("");
csdnArticle.setAuthorized_status(false);
csdnArticle.setNot_auto_saved("1");
csdnArticle.setSource("pc_mdeditor");
csdnArticle.setCover_images(Collections.emptyList());
csdnArticle.setCover_type(1);
csdnArticle.setIs_new(1);
csdnArticle.setVote_id(0);
csdnArticle.setResource_id("");
csdnArticle.setPubStatus("public");
String cookie = csdnProperties.getCookie();
BeanUtils.copyProperties(csdnArticleDto, csdnArticle);
return this.postArticle(csdnArticle, cookie);
}
测试
@Test
void postArticle() {
CSDNArticleDto csdnArticleDto = new CSDNArticleDto();
csdnArticleDto.setTitle("我是测试文章");
csdnArticleDto.setDescription("这是一个测试文章");
csdnArticleDto.setMarkdowncontent("### 这是一个测试文章");
csdnArticleDto.setTags("java");
CSDNResponse csdnResponse = csdnService.postArticle(csdnArticleDto);
}
8. 编写yml配置
主要作用是定义当前工具服务名、关闭日志输出、设置cookie
spring:
application:
name: ai-mcp-server-csdn
ai:
mcp:
server:
name: ${spring.application.name}
version: 1.0.0
main:
banner-mode: off
web-application-type: none
logging:
pattern:
console:
file:
name: ${spring.application.name}.log
server:
servlet:
encoding:
charset: UTF-8
force: true
enabled: true
csdn:
api:
cookie: xxxx
9. 启动类定义
使用ToolCallbackProvider将当前服务提供出去给其他系统使用
@SpringBootApplication
@Slf4j
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public ToolCallbackProvider csdnTool(CSDNServiceImpl csdnService) {
return MethodToolCallbackProvider.builder().toolObjects(csdnService).build();
}
@Override
public void run(String... args) throws Exception {
log.info("csdn begin");
}
}
10. 打包工具
我们需要将当前项目打包,记住当前打包文件的位置,我们需要在客户端配置相应的信息
需要在pom里面加入build代码
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<mainClass>com.flycode.Application</mainClass>
<executable>true</executable>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

🖥 二、客户端:集成 MCP 客户端自动调用发帖服务
1. 配置 MCP 启动 JSON
{
"mcpServers": {
"ai-mcp-server-csdn": {
"command": "D:\\Developer Tools\\JDK21\\bin\\java.exe",
"args": [
"-Dspring.ao.mcp.server.stdio=true",
"-jar",
"D:\\xxxx\\ai-mcp-server-csdn-1.0-SNAPSHOT.jar"
]
}
}
}
2. 客户端配置 yml
详细配置客户端信息可以浏览MCP新版实现
spring:
application:
name: fly-dev-app
ai:
ollama:
base-url: http://172.26.37.0:11434
embedding:
enabled: true
options:
num-batch: 512
model: nomic-embed-text
zhipuai:
api-key: a9445ac34f0841a899b13699b19c7fb3.NCGDN8fQmcWmMoTd
chat:
options:
model: glm-4-plus
base-url: https://open.bigmodel.cn/api/paas/
mcp:
client:
stdio:
servers-configuration: classpath:config/mcp-servers.json
server:
port: 8090
🧪 三、测试端:AI 联动自动生成并发布 CSDN 文章
1. 测试工具注册是否成功
详细配置客户端信息可以浏览MCP新版实现
@Configuration
public class ZhipuConfig {
@Bean
public ZhiPuAiApi zhiPuAiApi(@Value("${spring.ai.zhipuai.base-url}") String baseUrl, @Value("${spring.ai.zhipuai.api-key}") String apiKey) {
return new ZhiPuAiApi(baseUrl, apiKey);
}
@Bean
public ChatClient.Builder zhipuChatClientBuilder(ZhiPuAiChatModel zhipuChatModel) {
return new DefaultChatClientBuilder(zhipuChatModel, ObservationRegistry.NOOP, (ChatClientObservationConvention) null);
}
}
测试当前有哪些工具可以调用
@Resource
private ToolCallbackProvider toolCallback;
@Resource
private ChatClient.Builder zhipuChatClientBuilder;
@Test
public void testTools() {
String res = zhipuChatClientBuilder.defaultTools(toolCallback).build().prompt("当前有哪些工具可用").call().chatResponse().getResult().getOutput().getText();
log.info(res);
}

2. 自动生成发帖内容并发布
@Test
public void testCsdnTool() {
String prompt = "帮我生成一个Java的快速排序代码,帮我将这些代码以及代码讲解使用Markdown格式,标题为快速排序算法,内容是之前的Markdown格式,标签是Java,描述就简要描述下快速排序算法是什么,然后在CSDN发帖";
String text = zhipuChatClientBuilder
.defaultTools(toolCallback)
.build()
.prompt(new Prompt(prompt))
.call()
.chatResponse()
.getResult()
.getOutput()
.getText();
log.info(text);
}
📸 自动发帖全过程截图:


📌 注意:CSDN 对频繁发帖行为有限制,避免频繁触发。
✅ 总结
我们已经完整实现:
- 服务端处理 Markdown 转 HTML 及发帖请求
- 客户端配置 MCP 启动方式
- 测试端集成 AI 工具实现一键发帖
⚠️ 温馨提示:请确保 Cookie 合法有效,发帖频率合理,以免触发平台风控。
欢迎关注我获取更多自动化开发干货!
贡献者
flycodeu
版权所有
版权归属:flycodeu