原理讲解
基础概念
Introduction - Model Context Protocol
- MCP Host:想要通过 MCP 访问数据的程序,例如 Claude Desktop、IDE 或 AI 工具
- MCP Clients:与服务器保持 1:1 连接的协议客户端
- MCP Servers:轻量级程序,每个程序通过标准化模型上下文协议公开特定功能
- Local Data Sources: MCP 服务器可以安全访问的计算机文件、数据库和服务
- Remote Services: MCP 服务器可以通过互联网(例如,通过 API)连接的外部系统
核心组件
协议层
协议层处理消息框架,请求/响应链接和高级通信模式。
class Protocol<Request, Notification, Result> {
// Handle incoming requests, 处理请求
setRequestHandler<T>(schema: T, handler: (request: T, extra: RequestHandlerExtra) => Promise<Result>): void
// Handle incoming notifications, 处理通知, 不需要对方响应的请求
setNotificationHandler<T>(schema: T, handler: (notification: T) => Promise<void>): void
// Send requests and await responses, 请求
request<T>(request: Request, schema: T, options?: RequestOptions): Promise<T>
// Send one-way notifications, 通知
notification(notification: Notification): Promise<void>
}
传输层
运输层处理客户与服务器之间的实际通信。 MCP支持多种运输机制:
- Stdio transport
-
- 使用标准输入/输出进行通信
- 适用于本地流程的处理
- HTTP with SSE transport
-
- 使用服务器范围的事件进行服务器到客户消息
- 使用 http POST方式发送消息,用于客户到服务器消息
所有运输都使用JSON-RPC 2.0交换消息。有关模型上下文协议消息格式的详细信息,请参见规格。
消息类型
MCP具有这些主要消息类型:
- 请求期望对方有回应:
interface Request {
method: string;
params?: { ... };
}
- 结果是对请求的成功响应:
interface Result {
[key: string]: unknown;
}
- 错误表明请求失败:
interface Error {
code: number;
message: string;
data?: unknown;
}
- 通知是单向消息,不会期望回复:
interface Notification {
method: string;
params?: { ... };
}
生命周期
初始化
- 客户以协议版本和功能发送
initialize
请求 - 服务器响应其协议版本和功能
- 客户将
initialized
通知发送为确认 - 正常消息交换开始
- init client, connect server
- init model, get tools, query, tool call
- tool result -> model
- model summary
尝试使用
通过mcp工具读取本地文件内容
- clone 官方提供的mcp服务项目:
git clone git@github.com:modelcontextprotocol/servers.git
- 构建本地服务:
docker build -t mcp/filesystem -f src/filesystem/Dockerfile .
- 镜像构建完成后, 采用
LangChain4j
进行测试用例构建:
package dev.langchain4j.example.mcp;
import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.McpTransport;
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.tool.ToolProvider;
import java.io.File;
import java.util.List;
public class McpToolsExampleOverStdio {
// We will let the AI read the contents of this file
/**
* This example uses the `server-filesystem` MCP server to showcase how
* to allow an LLM to interact with the local filesystem.
* <p>
* Running this example requires npm to be installed on your machine,
* because it spawns the `server-filesystem` as a subprocess via npm:
* `npm exec @modelcontextprotocol/server-filesystem@0.6.2`.
* <p>
* Of course, feel free to swap out the server with any other MCP server.
* <p>
* The communication with the server is done directly via stdin/stdout.
* <p>
* IMPORTANT: when executing this, make sure that the working directory is
* equal to the root directory of the project
* (`langchain4j-examples/mcp-example`), otherwise the program won't be able to find
* the proper file to read. If you're working from another directory,
* adjust the path inside the StdioMcpTransport.Builder() usage in the main method.
*/
public static void main(String[] args) throws Exception {
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.baseUrl(System.getenv("OPENAI_API_URL"))
.modelName("gpt-4o-mini")
.logRequests(true)
.logResponses(true)
.build();
McpTransport transport = new StdioMcpTransport.Builder()
.command(
List.of(
"docker", "run", "-i", "--rm",
"--mount", "type=bind,src=/Users/louye/fun-project/langchain4j-examples/mcp-example/src/main/resources,dst=/projects",
"mcp/filesystem",
"/projects"
)
)
.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("修改文件 /projects/file.txt 的内容, 将内容修改为: Hello, world!");
System.out.println("RESPONSE: " + response);
} finally {
mcpClient.close();
}
}
}
参考文档: https://github.com/modelcontextprotocol/servers/blob/main/src/filesystem/README.md
实际上McpTransport
就是帮用户通过特定的命令进行启动MCP的server服务器