打造一个支持MySQL查询的MCP同步插件:Java实现

打造一个支持MySQL查询的MCP同步插件:Java实现

用Java实现一个MCP本地插件,直接通过JDBC操作本地MySQL,并通过STDIO与上层MCP客户端(例如Cursor)通信。插件注册一个名为mysql

的同步工具,接收连接参数及SQL查询,执行后将结果以JSON返回。目录结构、完整代码及在Cursor中的示例配置如下。

摘要

我们基于 Model Context Protocol Java SDK 实现了一个简单的 MCP Server 插件,它:

  1. 在 Server 启动时注册一个名为 mysql 的工具(SyncToolSpecification),其参数定义包括 hostuserpassword
    databasequery 等字段。

  2. 在工具处理器中利用 MySQL 官方 JDBC 驱动(mysql-connector-java)连接数据库,执行查询,并将每行结果封装为 JSON 数组返回。

  3. 打包为可执行 JAR 后,通过命令行启动,Cursor 中配置类似于:

    json 复制代码
    {
     "mcpServers": {
       "mysql": {
         "command": "java",
         "args": ["-jar", "/Users/changmeng.yuan.o/Desktop/mysql-server-mcp-java-demo/target/mysql-server-mcp-java-demo-1.0.0.jar"],
         "env": {
           "MYSQL_HOST": "localhost",
           "MYSQL_USER": "root",
           "MYSQL_PASSWORD": "875213MenG...",
           "MYSQL_DATABASE": "test"
         }
       }
     }

}

复制代码
即可在对话中直接调用 `mysql` 工具执行任意查询并获取结果。

## 依赖

在 `pom.xml` 中声明以下关键依赖:

* **MCP 核心 SDK**(包含 STDIO Server 传输实现)

```xml
<dependency>
 <groupId>io.modelcontextprotocol.sdk</groupId>
 <artifactId>mcp</artifactId>
 <version>0.9.0</version>
</dependency>
  • MySQL JDBC 驱动

    可以用新版的 mysql-connector-j

    xml 复制代码
     <dependency>
          <groupId>com.mysql</groupId>
          <artifactId>mysql-connector-j</artifactId>
          <version>8.2.0</version>
      </dependency>

    (来自 MySQL 官方 Maven 中心)

  • Jackson 用于 JSON 序列化(可选,STDIO Transport 已自带,但我们手动构造 JSON)

    xml 复制代码
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.15.2</version>
    </dependency>

目录结构

复制代码
mcp-mysql-plugin/
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── example
        │           └── mcpmysql
        │               ├── Main.java
        │               └── MysqlTool.java
        └── resources
            └── application.properties

pom.xml

xml 复制代码
<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.codeyuan.mcpmysql</groupId>
    <artifactId>mysql-server-mcp-java-demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <!-- 指定 Java 版本 -->
        <java.version>17</java.version>
        <mcp.version>0.9.0</mcp.version>
        <jackson.version>2.15.2</jackson.version>
        <mysql.connector.version>8.2.0</mysql.connector.version>
        <!-- 用于 Maven Compiler Plugin 的 release 配置 -->
        <maven.compiler.release>${java.version}</maven.compiler.release>
    </properties>

    <dependencies>
        <!-- MCP core SDK with STDIO transport -->
        <dependency>
            <groupId>io.modelcontextprotocol.sdk</groupId>
            <artifactId>mcp</artifactId>
            <version>${mcp.version}</version>
        </dependency>
        <!-- MySQL JDBC -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>${mysql.connector.version}</version>
        </dependency>
        <!-- Jackson JSON -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.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>
                    <!-- 一次性设置 source、target 和标准库版本 -->
                    <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.codeyuan.mcpmysql.Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Main.java

java 复制代码
package com.codeyuan.mcpmysql;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;

/**
 * main主函数方法
 * @author codeyuan
 */
public class Main {
    public static void main(String[] args) throws InterruptedException {
        // 1) 使用 STDIO 传输层启动服务器
        var transportProvider = new StdioServerTransportProvider(new ObjectMapper());

        // 2) 构建并启动同步 MCP Server,启用工具执行能力
        McpSyncServer server = McpServer.sync(transportProvider)
                // 配置一下  Server 信息
                .serverInfo("mysql-plugin", "1.0.0")
                .capabilities(McpSchema.ServerCapabilities.builder()
                        // 开启工具支持
                        .tools(true)
                        .build())
                // build() 方法会立即启动服务器并监听输入:contentReference[oaicite:6]{index=6}
                .build();

        // 3) 注册自定义 MySQL 工具
        // addTool 可在运行时动态添加工具:contentReference[oaicite:7]{index=7}
        server.addTool(MysqlTool.specification());

        // 4) 在 JVM 退出时优雅关闭服务器
        // close() 用于关闭传输并释放资源:contentReference[oaicite:8]{index=8}
        Runtime.getRuntime().addShutdownHook(new Thread(server::close));

        // 5) 阻塞主线程,保持进程存活
        // join() 阻塞主线程,防止 JVM 退出:contentReference[oaicite:9]{index=9}
        Thread.currentThread().join();
    }
}

说明 :基于文档示例,使用 McpServer.sync(...) 构建同步服务器,开启 tools 功能,并注册我们自定义的工具。

MysqlTool.java

java 复制代码
package com.codeyuan.mcpmysql;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpSchema;

import java.sql.*;
import java.util.*;

/**
 * 执行连接操作mysql
 * 支持 SELECT 和非 SELECT 类型语句
 * @author codeyuan
 */
public class MysqlTool {
    private static final ObjectMapper JSON = new ObjectMapper();

    // 定义工具规格:name、description、JSON 参数 schema
    public static McpServerFeatures.SyncToolSpecification specification() {
        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("mysql", "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/%s?useSSL=false&allowMultiQueries=true", host, db);

                try (Connection conn = DriverManager.getConnection(url, user, pass);
                     Statement stmt = conn.createStatement()) {

                    if (sql.toLowerCase().startsWith("select")) {
                        try (ResultSet rs = stmt.executeQuery(sql)) {
                            List<Map<String, Object>> rows = new ArrayList<>();
                            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("rows", rows);
                            return new McpSchema.CallToolResult(String.valueOf(result), false);
                        }
                    } else {
                        int affected = stmt.executeUpdate(sql);
                        Map<String, Object> result = Map.of("affectedRows", affected);
                        return new McpSchema.CallToolResult(String.valueOf(result), false);
                    }
                }

            } catch (Exception e) {
                // 错误时将异常信息返回
                return new McpSchema.CallToolResult(String.valueOf(Map.of("error", e.getMessage())), false);
            }
        });
    }
}

application.properties

(可用于默认值配置,示例中未使用;所有参数均从工具调用时传入或环境变量读取。)

properties 复制代码
# 可在此预置 host, user, password, database 等默认值

打包与发布

bash 复制代码
# 编译并打包为 fat-jar
mvn clean package
# 生成目标: target/mcp-mysql-plugin-1.0.0.jar

将生成的 JAR 上传或放置在可访问路径,然后在 Cursor 等 MCP 客户端中配置:

json 复制代码
{
  "mcpServers": {
    "mysql": {
      "command": "java",
      "args": [
        "-jar",
        "/absolute/path/to/mcp-mysql-plugin-1.0.0.jar"
      ],
      "env": {
        "MYSQL_HOST": "localhost",
        "MYSQL_USER": "root",
        "MYSQL_PASSWORD": "secret",
        "MYSQL_DATABASE": "testdb"
      }
    }
  }
}

调用示例(在对话中):

json 复制代码
{
  "tool": "mysql",
  "arguments": {
    "host": "localhost",
    "user": "root",
    "password": "secret",
    "database": "testdb",
    "query": "SELECT * FROM users LIMIT 10"
  }
}

即可返回如下 JSON 结构:

json 复制代码
{
  "rows": [
    {
      "id": 1,
      "name": "Alice",
      "email": "alice@example.com"
    }
    ...
  ]
}

获取直接使用自然语言对话

复制代码
我是用的数据库版本是8.0,给我创建一个用户表,用户表可能存在,表中有姓名、年纪、性别、地址等字段,模拟插入10条数据

参考文档:

  1. https://modelcontextprotocol.io/sdk/java/mcp-server "MCP Server - Model Context Protocol"
  2. https://modelcontextprotocol.io/sdk/java/mcp-overview "Overview - Model Context Protocol"

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

下面关注回复【mysql-server-mcp-java-demo】

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

相关推荐
num_killer3 小时前
小白的Langchain学习
java·python·学习·langchain
期待のcode3 小时前
Java虚拟机的运行模式
java·开发语言·jvm
程序员老徐3 小时前
Tomcat源码分析三(Tomcat请求源码分析)
java·tomcat
a程序小傲4 小时前
京东Java面试被问:动态规划的状态压缩和优化技巧
java·开发语言·mysql·算法·adb·postgresql·深度优先
仙俊红4 小时前
spring的IoC(控制反转)面试题
java·后端·spring
阿湯哥4 小时前
AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南
java·人工智能·spring
小楼v4 小时前
说说常见的限流算法及如何使用Redisson实现多机限流
java·后端·redisson·限流算法
与遨游于天地4 小时前
NIO的三个组件解决三个问题
java·后端·nio
czlczl200209254 小时前
Guava Cache 原理与实战
java·后端·spring
yangminlei4 小时前
Spring 事务探秘:核心机制与应用场景解析
java·spring boot