自動(dòng)發(fā)送CSDN文章:用TaoToken統(tǒng)一Key打通Spring Boot與Java API)
1. 從本地 Markdown 到 CSDN 草稿MCP 服務(wù)自動(dòng)發(fā)送文章的真實(shí)痛點(diǎn)如果你寫過(guò)一段時(shí)間技術(shù)博客大概率經(jīng)歷過(guò)這種循環(huán)本地用 Typora 或 VS Code 寫完一篇 Markdown手動(dòng)打開 CSDN 創(chuàng)作中心復(fù)制粘貼調(diào)整格式補(bǔ)標(biāo)簽選分類最后點(diǎn)發(fā)布。單篇還好一旦要同步三五篇舊文或者想把 AI 生成的初稿批量推上去這套動(dòng)作就變成了純體力活。我試過(guò)用腳本直接調(diào) CSDN 的接口能跑通但很快遇到第二個(gè)問(wèn)題Key 太散了。CSDN 的 Cookie 是一套模型調(diào)用是另一套如果還想接 Claude Code 或者別的 Agent 工具又是第三套鑒權(quán)。每個(gè)工具都要單獨(dú)配一遍 Base URL、API Key、Model ID改一個(gè)地方要翻好幾個(gè)配置文件。這時(shí)候 MCPModel Context Protocol的價(jià)值就出來(lái)了——它把「工具能力」標(biāo)準(zhǔn)化成服務(wù)端客戶端只需要連一個(gè)入口就能調(diào)用發(fā)布文章、生成摘要、潤(rùn)色正文這些動(dòng)作。這篇要解決的核心場(chǎng)景是用 Spring Boot 搭一個(gè) MCP 服務(wù)通過(guò) Java 調(diào)用 API把本地 Markdown 文章自動(dòng)推送到 CSDN。同時(shí)用 TaoToken 的統(tǒng)一 Key 把模型調(diào)用和工具調(diào)用的鑒權(quán)收斂到一處避免多工具 Key 分散、配置繁瑣的問(wèn)題。適合有 Java 基礎(chǔ)、想把自己的內(nèi)容發(fā)布鏈路自動(dòng)化的開發(fā)者也適合正在研究 MCP 服務(wù)端怎么落地的人。整條鏈路拆開看是三段Spring Boot 提供 MCP 服務(wù)端接口Java 側(cè)負(fù)責(zé) Markdown 轉(zhuǎn) HTML 和組裝請(qǐng)求TaoToken 提供統(tǒng)一的模型與 API 入口。下面按可復(fù)現(xiàn)的順序一步步來(lái)每一步都給到能直接抄的配置和代碼。2. TaoToken 統(tǒng)一 Key 前置把模型與工具鑒權(quán)收斂到一個(gè)入口在動(dòng)手寫 MCP 服務(wù)之前先把鑒權(quán)這層理清楚。傳統(tǒng)做法是每個(gè)外部服務(wù)配一套憑證CSDN 用 Cookie模型調(diào)用用某個(gè)平臺(tái)的 KeyAgent 工具再配一套。問(wèn)題在于一旦你要在 MCP 服務(wù)里同時(shí)做「生成文章摘要」和「發(fā)布到 CSDN」服務(wù)端就得持有多個(gè)憑證配置項(xiàng)散落在 application.yml、環(huán)境變量、甚至硬編碼里。TaoToken 在這里扮演的是統(tǒng)一入口的角色。它提供兼容 OpenAI 風(fēng)格的 API 地址模型對(duì)話、Coding Plan、API Keys 管理都在同一個(gè)控制臺(tái)里。對(duì) MCP 服務(wù)端來(lái)說(shuō)你只需要記住一個(gè) Base URL 和一個(gè) Key模型調(diào)用走這個(gè)入口工具鏈的鑒權(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ù)配置的時(shí)候別把查詢串帶進(jìn)去。具體到操作先去控制臺(tái)創(chuàng)建一個(gè) API Key??刂婆_(tái)地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 登錄后在 API Keys 頁(yè)面新建一個(gè)復(fù)制出來(lái)形如sk-xxxxxxxx的字符串。這個(gè) Key 后面會(huì)同時(shí)用在兩處一是 MCP 服務(wù)端調(diào)用模型生成摘要二是作為統(tǒng)一憑證管理其他工具調(diào)用。如果你還沒(méi)決定用哪個(gè)模型可以先去模型對(duì)話頁(yè)面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 試一下確認(rèn)模型 ID 再寫進(jìn)配置。這里有個(gè)容易踩的坑很多人把 Base URL 寫成https://taotoken.net/api/帶尾斜杠然后在代碼里又拼一次路徑結(jié)果變成雙斜杠導(dǎo)致 404。正確寫法是 Base URL 只到/api具體路徑由客戶端庫(kù)拼接。另外Key 不要提交到 Git用環(huán)境變量注入Spring Boot 里用${TAOTOKEN_API_KEY}占位。對(duì)于長(zhǎng)期做編碼和 Agent 的場(chǎng)景可以考慮 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 配置細(xì)節(jié)以文檔為準(zhǔn)。把 Key 準(zhǔn)備好之后MCP 服務(wù)端的鑒權(quán)層就簡(jiǎn)化成「讀一個(gè)環(huán)境變量」。這一步做完后面 Spring Boot 里所有需要模型能力的地方都復(fù)用同一個(gè) Key不用再為每個(gè)工具單獨(dú)配。3. Spring Boot MCP 服務(wù)端可復(fù)制配置application.yml 與 settings 片段這一節(jié)給到能直接落地的配置。項(xiàng)目基于 Spring Boot 3.4.xJDK 17Maven 3.6。核心依賴包括spring-ai-mcp-server-spring-boot-starter、Retrofit、OkHttp、commonmark。先在pom.xml里加上這些依賴版本按你項(xiàng)目實(shí)際管理這里給的是參考版本。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 三件套寫全同時(shí)配置 CSDN 接口的超時(shí)參數(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ù)端的連接配置通常是一個(gè) JSON 片段。以 Claude Code 的 MCP 配置為例路徑一般在~/.claude/settings.json或項(xiàng)目級(jí).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 通過(guò)環(huán)境變量注入Model ID 在taotoken.model-id。如果你用 Codex 的auth.json結(jié)構(gòu)類似把 Key 放在對(duì)應(yīng)字段即可。Cline 的 MCP 配置也是 JSON字段名可能略有差異核心是 command、args、env 三塊。配置寫完先別急著跑檢查兩點(diǎn)一是TAOTOKEN_API_KEY環(huán)境變量在當(dāng)前 shell 里能echo出來(lái)二是TAOTOKEN_MODEL_ID是控制臺(tái)里真實(shí)存在的模型 ID。這兩點(diǎn)確認(rèn)了服務(wù)啟動(dòng)時(shí)就不會(huì)因?yàn)殍b權(quán)失敗卡住。4. Java 側(cè)核心實(shí)現(xiàn)與一次真實(shí)發(fā)送 CSDN 文章的驗(yàn)證配置就緒后寫核心代碼。整體分四塊Markdown 轉(zhuǎn) HTML、CSDN 請(qǐng)求 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); } }接著是請(qǐng)求 DTO字段名要和 CSDN 接口對(duì)齊用 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)的注冊(cè)。Spring AI 的 MCP starter 提供了MethodToolCallbackProvider把帶有工具注解的方法暴露出去。Bean public ToolCallbackProvider csdnTools(CSDNArticleService articleService) { return MethodToolCallbackProvider.builder() .toolObjects(articleService) .build(); }CSDNArticleService里封裝發(fā)布邏輯同時(shí)調(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ù)自動(dòng)發(fā)送CSDN文章實(shí)戰(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ù)文章不超過(guò)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(); } }驗(yàn)證環(huán)節(jié)準(zhǔn)備一篇本地 Markdown比如demo.md內(nèi)容隨意但要有標(biāo)題和正文。然后從瀏覽器登錄 CSDN 創(chuàng)作中心打開開發(fā)者工具在 Network 里找任意一個(gè)bizapi.csdn.net的請(qǐng)求復(fù)制請(qǐng)求頭里的 Cookie 值。注意 Cookie 有時(shí)效性過(guò)期了要重新取。寫一個(gè)測(cè)試類或者直接用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); } }跑通后控制臺(tái)會(huì)打印文章地址打開就是 CSDN 草稿或已發(fā)布狀態(tài)。實(shí)測(cè)下來(lái)從本地 Markdown 到 CSDN 草稿整個(gè)鏈路在 3 秒左右完成摘要由 TaoToken 生成正文格式由 commonmark 轉(zhuǎn)換Cookie 只在這一處使用。5. 本篇常見(jiàn)錯(cuò)排查401、local proxy failed、reading choices 與 OAuth自動(dòng)化鏈路跑不通報(bào)錯(cuò)通常集中在幾個(gè)地方。下面按真實(shí)遇到的錯(cuò)誤對(duì)照排查。401 Unauthorized。這個(gè)最常見(jiàn)分兩種。一種是 TaoToken 側(cè)返回 401說(shuō)明TAOTOKEN_API_KEY沒(méi)讀到或者 Key 失效。檢查環(huán)境變量是否在當(dāng)前運(yùn)行環(huán)境可見(jiàn)Spring Boot 啟動(dòng)日志里搜taotoken.api-key看是否解析成空。另一種是 CSDN 側(cè)返回 401說(shuō)明 Cookie 過(guò)期或格式不對(duì)。Cookie 要完整復(fù)制包括UserToken、UserInfo這些字段少一個(gè)都可能鑒權(quán)失敗。注意 Cookie 不要帶換行復(fù)制后檢查一下。local proxy failed。這個(gè)報(bào)錯(cuò)通常出現(xiàn)在客戶端連 MCP 服務(wù)端的時(shí)候比如 Claude Code 啟動(dòng) MCP 進(jìn)程失敗。原因可能是command路徑不對(duì)或者args里的 jar 路徑是相對(duì)路徑。改成絕對(duì)路徑先手動(dòng)在終端跑一遍java -jar /path/to/xxx.jar確認(rèn)能啟動(dòng)再寫進(jìn)配置。如果手動(dòng)能跑、客戶端報(bào) proxy failed檢查客戶端的 MCP 配置 JSON 是否有語(yǔ)法錯(cuò)誤比如多了逗號(hào)。reading choices 相關(guān)報(bào)錯(cuò)。這個(gè)一般出現(xiàn)在解析模型返回的時(shí)候。TaoToken 的兼容接口返回結(jié)構(gòu)是 OpenAI 風(fēng)格choices數(shù)組里取message.content。如果你直接body.toString()或者按別的結(jié)構(gòu)解析就會(huì)報(bào) reading choices 失敗。正確做法是定義響應(yīng) DTO用 Jackson 反序列化取choices[0].message.content。另外注意有些模型返回的content可能是 null要做空值判斷。OAuth 相關(guān)報(bào)錯(cuò)。如果你在 MCP 客戶端里配置了 OAuth 流程但服務(wù)端是本地進(jìn)程模式可能會(huì)報(bào) OAuth 不適用。本地 MCP 服務(wù)端一般用環(huán)境變量傳 Key不需要走 OAuth。檢查客戶端配置里是否誤開了 OAuth 選項(xiàng)關(guān)掉即可。如果確實(shí)需要 OAuth那是遠(yuǎn)程 MCP 服務(wù)端的場(chǎng)景本地 jar 模式不涉及。CSDN 接口返回非 200 但 HTTP 是 200。這種情況是業(yè)務(wù)層錯(cuò)誤response.body().getCode()不等于 200。常見(jiàn)原因是文章內(nèi)容為空、標(biāo)題超長(zhǎng)、標(biāo)簽格式不對(duì)。CSDN 的標(biāo)簽用逗號(hào)分隔不要帶空格。分類字段如果填了不存在的分類 ID也會(huì)失敗。建議先把status設(shè)為 0草稿確認(rèn)能創(chuàng)建成功再改成發(fā)布。Markdown 轉(zhuǎn) HTML 后格式錯(cuò)亂。commonmark 默認(rèn)不處理表格和任務(wù)列表如果你的文章里有這些需要加擴(kuò)展。引入commonmark-ext-gfm-tables和commonmark-ext-task-list-items在Parser.builder()里注冊(cè)擴(kuò)展。否則表格會(huì)變成純文本CSDN 編輯器里顯示很難看。排查順序建議先確認(rèn) TaoToken Key 能單獨(dú)調(diào)通模型對(duì)話再確認(rèn) CSDN Cookie 能單獨(dú)調(diào)通發(fā)布接口最后把兩者串起來(lái)。分步驗(yàn)證比一上來(lái)跑全鏈路更容易定位問(wèn)題。6. 把鏈路固定下來(lái)從手動(dòng)發(fā)布到 MCP 工具調(diào)用的日常用法鏈路跑通之后日常用法可以更省事。你不需要每次都寫測(cè)試類而是把發(fā)布能力注冊(cè)成 MCP 工具在支持 MCP 的客戶端里直接調(diào)用。比如在 Claude Code 里配置好 MCP 服務(wù)端后直接說(shuō)「把 demo.md 發(fā)布到 CSDN」客戶端會(huì)調(diào)用服務(wù)端暴露的工具方法傳入文件路徑服務(wù)端讀文件、轉(zhuǎn) HTML、生成摘要、調(diào) CSDN 接口返回文章地址。這里的關(guān)鍵是把工具方法的入?yún)⒃O(shè)計(jì)好。建議入?yún)⒅槐┞秄ilePath和可選的titleCookie 從服務(wù)端環(huán)境變量讀不要每次傳。這樣客戶端調(diào)用時(shí)不用關(guān)心鑒權(quán)細(xì)節(jié)體驗(yàn)更順。工具方法的注解用 Spring AI 的Tool描述寫清楚客戶端才能正確理解什么時(shí)候調(diào)用它。對(duì)于長(zhǎng)期做內(nèi)容分發(fā)的場(chǎng)景可以把多個(gè)平臺(tái)的發(fā)布能力都注冊(cè)成 MCP 工具統(tǒng)一走 TaoToken 的 Key 管理。這樣新增一個(gè)平臺(tái)只需要加一個(gè)工具方法不用改客戶端的鑒權(quán)配置。Coding Plan 適合這種高頻、多工具的調(diào)用模式地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。最后給一個(gè)實(shí)用技巧把 CSDN Cookie 的刷新也做成半自動(dòng)。Cookie 過(guò)期時(shí)接口返回 401服務(wù)端捕獲后記錄日志并返回明確提示你在客戶端看到提示后手動(dòng)更新環(huán)境變量重啟服務(wù)即可。不要嘗試自動(dòng)登錄 CSDN 獲取 Cookie那涉及驗(yàn)證碼和風(fēng)控不穩(wěn)定也不合規(guī)。手動(dòng)更新一次 Cookie 能用挺久配合 MCP 的調(diào)用體驗(yàn)整體效率比純手動(dòng)發(fā)布高很多。如果你在配置過(guò)程中卡在某個(gè)報(bào)錯(cuò)先去接入文檔 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 對(duì)照參數(shù)再去 API Keys 頁(yè)面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 確認(rèn) Key 狀態(tài)。模型側(cè)的問(wèn)題用模型對(duì)話頁(yè)面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 單獨(dú)驗(yàn)證能快速區(qū)分是模型調(diào)用問(wèn)題還是 CSDN 接口問(wèn)題。