上下文协议(MCP)Java SDK 使用指南

简介: 到目前为止,我们已经具备了测试 MCP 交互和核心概念所需的全部组件。

当我们把各种内部系统、数据源、工具接入大语言模型时,往往会遇到一个尴尬的问题:每个团队、每套系统都有自己的一套“接入规范”。有的用 HTTP API,有的用消息队列,有的直接连数据库,最后一圈串下来,既难以统一治理,又很难在不同应用之间复用。这时,你可能会问:有没有一种通用的协议,既能让 AI 模型方便地调用外部工具、访问数据,又能让后端服务方用标准方式暴露能力?

Model Context Protocol(MCP)就是为此而生的标准之一,而本文要介绍的 Java SDK,则为 Java 开发者提供了一条直接接入 MCP 生态的通路。通过它,你可以用统一的模型,在 Java 应用里暴露工具、资源、提示模版,也可以轻松作为客户端去调用这些能力。本文将从整体架构讲起,一步步带你用一个可运行的示例,搭建起自己的 MCP 服务端与客户端。

  1. 概览
    随着近年来 AI 的快速发展,越来越多的工具和系统开始与 AI 模型集成。但随之而来的一个挑战是:每种集成都可能采用完全不同的标准和方式,将外部工具、资源和系统接入到 AI 模型中。

Model Context Protocol(MCP)是一个开源标准,它定义了 AI 应用(如大语言模型、图像生成模型等)与工具、数据源以及其他资源之间的集成方式。借助 MCP,AI 应用可以按外部系统约定的方式访问数据、调用工具并执行工作流。

MCP 的 Java SDK 为开发者提供了一组库,支持多种协议和通信机制,用于把 Java 应用与 AI 应用连接起来。

在本教程中,我们将一起了解这个 SDK,并通过一个简单示例来体验 MCP 的使用方式。

  1. 架构
    MCP 架构的核心组件主要包括:

• MCP Host:负责管理多个 MCP Client
• MCP Client:从 MCP Server 获取上下文,供 MCP Host 使用
• MCP Server:向 MCP Client 提供上下文信息和可调用能力
MCP 将通信划分为两个概念层次:数据层(Data Layer),用于定义客户端与服务端的通信协议和生命周期管理;以及 传输层(Transport Layer),用于定义客户端和服务端之间的具体传输通道和机制。

Java 版的 MCP SDK 将这些概念映射为如下几个层次:

• Client/Server 层:通过 McpClient / McpServer 实现并管理客户端/服务端的具体操作
• Session 层:通过 McpSession 管理通信模式和会话状态
• Transport 层:通过 McpTransport 处理消息的序列化与反序列化
客户端会调用 MCP 服务端暴露的一到多个工具(tool),而底层的通信则由传输层负责。

在 MCP 中,Primitive(原语) 是最基础的构建单元,用来定义可用的上下文信息类型以及可执行的操作范围。服务端和客户端都提供了一些原语。

服务端侧的原语包括工具(tools)、资源(resources)和提示模版(prompts)。工具是 AI 应用可以调用的可执行函数,例如查询数据库、文件操作等。资源是提供给客户端的上下文数据源,例如数据库结构、文件内容等。提示模版是可复用的模版,用于与语言模型进行交互。

客户端侧的原语则帮助 McpServer 的实现者构建更丰富的交互能力,包括采样(sampling)、信息补充(elicitation)和日志(logging)。采样允许服务端在不集成模型 SDK 的情况下,向客户端请求语言模型补全结果。 信息补充让服务端能够向用户请求额外信息或确认操作。 日志则允许服务端向客户端发送日志消息,用于调试和监控。

  1. 环境准备
    要使用 MCP Java SDK,我们需要在项目中加入 mcp 依赖:


io.modelcontextprotocol.sdk
mcp
0.15.0

3.1 定义一个 MCP 工具
我们先通过 LoggingTool 这个类,定义一个非常简单的 MCP 工具,用来打印收到的提示词(prompt),该方法返回一个 SyncToolSpecification:

public class LoggingTool {

public static McpServerFeatures.SyncToolSpecification logPromptTool() {
    McpSchema.JsonSchema inputSchema = new McpSchema.JsonSchema("object",
        Map.of("prompt", String.class), List.of("prompt"), false, null, null);

    return new McpServerFeatures.SyncToolSpecification(
        new McpSchema.Tool(
          "logPrompt", "Log Prompt","Logs a provided prompt", inputSchema, null, null, null),
            (exchange, args) -> {
                String prompt = (String) args.get("prompt");
                return McpSchema.CallToolResult.builder()
                  .content(List.of(new McpSchema.TextContent("Input Prompt: " + prompt)))
                  .isError(false)
                  .build();
            });
}

}
这里我们首先定义了输入的 JSON Schema,用来为用户输入建立一个清晰的契约。接着,使用该输入 Schema 来实例化一个 Tool,在处理逻辑中提取出 prompt 参数,并最终返回包含该 prompt 的 TextContent 结果。

  1. MCP 客户端与服务端搭建
    接下来,我们需要一个 MCP 服务端来暴露自定义工具,以及一个或多个 MCP 客户端,用于连接该服务端并调用其中的工具。

4.1 MCP 服务端实现
McpServer 具有一组能力(capabilities),用来告知客户端当前服务器支持哪些类别的协议操作,例如日志记录、提示词补全、资源访问等。此外,工具(tools)则提供给客户端可调用的具体函数。

先来看一个 McpServer 的实现:

public class McpServerApp {

public static McpSyncServer createServer() {
    JacksonMcpJsonMapper jsonMapper = new JacksonMcpJsonMapper(new ObjectMapper());
    StdioServerTransportProvider transportProvider = new StdioServerTransportProvider(
        jsonMapper);

    return McpServer.sync(transportProvider)
      .serverInfo("baeldung-demo-server", "0.0.1")
      .capabilities(McpSchema.ServerCapabilities.builder()
        .tools(true)
        .logging()
        .build())
      .tools(LoggingTool.logPromptTool())
      .build();
}

public static void main(String[] args) {
    createServer();
}

}
上面代码定义了一个同步的 McpServer,通过标准输入/输出流并使用 JSON 消息格式进行通信。随后我们声明了服务器的能力:开启工具以及日志功能(基于 SLF4J 日志框架),最后把自定义的 logPromptTool 注册到服务器上。

4.3 MCP 客户端实现
下面再定义一个简单的 McpClient,用于连接到服务端:

public class McpClientApp {

public static McpSyncClient getClient() {
    ServerParameters params = ServerParameters
      .builder("npx")
      .args("-y", "@modelcontextprotocol/server-everything")
      .build();

    JacksonMcpJsonMapper jsonMapper = new JacksonMcpJsonMapper(new ObjectMapper());
    McpClientTransport transport = new StdioClientTransport(params, jsonMapper);

    return io.modelcontextprotocol.client.McpClient.sync(transport)
     .build();

}

public static void main(String[] args) {
    McpSyncClient client = getClient();
    client.initialize();
}

}
这里我们使用 MCP 提供的示例服务端,并通过 ServerParameters 进行配置。客户端同样通过标准输入/输出流以及 JSON 消息格式与富贵论坛APP服务端进行同步通信。

  1. 测试
    到目前为止,我们已经具备了测试 MCP 交互和核心概念所需的全部组件。

5.1 测试 MCP 工具与客户端实现
我们先从测试 LoggingTool 开始,验证其输出是否正确:

@Test
void whenLogPromptToolCalled_thenReturnsResult() {
McpSchema.CallToolRequest request = new McpSchema.CallToolRequest("",
Map.of("prompt", "Unit test message"));

McpServerFeatures.SyncToolSpecification toolSpec = LoggingTool.logPromptTool();
McpSchema.CallToolResult result 
    = toolSpec.callHandler().apply(null, request);
assertNotNull(result);
assertFalse(result.isError());
assertEquals(
    "Input Prompt: Unit test message",((McpSchema.TextContent) (result.content()
    .getFirst()))
    .text());

}
在这个测试中,我们创建了一个带有 prompt 的 CallToolRequest,并将其传递给 LoggingTool 的 SyncToolSpecification。随后,我们断言返回结果非空、无错误,并且返回的文本内容与预期相同。

接下来,我们再通过 MCP 提供的示例服务端来测试 McpClient:

@Test
void whenCalledViaClient_thenReturnsLoggedResult() {
McpSchema.CallToolRequest request = new McpSchema.CallToolRequest(
"echo", Map.of("message", "Client-server test message"));
McpSchema.CallToolResult result = client.callTool(request);

assertNotNull(result);
assertNull(result.isError());
assertEquals("Echo: Client-server test message",
    ((McpSchema.TextContent) (result.content()
    .getFirst())).text());

}
MCP 示例服务端暴露了一个名为 echo 的工具,它会把输入消息原样返回,这与我们之前实现的 LoggingTool 的行为类似。

5.2 测试本地服务端
最后,我们来测试自己编写的本地服务端。为此需要定义一个单独的 McpClient,并使用指向本地 JAR 的不同服务端参数:

public class McpClientApp2 {

private static final Logger log = LoggerFactory.getLogger(McpClientApp2.class);

public static void main(String[] args) {
    String jarPath = new java.io.File("java-mcp/target/java-mcp-1.0.0-SNAPSHOT.jar")
                         .getAbsolutePath();
    ServerParameters params = ServerParameters.builder("java")
        .args("-jar", jarPath)
        .build();

    JacksonMcpJsonMapper jsonMapper = new JacksonMcpJsonMapper(new ObjectMapper());
    McpClientTransport transport = new StdioClientTransport(params, jsonMapper);

    McpSyncClient client = McpClient.sync(transport)
        .build();

    client.initialize();

    ListToolsResult tools = client.listTools();
    McpClientApp2.log.info("Tools exposed by the server:");
    tools
      .tools()
      .forEach(tool -> System.out.println(" - " + tool.name()));

    McpClientApp2.log.info("\nCalling 'logPrompt' tool...");
    CallToolResult result = client.callTool(
      new CallToolRequest("logPrompt", Map.of("prompt", "Hello from MCP client!")));
    McpClientApp2.log.info("Result: " + result.content());

    client.closeGracefully();
}

}
运行该客户端后,我们可以通过日志来验证它是否成功连接到了本地 JAR 中定义的服务端:

14:04:27.879 [boundedElastic-1] INFO i.m.c.transport.StdioClientTransport - MCP server starting.
14:04:27.920 [boundedElastic-1] INFO i.m.c.transport.StdioClientTransport - MCP server started
14:04:28.517 [pool-4-thread-1] INFO i.m.c.transport.StdioClientTransport - STDERR Message received: 14:04:28.504 [pool-1-thread-1] INFO i.m.server.McpAsyncServer - Client initialize request - Protocol: 2024-11-05, Capabilities: ClientCapabilities[experimental=null, roots=null, sampling=null, elicitation=null], Info: Implementation[name=Java SDK MCP Client, title=null, version=0.15.0]
14:04:28.575 [pool-1-thread-1] INFO i.m.client.LifecycleInitializer - Server response with Protocol: 2024-11-05, Capabilities: ServerCapabilities[completions=null, experimental=null, logging=LoggingCapabilities[], prompts=null, resources=null, tools=ToolCapabilities[listChanged=true]], Info: Implementation[name=baeldung-demo-server, title=null, version=0.0.1] and Instructions null
14:04:28.626 [main] INFO mcp.McpClientApp2 - Tools exposed by the server:
14:04:28.626 [main] INFO mcp.McpClientApp2 -
Calling 'logPrompt' tool...

  • logPrompt
    14:04:28.671 [main] INFO mcp.McpClientApp2 - Result: [TextContent[annotations=null, text=Input Prompt: Hello from MCP client!, meta=null]]
    14:04:28.784 [ForkJoinPool.commonPool-worker-1] WARN i.m.c.transport.StdioClientTransport - Process terminated with code 143

Process finished with exit code 0
从日志可以看到,首先 McpServer 启动成功,随后客户端完成初始化并与服务端建立连接。接着客户端请求列出服务端暴露的工具,最后在调用 logPrompt 工具之后,我们也看到了来自服务端的返回结果。

  1. 总结
    在本文中,我们首先回顾了 MCP 及其 Java SDK 的整体架构,重点介绍了 McpServer、McpClient 和 McpHost 之间的关系,以及由 McpTransport 负责的传输层细节。

接着,我们了解了 MCP 中的各种原语(primitives),以及在服务端和客户端侧分别可用的类型和能力。

最后,我们实现了一个简单的 MCP 工具,并通过 MCP 示例服务端和本地自建服务端,验证了 McpClient 的连接与调用流程。借助 MCP 标准和 Java SDK,你可以更方便地把现有系统能力以统一的方式暴露给 AI 应用,也能在不同项目之间复用这套集成模式。

相关文章
|
2天前
|
人工智能 API 开发工具
Claude Code国内安装:2026最新保姆教程(附cc-switch配置)
Claude Code是我目前最推荐的AI编程工具,没有之一。 它可能不是最简单的,但绝对是上限最高的。一旦跑通安装、接上模型、定好规范,你会发现很多原本需要几小时的工作,现在几分钟就能搞定。 这套方案的核心优势就三个字:可控性。你不用依赖任何不稳定服务,所有组件都在自己手里。模型效果不好?换一个。框架更新了?自己决定升不升。 这才是AI时代开发者该有的姿势——不是被动等喂饭,而是主动搭建自己的生产力基础设施。 希望这篇保姆教程,能帮你顺利上车。做出你自己的作品。
Claude Code国内安装:2026最新保姆教程(附cc-switch配置)
|
9天前
|
缓存 人工智能 自然语言处理
我对比了8个Claude API中转站,踩了不少坑,总结给你
本文是个人开发者耗时1周实测的8大Claude中转平台横向评测,聚焦Claude Code真实体验:以加权均价(¥/M token)、内部汇率、缓存支持、模型真实性及稳定性为核心指标。
3815 21
|
5天前
|
人工智能 JSON BI
DeepSeek V4 来了!超越 Claude Sonnet 4.5,赶紧对接 Claude Code 体验一把
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro 的真实体验与避坑记录 本文记录我将 Claude Code 对接 DeepSeek 最新模型(V4Pro)后的真实体验,测试了 Skills 自动化查询和积木报表 AI 建表两个场景——有惊喜,也踩
2391 8
|
4天前
|
人工智能 缓存 BI
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro,跑完 Skills —— OA 审批、大屏、报表、部署 5 大实战场景后的真实体验 ![](https://oscimg.oschina.net/oscnet/up608d34aeb6bafc47f
2002 4
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
|
21天前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
18905 60
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
2天前
|
SQL 人工智能 弹性计算
阿里云发布 Agentic NDR,威胁检测与响应进入智能体时代
欢迎前往阿里云云防火墙控制台体验!
1168 2

热门文章

最新文章