LangChain4j + MCP:讓你的 AI 輕松調(diào)用外部工具(內(nèi)附GitHub-MCP實(shí)戰(zhàn))

本文已收錄在Github關(guān)注我,緊跟本系列專欄文章,咱們下篇再續(xù)!

  • ?? 魔都架構(gòu)師 | 全網(wǎng)30W技術(shù)追隨者
  • ?? 大廠分布式系統(tǒng)/數(shù)據(jù)中臺實(shí)戰(zhàn)專家
  • ?? 主導(dǎo)交易系統(tǒng)百萬級流量調(diào)優(yōu) & 車聯(lián)網(wǎng)平臺架構(gòu)
  • ?? AIGC應(yīng)用開發(fā)先行者 | 區(qū)塊鏈落地實(shí)踐者
  • ?? 以技術(shù)驅(qū)動創(chuàng)新,我們的征途是改變世界!
  • ?? 實(shí)戰(zhàn)干貨:編程嚴(yán)選網(wǎng)

0 前言

LangChain4j 支持模型上下文協(xié)議(MCP),用于與符合 MCP 標(biāo)準(zhǔn)的服務(wù)器通信,從而調(diào)用并執(zhí)行工具。

該協(xié)議支持兩種通信方式,LangChain4j 均已支持:

  • HTTP 模式:客戶端通過 SSE 通道接收服務(wù)端事件,并通過 HTTP POST 請求發(fā)指令
  • stdio 模式:客戶端可將 MCP 服務(wù)器作為本地子進(jìn)程運(yùn)行,并通過標(biāo)準(zhǔn)輸入/輸出與其通信

想讓聊天模型或 AI 服務(wù)使用 MCP 服務(wù)器提供的工具,先得創(chuàng)建一個 MCP 工具提供者實(shí)例。

1 創(chuàng)建 MCP 工具提供者(MCP tool provider)

1.1 MCP通信方式

先要構(gòu)建一個 MCP 通信方式的實(shí)例。

① stdio

以本地啟動 NPM 包為例:

McpTransport transport = new StdioMcpTransport.Builder()
    .command(List.of("/usr/bin/npm", "exec", "@modelcontextprotocol/server-everything@0.6.2"))
    .logEvents(true) // 開啟日志記錄(可選)
    .build();

② HTTP

需要兩個 URL:

  • 一個用于啟動 SSE channel
  • 另一個用于通過 POST 提交命令:
McpTransport transport = new HttpMcpTransport.Builder()
    .sseUrl("http://localhost:3001/sse") // SSE 事件channel地址
    .logRequests(true) // 開啟請求日志
    .logResponses(true) // 開啟響應(yīng)日志
    .build();

1.2 創(chuàng)建 MCP 客戶端

代表可以通過給定的傳輸協(xié)議,使用服務(wù)器檢索和執(zhí)行工具的客戶端,該客戶端可以與MCP服務(wù)器通信。

使用 transport 實(shí)例創(chuàng)建 MCP 客戶端:

McpClient mcpClient = new DefaultMcpClient.Builder()
    .transport(transport)
    .build();

1.3 創(chuàng)建 MCP 工具提供者

工具提供者。每次調(diào)用AI服務(wù)并為該特定調(diào)用提供工具時,都會調(diào)用它。 toolproviderresult中返回的工具將包含在對LLM的請求中。

使用 MCP 客戶端創(chuàng)建工具提供者:

ToolProvider toolProvider = McpToolProvider.builder()
    .mcpClients(List.of(mcpClient))
    .build();

一個 MCP 工具提供者可同時用多個 MCP 客戶端。如需自定義在連接某個服務(wù)器失敗時行為,可 builder.failIfOneServerFails(boolean) 設(shè)置:

  • 默認(rèn) false:忽略單個服務(wù)器失敗,繼續(xù)使用其他服務(wù)器
  • 若置 true:任一服務(wù)器失敗都會導(dǎo)致整個工具提供者拋異常

將工具提供者綁定到 AI 服務(wù)中,只需在構(gòu)建 AI 服務(wù)時傳入:

Bot bot = AiServices.builder(Bot.class)
    .chatModel(model)
    .toolProvider(toolProvider)
    .build();

2 日志功能

MCP 協(xié)議支持服務(wù)端向客戶端發(fā)送日志消息。默認(rèn),客戶端會將這些日志轉(zhuǎn)為 SLF4J 格式輸出。如想自定義日志處理邏輯,可實(shí)現(xiàn) dev.langchain4j.mcp.client.logging.McpLogMessageHandler 接口,并傳入客戶端構(gòu)造器:

McpClient mcpClient = new DefaultMcpClient.Builder()
    .transport(transport)
    .logMessageHandler(new MyLogMessageHandler()) // 自定義日志處理器
    .build();

3 資源操作

獲取服務(wù)器上的 MCP 資源,使用:

  • client.listResources():返回 McpResource 列表,包含資源元數(shù)據(jù)及 URI
  • client.listResourceTemplates():獲取資源模板

獲取資源具體內(nèi)容時,用client.readResource(uri),傳入資源 URI,返回 McpReadResourceResult,其中包含一個或多個 McpResourceContents

  • McpBlobResourceContents:二進(jìn)制資源
  • McpTextResourceContents:文本資源

4 提示詞操作(Prompts)

獲取服務(wù)器上定義的MCP 提示詞,用:

  • client.listPrompts():返回提示詞 McpPrompt 列表,包含名稱和參數(shù)信息
  • client.getPrompt(name, arguments):渲染具體提示詞內(nèi)容,返回一組 McpPromptMessage,包含角色(如 userassistant)和消息內(nèi)容

當(dāng)前支持的消息內(nèi)容類型包括:

  • McpTextContent:文本
  • McpImageContent:圖像
  • McpEmbeddedResource:嵌入資源

提示詞消息可用 McpPromptMessage.toChatMessage() 轉(zhuǎn)為通用的 LangChain4j 消息類型 ChatMessage,但需滿足:

  • roleassistant 時,內(nèi)容須是文本,否則會拋異常
  • 包含二進(jìn)制內(nèi)容的消息無法轉(zhuǎn)換

5 使用 Docker 運(yùn)行 GitHub MCP 服務(wù)器

看一個通過 MCP 協(xié)議連接 GitHub 的示例。目標(biāo)是用 LangChain4j 和 MCP 客戶端獲取并總結(jié) GitHub 上公開倉庫的最新提交信息。

通過 MCP 提供的 GitHub 服務(wù)器實(shí)現(xiàn)(見 MCP GitHub 倉庫),通過 Docker 本地運(yùn)行。

構(gòu)建 Docker 鏡像

先克隆或下載 MCP GitHub 服務(wù)器源碼,進(jìn)入根目錄,執(zhí)行以下命令構(gòu)建鏡像:

docker build -t mcp/github -f Dockerfile .

構(gòu)建完成后,本地會生成 mcp/github 鏡像:

docker image ls

REPOSITORY   TAG         IMAGE ID        SIZE
mcp/github   latest      b141704170b1    173MB

6 開發(fā)工具提供者代碼示例

創(chuàng)建 Java 類 McpGithubToolsExample,使用 LangChain4j 連接 GitHub MCP 服務(wù)器,執(zhí)行以下操作:

  • 啟動 Docker 容器運(yùn)行 GitHub MCP 服務(wù)器
  • 使用 stdio 通信方式連接 MCP 服務(wù)器
  • 使用語言模型總結(jié) LangChain4j 倉庫最近 3 次提交信息

?? 提示:下面代碼中通過環(huán)境變量 GITHUB_PERSONAL_ACCESS_TOKEN 傳入 GitHub Token,訪問公共倉庫時可選。

獲取GITHUB_PERSONAL_ACCESS_TOKEN

直達(dá):https://github.com/settings/personal-access-tokens/new

自己保存好:

構(gòu)建好的鏡像:

docker run --rm -d \
  --name mcp-github-server \
  -e GITHUB_PERSONAL_ACCESS_TOKEN=token \
  mcp/github

啟動成功:

public static void main(String[] args) throws Exception {

    ChatLanguageModel model = OllamaChatModel.builder()
        .baseUrl("http://localhost:11434") // Ollama 默認(rèn)本地服務(wù)地址
        .modelName("llama3-groq-tool-use:8b") // 你本地 Ollama 拉取的模型名稱
        .logRequests(true)
        .logResponses(true)
        .build();

    McpTransport transport = new StdioMcpTransport.Builder()
        .command(List.of("/usr/local/bin/docker", "run", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "-i", "mcp/github"))
        .logEvents(true)
        .build();

    McpClient mcpClient = new DefaultMcpClient.Builder()
        .transport(transport)
        .build();

    ToolProvider toolProvider = McpToolProvider.builder()
        .mcpClients(List.of(mcpClient))
        .build();

    Bot bot = AiServices.builder(Bot.class)
        .chatModel(model)
        .toolProvider(toolProvider)
        .build();

    try {
        String response = bot.chat("Summarize the last 3 commits of the LangChain4j GitHub repository");
        System.out.println("RESPONSE: " + response);
    } finally {
        mcpClient.close();
    }
}

7 執(zhí)行示例代碼

運(yùn)行 Java 應(yīng)用后,收到類似輸出,總結(jié) LangChain4j 倉庫最近 3 次提交內(nèi)容:

以下是 LangChain4j GitHub 倉庫最近三次提交的摘要:

1. **提交 [36951f9](https://github.com/langchain4j/langchain4j/commit/36951f9649c1beacd8b9fc2d910a2e23223e0d93)**(時間:2025-02-05)
   - **作者:** Dmytro Liubarskyi
   - **信息:** 更新至 `upload-pages-artifact@v3`
   - **詳情:** 此提交將上傳頁面資源的 GitHub Action 升級至版本 3。

2. **提交 [6fcd19f](https://github.com/langchain4j/langchain4j/commit/6fcd19f50c8393729a0878d6125b0bb1967ac055)**(時間:2025-02-05)
   - **作者:** Dmytro Liubarskyi
   - **信息:** 更新至 `checkout@v4`、`deploy-pages@v4` 和 `upload-pages-artifact@v4`
   - **詳情:** 此提交升級了多個 GitHub Action 到版本 4。

3. **提交 [2e74049](https://github.com/langchain4j/langchain4j/commit/2e740495d2aa0f16ef1c05cfcc76f91aef6f6599)**(時間:2025-02-05)
   - **作者:** Dmytro Liubarskyi
   - **信息:** 更新至 `setup-node@v4` 和 `configure-pages@v4`
   - **詳情:** 此提交將相關(guān) GitHub Action 升級至版本 4。

這三次提交都由 Dmytro Liubarskyi 完成,時間相同,主要內(nèi)容為將 GitHub Actions 升級至新版。

本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內(nèi)容