序
本文主要研究一下langchain4j對Model Context Protocol (MCP) 的支持
MCP
MCP協議規定了兩種傳輸方式:
- HTTP:客戶端請求一個SSE(Server-Sent Events)通道以從服務器接收事件,然后通過HTTP POST請求發送命令。這種方式適用于需要跨網絡通信的場景,通常用于分布式系統或需要高并發的場景。
- stdio:客戶端可以將MCP服務器作為本地子進程運行,并通過標準輸入/輸出直接與其通信。這種方式適用于本地集成和命令行工具,適合簡單的本地批處理任務。
如果需要讓ChatModel或AI service運行由MCP服務器提供的工具,則需要創建一個MCP tool provider
McpToolProvider
McpTransport
McpTransport transport = new StdioMcpTransport.Builder()
.command(List.of("/usr/bin/npm", "exec", "@modelcontextprotocol/server-everything@0.6.2"))
.logEvents(true) // only if you want to see the traffic in the log
.build();
對于HTTP方式則如下:
McpTransport transport = new HttpMcpTransport.Builder()
.sseUrl("http://localhost:3001/sse")
.logRequests(true) // if you want to see the traffic in the log
.logResponses(true)
.build();
McpClient
McpClient mcpClient = new DefaultMcpClient.Builder()
.transport(transport)
.logMessageHandler(new MyLogMessageHandler())
.build();
基于McpTransport創建McpClient,可以指定logMessageHandler來打印相關信息,可以通過client.listResources()、client.listResourceTemplates()來返回MCP的resources
provider
ToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build();
基于McpClient來創建McpToolProvider
AiService
Bot bot = AiServices.builder(Bot.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
AiServices.builder提供了方法來設置toolProvider
示例
public static void main(String[] args) throws Exception {
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.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)
.chatLanguageModel(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();
}
}
這里使用stdio的方式來連接docker部署的GitHub MCP server,通過chat告訴LLM去總結LangChain4j這個github倉庫最近的3個commits
返回結果示例如下:
Here are the summaries of the last three commits in the LangChain4j GitHub repository:
1. **Commit [36951f9](https://github.com/langchain4j/langchain4j/commit/36951f9649c1beacd8b9fc2d910a2e23223e0d93)** (Date: 2025-02-05)
- **Author:** Dmytro Liubarskyi
- **Message:** Updated to `upload-pages-artifact@v3`.
- **Details:** This commit updates the GitHub Action used for uploading pages artifacts to version 3.
2. **Commit [6fcd19f](https://github.com/langchain4j/langchain4j/commit/6fcd19f50c8393729a0878d6125b0bb1967ac055)** (Date: 2025-02-05)
- **Author:** Dmytro Liubarskyi
- **Message:** Updated to `checkout@v4`, `deploy-pages@v4`, and `upload-pages-artifact@v4`.
- **Details:** This commit updates multiple GitHub Actions to their version 4.
3. **Commit [2e74049](https://github.com/langchain4j/langchain4j/commit/2e740495d2aa0f16ef1c05cfcc76f91aef6f6599)** (Date: 2025-02-05)
- **Author:** Dmytro Liubarskyi
- **Message:** Updated to `setup-node@v4` and `configure-pages@v4`.
- **Details:** This commit updates the `setup-node` and `configure-pages` GitHub Actions to version 4.
All commits were made by the same author, Dmytro Liubarskyi, on the same day, focusing on updating various GitHub Actions to newer versions.
源碼
ToolProvider
langchain4j/src/main/java/dev/langchain4j/service/tool/ToolProvider.java
@FunctionalInterface
public interface ToolProvider {
/**
* Provides tools for the request to the LLM.
*
* @param request {@link ToolProviderRequest} contains {@link UserMessage} and chat memory id (see {@link MemoryId}).
* @return {@link ToolProviderResult} contains tools that should be included in the request to the LLM.
*/
ToolProviderResult provideTools(ToolProviderRequest request);
}
langchain4j定義了ToolProvider接口,每次調用AI服務時,它都會被調用,并為該次調用提供相應的工具,其provideTools返回ToolProviderResult
McpToolProvider
public class McpToolProvider implements ToolProvider {
private final List<McpClient> mcpClients;
private final boolean failIfOneServerFails;
private static final Logger log = LoggerFactory.getLogger(McpToolProvider.class);
private McpToolProvider(Builder builder) {
this.mcpClients = new ArrayList<>(builder.mcpClients);
this.failIfOneServerFails = Utils.getOrDefault(builder.failIfOneServerFails, false);
}
@Override
public ToolProviderResult provideTools(final ToolProviderRequest request) {
ToolProviderResult.Builder builder = ToolProviderResult.builder();
for (McpClient mcpClient : mcpClients) {
try {
List<ToolSpecification> toolSpecifications = mcpClient.listTools();
for (ToolSpecification toolSpecification : toolSpecifications) {
builder.add(
toolSpecification, (executionRequest, memoryId) -> mcpClient.executeTool(executionRequest));
}
} catch (Exception e) {
if (failIfOneServerFails) {
throw new RuntimeException("Failed to retrieve tools from MCP server", e);
} else {
log.warn("Failed to retrieve tools from MCP server", e);
}
}
}
return builder.build();
}
//......
}
McpToolProvider要求構造器傳入McpToolProvider.Builder,provideTools會遍歷mcpClients,之后遍歷mcpClient.listTools(),構建每個tool對應的executor(
mcpClient.executeTool(executionRequest)
)
小結
langchain4j提供了langchain4j-mcp模塊來支持MCP協議,它通過McpToolProvider來實現ToolProvider接口,以tool的方式來對接mcp。