自動發(fā)送CSDN文章:用TaoToken統(tǒng)一Key打通Spring Boot與Java API)
1. 從本地 Markdown 到 CSDN 草稿MCP 服務(wù)自動發(fā)送文章的真實痛點如果你寫過一段時間技術(shù)博客大概率經(jīng)歷過這種循環(huán)本地用 Typora 或 VS Code 寫完一篇 Markdown手動打開 CSDN 創(chuàng)作中心復(fù)制粘貼調(diào)整格式補標(biāo)簽選分類最后點發(fā)布。單篇還好一旦要同步三五篇舊文或者想把 AI 生成的初稿批量推上去這套動作就變成了純體力活。我試過用腳本直接調(diào) CSDN 的接口能跑通但很快遇到第二個問題Key 太散了。CSDN 的 Cookie 是一套模型調(diào)用是另一套如果還想接 Claude Code 或者別的 Agent 工具又是第三套鑒權(quán)。每個工具都要單獨配一遍 Base URL、API Key、Model ID改一個地方要翻好幾個配置文件。這時候 MCPModel Context Protocol的價值就出來了——它把「工具能力」標(biāo)準(zhǔn)化成服務(wù)端客戶端只需要連一個入口就能調(diào)用發(fā)布文章、生成摘要、潤色正文這些動作。這篇要解決的核心場景是用 Spring Boot 搭一個 MCP 服務(wù)通過 Java 調(diào)用 API把本地 Markdown 文章自動推送到 CSDN。同時用 TaoToken 的統(tǒng)一 Key 把模型調(diào)用和工具調(diào)用的鑒權(quán)收斂到一處避免多工具 Key 分散、配置繁瑣的問題。適合有 Java 基礎(chǔ)、想把自己的內(nèi)容發(fā)布鏈路自動化的開發(fā)者也適合正在研究 MCP 服務(wù)端怎么落地的人。整條鏈路拆開看是三段Spring Boot 提供 MCP 服務(wù)端接口Java 側(cè)負責(zé) Markdown 轉(zhuǎn) HTML 和組裝請求TaoToken 提供統(tǒng)一的模型與 API 入口。下面按可復(fù)現(xiàn)的順序一步步來每一步都給到能直接抄的配置和代碼。2. TaoToken 統(tǒng)一 Key 前置把模型與工具鑒權(quán)收斂到一個入口在動手寫 MCP 服務(wù)之前先把鑒權(quán)這層理清楚。傳統(tǒng)做法是每個外部服務(wù)配一套憑證CSDN 用 Cookie模型調(diào)用用某個平臺的 KeyAgent 工具再配一套。問題在于一旦你要在 MCP 服務(wù)里同時做「生成文章摘要」和「發(fā)布到 CSDN」服務(wù)端就得持有多個憑證配置項散落在 application.yml、環(huán)境變量、甚至硬編碼里。TaoToken 在這里扮演的是統(tǒng)一入口的角色。它提供兼容 OpenAI 風(fēng)格的 API 地址模型對話、Coding Plan、API Keys 管理都在同一個控制臺里。對 MCP 服務(wù)端來說你只需要記住一個 Base URL 和一個 Key模型調(diào)用走這個入口工具鏈的鑒權(quán)也在這里統(tǒng)一管理。官網(wǎng)入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 注意 API 地址不帶 UTM 參數(shù)配置的時候別把查詢串帶進去。具體到操作先去控制臺創(chuàng)建一個 API Key??刂婆_地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 登錄后在 API Keys 頁面新建一個復(fù)制出來形如sk-xxxxxxxx的字符串。這個 Key 后面會同時用在兩處一是 MCP 服務(wù)端調(diào)用模型生成摘要二是作為統(tǒng)一憑證管理其他工具調(diào)用。如果你還沒決定用哪個模型可以先去模型對話頁面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 試一下確認模型 ID 再寫進配置。這里有個容易踩的坑很多人把 Base URL 寫成https://taotoken.net/api/帶尾斜杠然后在代碼里又拼一次路徑結(jié)果變成雙斜杠導(dǎo)致 404。正確寫法是 Base URL 只到/api具體路徑由客戶端庫拼接。另外Key 不要提交到 Git用環(huán)境變量注入Spring Boot 里用${TAOTOKEN_API_KEY}占位。對于長期做編碼和 Agent 的場景可以考慮 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 它更適合高頻調(diào)用和工具鏈集成。接入文檔在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 配置細節(jié)以文檔為準(zhǔn)。把 Key 準(zhǔn)備好之后MCP 服務(wù)端的鑒權(quán)層就簡化成「讀一個環(huán)境變量」。這一步做完后面 Spring Boot 里所有需要模型能力的地方都復(fù)用同一個 Key不用再為每個工具單獨配。3. Spring Boot MCP 服務(wù)端可復(fù)制配置application.yml 與 settings 片段這一節(jié)給到能直接落地的配置。項目基于 Spring Boot 3.4.xJDK 17Maven 3.6。核心依賴包括spring-ai-mcp-server-spring-boot-starter、Retrofit、OkHttp、commonmark。先在pom.xml里加上這些依賴版本按你項目實際管理這里給的是參考版本。dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-server-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency dependency groupIdcom.squareup.retrofit2/groupId artifactIdretrofit/artifactId version2.9.0/version /dependency dependency groupIdcom.squareup.retrofit2/groupId artifactIdconverter-jackson/artifactId version2.9.0/version /dependency dependency groupIdcom.squareup.okhttp3/groupId artifactIdokhttp/artifactId version4.12.0/version /dependency dependency groupIdorg.commonmark/groupId artifactIdcommonmark/artifactId version0.21.0/version /dependency然后是application.yml。這里把 TaoToken 的 Base URL、Key、Model ID 三件套寫全同時配置 CSDN 接口的超時參數(shù)。注意 Key 用環(huán)境變量占位不要寫死。spring: application: name: mcp-service-article-message main: banner-mode: off web-application-type: none taotoken: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} model-id: ${TAOTOKEN_MODEL_ID:gpt-4o-mini} csdn: api: base-url: https://bizapi.csdn.net/ connect-timeout: 30 read-timeout: 30 write-timeout: 30 logging: pattern: console: %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n file: name: data/log/${spring.application.name}.log如果你用的是 Claude Code 或者 Cline 這類客戶端MCP 服務(wù)端的連接配置通常是一個 JSON 片段。以 Claude Code 的 MCP 配置為例路徑一般在~/.claude/settings.json或項目級.mcp.json寫法如下{ mcpServers: { article-publisher: { command: java, args: [-jar, /path/to/mcp-service-article-message.jar], env: { TAOTOKEN_API_KEY: sk-你的Key, TAOTOKEN_MODEL_ID: gpt-4o-mini } } } }這里 Base URL、Key、Model ID 三件套都齊了Base URL 在application.yml的taotoken.base-urlKey 通過環(huán)境變量注入Model ID 在taotoken.model-id。如果你用 Codex 的auth.json結(jié)構(gòu)類似把 Key 放在對應(yīng)字段即可。Cline 的 MCP 配置也是 JSON字段名可能略有差異核心是 command、args、env 三塊。配置寫完先別急著跑檢查兩點一是TAOTOKEN_API_KEY環(huán)境變量在當(dāng)前 shell 里能echo出來二是TAOTOKEN_MODEL_ID是控制臺里真實存在的模型 ID。這兩點確認了服務(wù)啟動時就不會因為鑒權(quán)失敗卡住。4. Java 側(cè)核心實現(xiàn)與一次真實發(fā)送 CSDN 文章的驗證配置就緒后寫核心代碼。整體分四塊Markdown 轉(zhuǎn) HTML、CSDN 請求 DTO、Retrofit 接口定義、MCP 工具回調(diào)。先看 Markdown 轉(zhuǎn)換CSDN 的接口要求內(nèi)容是 HTML所以本地 Markdown 必須先轉(zhuǎn)。Component public class MarkdownConverter { private final Parser parser Parser.builder().build(); private final HtmlRenderer renderer HtmlRenderer.builder().build(); public String convertToHtml(String markdown) { if (markdown null || markdown.isEmpty()) { return ; } Node document parser.parse(markdown); return renderer.render(document); } }接著是請求 DTO字段名要和 CSDN 接口對齊用 Jackson 注解映射。Data Builder public class ArticleRequestDTO { JsonProperty(article_id) private String articleId; private String title; private String description; private String content; private String tags; private String categories; private String type; private Integer status; JsonProperty(read_type) private String readType; }Retrofit 接口定義注意 Header 里要帶 Cookie這是 CSDN 的身份憑證。public interface ICSDNService { Headers({ accept: application/json, text/plain, */*, content-type: application/json; }) POST(/blog-console-api/v1/postedit/saveArticle) CallArticleResponseDTO saveArticleV1( Body ArticleRequestDTO request, Header(Cookie) String cookieValue); }然后是 MCP 工具回調(diào)的注冊。Spring AI 的 MCP starter 提供了MethodToolCallbackProvider把帶有工具注解的方法暴露出去。Bean public ToolCallbackProvider csdnTools(CSDNArticleService articleService) { return MethodToolCallbackProvider.builder() .toolObjects(articleService) .build(); }CSDNArticleService里封裝發(fā)布邏輯同時調(diào)用 TaoToken 生成摘要。這里用 Spring 的RestClient調(diào) TaoToken 的兼容接口Base URL 從配置讀。Service Slf4j public class CSDNArticleService { Value(${taotoken.base-url}) private String taotokenBaseUrl; Value(${taotoken.api-key}) private String taotokenApiKey; Value(${taotoken.model-id}) private String modelId; Resource private ICSDNService csdnService; Resource private MarkdownConverter markdownConverter; public String publishToCSDN(String markdown, String cookie) throws IOException { String htmlContent markdownConverter.convertToHtml(markdown); String summary generateSummary(markdown); ArticleRequestDTO request ArticleRequestDTO.builder() .title(MCP服務(wù)自動發(fā)送CSDN文章實戰(zhàn)) .description(summary) .content(htmlContent) .tags(Java,Spring Boot,MCP) .type(original) .status(0) .readType(public) .build(); ResponseArticleResponseDTO response csdnService.saveArticleV1(request, cookie).execute(); if (response.isSuccessful() response.body() ! null response.body().getCode() 200) { String url response.body().getData().getUrl(); log.info(發(fā)布成功文章地址{}, url); return url; } log.error(發(fā)布失敗HTTP狀態(tài)碼{}, response.code()); return null; } private String generateSummary(String markdown) { RestClient client RestClient.builder() .baseUrl(taotokenBaseUrl) .defaultHeader(Authorization, Bearer taotokenApiKey) .build(); String prompt 用一句話總結(jié)以下技術(shù)文章不超過80字\n markdown; return client.post() .uri(/v1/chat/completions) .body(Map.of( model, modelId, messages, List.of(Map.of(role, user, content, prompt)) )) .retrieve() .body(Map.class) .toString(); } }驗證環(huán)節(jié)準(zhǔn)備一篇本地 Markdown比如demo.md內(nèi)容隨意但要有標(biāo)題和正文。然后從瀏覽器登錄 CSDN 創(chuàng)作中心打開開發(fā)者工具在 Network 里找任意一個bizapi.csdn.net的請求復(fù)制請求頭里的 Cookie 值。注意 Cookie 有時效性過期了要重新取。寫一個測試類或者直接用CommandLineRunner觸發(fā)SpringBootTest class PublishTest { Resource private CSDNArticleService articleService; Test void testPublish() throws IOException { String markdown Files.readString(Path.of(demo.md)); String cookie System.getenv(CSDN_COOKIE); String url articleService.publishToCSDN(markdown, cookie); assertNotNull(url); System.out.println(文章已發(fā)布 url); } }跑通后控制臺會打印文章地址打開就是 CSDN 草稿或已發(fā)布狀態(tài)。實測下來從本地 Markdown 到 CSDN 草稿整個鏈路在 3 秒左右完成摘要由 TaoToken 生成正文格式由 commonmark 轉(zhuǎn)換Cookie 只在這一處使用。5. 本篇常見錯排查401、local proxy failed、reading choices 與 OAuth自動化鏈路跑不通報錯通常集中在幾個地方。下面按真實遇到的錯誤對照排查。401 Unauthorized。這個最常見分兩種。一種是 TaoToken 側(cè)返回 401說明TAOTOKEN_API_KEY沒讀到或者 Key 失效。檢查環(huán)境變量是否在當(dāng)前運行環(huán)境可見Spring Boot 啟動日志里搜taotoken.api-key看是否解析成空。另一種是 CSDN 側(cè)返回 401說明 Cookie 過期或格式不對。Cookie 要完整復(fù)制包括UserToken、UserInfo這些字段少一個都可能鑒權(quán)失敗。注意 Cookie 不要帶換行復(fù)制后檢查一下。local proxy failed。這個報錯通常出現(xiàn)在客戶端連 MCP 服務(wù)端的時候比如 Claude Code 啟動 MCP 進程失敗。原因可能是command路徑不對或者args里的 jar 路徑是相對路徑。改成絕對路徑先手動在終端跑一遍java -jar /path/to/xxx.jar確認能啟動再寫進配置。如果手動能跑、客戶端報 proxy failed檢查客戶端的 MCP 配置 JSON 是否有語法錯誤比如多了逗號。reading choices 相關(guān)報錯。這個一般出現(xiàn)在解析模型返回的時候。TaoToken 的兼容接口返回結(jié)構(gòu)是 OpenAI 風(fēng)格choices數(shù)組里取message.content。如果你直接body.toString()或者按別的結(jié)構(gòu)解析就會報 reading choices 失敗。正確做法是定義響應(yīng) DTO用 Jackson 反序列化取choices[0].message.content。另外注意有些模型返回的content可能是 null要做空值判斷。OAuth 相關(guān)報錯。如果你在 MCP 客戶端里配置了 OAuth 流程但服務(wù)端是本地進程模式可能會報 OAuth 不適用。本地 MCP 服務(wù)端一般用環(huán)境變量傳 Key不需要走 OAuth。檢查客戶端配置里是否誤開了 OAuth 選項關(guān)掉即可。如果確實需要 OAuth那是遠程 MCP 服務(wù)端的場景本地 jar 模式不涉及。CSDN 接口返回非 200 但 HTTP 是 200。這種情況是業(yè)務(wù)層錯誤response.body().getCode()不等于 200。常見原因是文章內(nèi)容為空、標(biāo)題超長、標(biāo)簽格式不對。CSDN 的標(biāo)簽用逗號分隔不要帶空格。分類字段如果填了不存在的分類 ID也會失敗。建議先把status設(shè)為 0草稿確認能創(chuàng)建成功再改成發(fā)布。Markdown 轉(zhuǎn) HTML 后格式錯亂。commonmark 默認不處理表格和任務(wù)列表如果你的文章里有這些需要加擴展。引入commonmark-ext-gfm-tables和commonmark-ext-task-list-items在Parser.builder()里注冊擴展。否則表格會變成純文本CSDN 編輯器里顯示很難看。排查順序建議先確認 TaoToken Key 能單獨調(diào)通模型對話再確認 CSDN Cookie 能單獨調(diào)通發(fā)布接口最后把兩者串起來。分步驗證比一上來跑全鏈路更容易定位問題。6. 把鏈路固定下來從手動發(fā)布到 MCP 工具調(diào)用的日常用法鏈路跑通之后日常用法可以更省事。你不需要每次都寫測試類而是把發(fā)布能力注冊成 MCP 工具在支持 MCP 的客戶端里直接調(diào)用。比如在 Claude Code 里配置好 MCP 服務(wù)端后直接說「把 demo.md 發(fā)布到 CSDN」客戶端會調(diào)用服務(wù)端暴露的工具方法傳入文件路徑服務(wù)端讀文件、轉(zhuǎn) HTML、生成摘要、調(diào) CSDN 接口返回文章地址。這里的關(guān)鍵是把工具方法的入?yún)⒃O(shè)計好。建議入?yún)⒅槐┞秄ilePath和可選的titleCookie 從服務(wù)端環(huán)境變量讀不要每次傳。這樣客戶端調(diào)用時不用關(guān)心鑒權(quán)細節(jié)體驗更順。工具方法的注解用 Spring AI 的Tool描述寫清楚客戶端才能正確理解什么時候調(diào)用它。對于長期做內(nèi)容分發(fā)的場景可以把多個平臺的發(fā)布能力都注冊成 MCP 工具統(tǒng)一走 TaoToken 的 Key 管理。這樣新增一個平臺只需要加一個工具方法不用改客戶端的鑒權(quán)配置。Coding Plan 適合這種高頻、多工具的調(diào)用模式地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后給一個實用技巧把 CSDN Cookie 的刷新也做成半自動。Cookie 過期時接口返回 401服務(wù)端捕獲后記錄日志并返回明確提示你在客戶端看到提示后手動更新環(huán)境變量重啟服務(wù)即可。不要嘗試自動登錄 CSDN 獲取 Cookie那涉及驗證碼和風(fēng)控不穩(wěn)定也不合規(guī)。手動更新一次 Cookie 能用挺久配合 MCP 的調(diào)用體驗整體效率比純手動發(fā)布高很多。如果你在配置過程中卡在某個報錯先去接入文檔 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 對照參數(shù)再去 API Keys 頁面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 確認 Key 狀態(tài)。模型側(cè)的問題用模型對話頁面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 單獨驗證能快速區(qū)分是模型調(diào)用問題還是 CSDN 接口問題。