
从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
*/
@Slf4j
public 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: none
banner-mode: off
ai:
mcp:
server:
name: ydj-mysql-mcp-server
version: 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 {
"执行Mysql数据库操作")
(description =public String exeMysqlDB(
"mysql服务主机") String host,
(description ="用户名") String user,
(description ="密码") String password,
(description ="数据库") String database,
(description ="SQL语句") String query
(description =) {
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();
}
"为用户设置一个闹铃,返回的时间格式为ISO-DATE-TIME")
(description =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);
// getCurrentDateTime
CallToolResult res = client.callTool(new CallToolRequest("getCurrentDateTime", Map.of()));
System.out.println("获取用户所在时区的当前时间: " + res);
// setAlarm
res = client.callTool(new CallToolRequest("setAlarm",Map.of("time", "2025-05-13 21:10")));
System.out.println("设置闹钟: " + res);
// exeMysqlDB
String 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接入到大模型。赶紧动起手来😄

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