
从MCP(1)应用:通过mcp在大模型中关联应用私域数据库已可直观地感受到效果,现在来瞅瞅这个MCP工具的源代码——如何开发自己的MCP工具。简直太......请观后自品😄
回想一下,在没有 MCP 之前我们是怎么做的呢?我们多半是人工编辑描述好这些可能LLM所需要的信息——即上下文,然后粘贴给到它。
而现在,可由 MCP 这一充当着 AI 模型的"万能转接头"来帮 LLM 能自动获取数据或调用其它工具。
相信我:不久,在某些业务场景功能模块下,我们的后端应用服务中就要多出一份接口——它是MCP协议的(好比xml的webservice、json的RESTful、RPC)!
在MCP官网https://modelcontextprotocol.io有关示例基本是用TypeScript、Python实现的。现分别用MCP原生Java SDK和Spring AI实现。

一、使用MCP原生Java SDK实现
感兴趣可以看看SDK源码,代码量并不大:
https://github.com/modelcontextprotocol/java-sdk
1.1、工程结构如下

😄也就两个类,总共代码不足100行。
1.2、具体代码
- 依赖pom配置:
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.ydj</groupId><artifactId>ydj-kits-mysql4mcp</artifactId><version>1.0-SNAPSHOT</version><properties><java.version>17</java.version><mcp.version>0.10.0</mcp.version><jackson.version>2.18.3</jackson.version><lombok.version>1.18.36</lombok.version><mysql.connector.version>9.1.0</mysql.connector.version><maven.compiler.release>${java.version}</maven.compiler.release></properties><dependencies><dependency><groupId>io.modelcontextprotocol.sdk</groupId><artifactId>mcp</artifactId><version>${mcp.version}</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql.connector.version}</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>${jackson.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency></dependencies><build><plugins><!-- 1. Maven Compiler: 指定 Java 版本,支持文本块 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.14.0</version><configuration><release>${maven.compiler.release}</release></configuration></plugin><!-- 2. Maven Shade: 打包为 fat-jar --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.5.0</version><executions><execution><phase>package</phase><goals><goal>shade</goal></goals><configuration><!-- 去除依赖中的签名文件等,避免冲突 --><filters><filter><artifact>*:*</artifact><excludes><exclude>META-INF/*.SF</exclude><exclude>META-INF/*.DSA</exclude><exclude>META-INF/*.RSA</exclude></excludes></filter></filters><!-- 指定主类 --><transformers><transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"><mainClass>com.ydj.kits.mcp.mysql.kits.mcp.mysql.MySqlMcpServerApplication</mainClass></transformer></transformers></configuration></execution></executions></plugin></plugins></build></project>
重点注意:打包方式以及要指定主类入口。
- 入口main方法类:
/*** @Desc : 注册工具&启动监听服务* @Author : ydj* @Date : 2025/5/12*/public class MySqlMcpServerApplication {public static void main(String[] args) throws InterruptedException {//1) 使用 STDIO 传输层启动服务器var transportProvider = new StdioServerTransportProvider(new ObjectMapper());//2) 构建并启动同步 MCP Server,启用工具执行能力var server = McpServer.sync(transportProvider).serverInfo("ydj-kits-mysql4mcp", "1.0.0").capabilities(McpSchema.ServerCapabilities.builder().tools(true).build()).build();//3) 注册自定义 MySQL 工具:addTool 可在运行时动态添加工具server.addTool(ExeSqlOnMysqlService.exeMysqlDB());//4) 在 JVM 退出时优雅关闭服务器:close() 用于关闭传输并释放资源Runtime.getRuntime().addShutdownHook(new Thread(server::close));//5) 阻塞主线程,保持进程存活:join() 阻塞主线程,防止 JVM 退出Thread.currentThread().join();}}
- MySql工具能力服务实现类:
/*** @Desc : 执行连接操作mysql* @Author : ydj* @Date : 2025/5/12*/@Slf4jpublic class ExeSqlOnMysqlService {public static McpServerFeatures.SyncToolSpecification exeMysqlDB() {String schema = """{"type": "object","properties": {"host": { "type": "string" },"user": { "type": "string" },"password": { "type": "string" },"database": { "type": "string" },"query": { "type": "string" }},"required": ["host","user","password","database","query"]}""";McpSchema.Tool tool = new McpSchema.Tool("ydj-kits-mysql4mcp", "Execute SQL on MySQL", schema);return new McpServerFeatures.SyncToolSpecification(tool, (exchange, arguments) -> {try {String host = arguments.get("host").toString();String user = arguments.get("user").toString();String pass = arguments.get("password").toString();String db = arguments.get("database").toString();String sql = arguments.get("query").toString().trim();String url = String.format("jdbc:mysql://%s:3306/%s?useSSL=false&allowMultiQueries=true", host, db);log.info("host:{},user:{},db:{},sql:{}", host, user, db, sql);try (Connection conn = DriverManager.getConnection(url, user, pass);Statement stmt = conn.createStatement()) {if (sql.toLowerCase().startsWith("select")) {List<Map<String, Object>> rows = new ArrayList<>();try (ResultSet rs = stmt.executeQuery(sql)) {ResultSetMetaData meta = rs.getMetaData();int colCount = meta.getColumnCount();while (rs.next()) {Map<String, Object> row = new LinkedHashMap<>();for (int i = 1; i <= colCount; i++) {row.put(meta.getColumnLabel(i), rs.getObject(i));}rows.add(row);}}Map<String, Object> result = Map.of("Result Rows", rows);log.info("result:{}", result);return new McpSchema.CallToolResult(String.valueOf(result), false);}int affected = stmt.executeUpdate(sql);Map<String, Object> result = Map.of("Affected Rows", affected);log.info("result:{}", result);return new McpSchema.CallToolResult(String.valueOf(result), false);}} catch (Exception e) {log.error("error:{}", e.getMessage());return new McpSchema.CallToolResult(String.valueOf(Map.of("Error", e.getMessage())), true);}});}}
主要是定义工具规格:name、description、JSON参数schema(即与LLM进行沟通的Prompt,很重要)。至于Mysql具体处理那块就没什么特殊的了。
如上,代码实际很简单!然后正常打出的jar包即可在支持MCP的LLM工具中配置使用,具体如何配置请参见第一篇MCP(1)应用:通过mcp在大模型中关联应用私域数据库。

二、使用Spring AI实现
Spring在其SDK的基础之上进行了封装,因此开发起来更简单,自然毋庸置疑最后打出来jar包也会大很多。
2.1、工程结构如下

😄同样也就两个类,代码更少更简单(DateTimeService是用来测试发布多tool能力)。
2.2、具体代码
- 依赖pom配置:
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.4</version></parent><groupId>com.ydj</groupId><artifactId>mcp-demo-mysql-springai</artifactId><version>1.0-SNAPSHOT</version><properties><java.version>17</java.version><spring-ai.version>1.0.0</spring-ai.version><main.class>com.ydj.kits.mcp.mysql.StdioServerApplication</main.class><maven.compiler.release>${java.version}</maven.compiler.release></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>${spring-ai.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-mcp-server</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- MySQL JDBC --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!-- Jackson JSON --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>
如临时版本可能Maven中央私服仓库中下载不下来,则可以在Maven settings文件中添加阿里镜像仓库:
<mirrors><mirror><id>aliyunmaven</id><mirrorOf>*,!spring-milestones</mirrorOf><name>阿里云公共仓库</name><url>https://maven.aliyun.com/repository/public</url></mirror></mirrors><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/milestone</url><snapshots><enabled>false</enabled></snapshots></repository><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>false</enabled></releases></repository><repository><id>central-portal-snapshots</id><name>Central Portal Snapshots</name><url>https://central.sonatype.com/repository/maven-snapshots/</url><releases><enabled>false</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories>
- Spring配置信息application.yml:
spring:main:web-application-type: nonebanner-mode: offai:mcp:server:name: ydj-mysql-mcp-serverversion: 0.0.1
- 入口main方法类:
/*** @Desc : 启动类* @Author : ydj* @Date : 2025/5/12*/public class StdioServerApplication {public static void main(String[] args) {SpringApplication.run(StdioServerApplication.class, args);}}
- StdioServer工具装配类:
public class StdioServerConfig {public ToolCallbackProvider tools(DateTimeService dateTimeService, ExeSqlOnMysqlService exeSqlOnMysqlService) {return MethodToolCallbackProvider.builder().toolObjects(dateTimeService, exeSqlOnMysqlService).build();}}
- MySql工具能力服务实现类:
public class ExeSqlOnMysqlService {(description = "执行Mysql数据库操作")public String exeMysqlDB((description = "mysql服务主机") String host,(description = "用户名") String user,(description = "密码") String password,(description = "数据库") String database,(description = "SQL语句") String query) {log.info("host:{},user:{},db:{},sql:{}", host, user, database, query);String url = String.format("jdbc:mysql://%s:3306/%s?useSSL=false&allowMultiQueries=true", host, database);try (Connection conn = DriverManager.getConnection(url, user, password);Statement stmt = conn.createStatement()) {if (query.toLowerCase().startsWith("select")) {List<Map<String, Object>> rows = new ArrayList<>();try (ResultSet rs = stmt.executeQuery(query)) {ResultSetMetaData meta = rs.getMetaData();int colCount = meta.getColumnCount();while (rs.next()) {Map<String, Object> row = new LinkedHashMap<>();for (int i = 1; i <= colCount; i++) {row.put(meta.getColumnLabel(i), rs.getObject(i));}rows.add(row);}}Map<String, Object> result = Map.of("Result Rows", rows);log.info("result:{}", result);return result.toString();}int affected = stmt.executeUpdate(query);Map<String, Object> result = Map.of("Affected Rows", affected);log.info("result:{}", result);return result.toString();} catch (Exception e) {log.error("error:{}", e.getMessage());return Map.of("Error", e.getMessage()).toString();}}}
- 附加多Tool能力测试类:
public class DateTimeService {(description = "获取用户所在时区的当前时间")String getCurrentDateTime() {return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();}(description = "为用户设置一个闹铃,返回的时间格式为ISO-DATE-TIME")String setAlarm(String time) {LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);return alarmTime + "的闹铃已经设置完成";}}
如上,代码更简单!然后正常打出的jar包即可在支持MCP的LLM工具中配置使用,具体如何配置请参见第一篇MCP(1)应用:通过mcp在大模型中关联应用私域数据库。

三、思考总结
3.1、模拟测试验证
整体来说,两种实现方式都很简单。如上两种方式均可以使用如下代码进行本地验证测试:
public class ClientStdioTest {public static void main(String[] args) {var stdioParams = ServerParameters.builder("D:/Program Files/Java/jdk-17.0.4.1/bin/java").args("-jar","-Dspring.ai.mcp.server.stdio=true","-Dspring.main.web-application-type=none","-Dlogging.pattern.console=","D:\IdeaProjects\LLM\mcp-demo-mysql-springai\target\mcp-demo-mysql-springai-1.0-SNAPSHOT.jar").build();var transport = new StdioClientTransport(stdioParams);var client = McpClient.sync(transport).build();client.initialize();// 列出并展示可用的工具ListToolsResult toolsList = client.listTools();System.out.println("可用工具 = " + toolsList);// getCurrentDateTimeCallToolResult res = client.callTool(new CallToolRequest("getCurrentDateTime", Map.of()));System.out.println("获取用户所在时区的当前时间: " + res);// setAlarmres = client.callTool(new CallToolRequest("setAlarm",Map.of("time", "2025-05-13 21:10")));System.out.println("设置闹钟: " + res);// exeMysqlDBString sql = "select id,dep_name,dep_code from ydj_dep";res = client.callTool(new CallToolRequest("exeMysqlDB",Map.of("host", "192.1.2.5","user", "xxxx","password", "xxxxxxxx","database", "ydj_test","query", sql)));System.out.printf("执行SQL = [%s]结果: %s%n", sql, res);client.closeGracefully();}}
如上使用stdio传输,MCP服务器由客户端自动启动,注意需要先构建服务器jar(即上述代码中位置jar包需先打包好)。
3.2、基本运作流程&原理

- MCP Client首先从MCP Server获取可用的工具列表;
- 将用户的Query连同工具描述通过Function Calling一起发送给 LLM;
- LLM 决定是否需要使用工具以及使用哪些工具;
- 如果需要使用工具,MCP Client会通过 MCP Server执行相应的工具调用;
- 工具调用的结果会被发送回LLM;
- LLM基于所有信息生成自然语言响应;
- 最后将响应展示给用户。
MCP Server 是由 LLM 主动选择并调用的。有意思的是 LLM 具体是如何确定该使用哪些工具呢?以及是否会使用一些不存在的工具呢(幻觉)?
参见官网:
https://modelcontextprotocol.io/quickstart/server#what%E2%80%99s-happening-under-the-hood
3.3、对MCP发展的展望
- 新协议服务能力——MCP将成为软件对外开放和集成的新协议,SaaS、应用服务将会以MCP方式提供对外服务(如百度已提供MCP协议地图接口)。
- 产品交互重构——重构应用形态,智能助手将以“搜索框”的形式成为各个应用的交互入口,支持语音及自然语言交换形式,完成复杂任务的执行。
多出一份协议接口,业务核心能力复用,分分钟把原应用服务通过MCP接入到大模型。赶紧动起手来😄

本篇文章来源于微信公众号: 梦语路
文章评论