打造一个支持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": "[email protected]"
    }
    ...
  ]
}

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

复制代码
我是用的数据库版本是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】

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

相关推荐
星辰离彬几秒前
数据库优化技巧:MySQL 重复数据查询与删除(仅保留一条)的性能优化策略
数据库·mysql
小邓是个人才呀1 小时前
第二章:Android常用UI控件
android·java·ui
68岁扶墙肾透1 小时前
Java安全-Servlet内存马
java·安全·web安全·网络安全·系统安全·网络攻击模型
码农爱java1 小时前
Elasticsearch 深入分析三种分页查询【Elasticsearch 深度分页】
java·大数据·spring boot·后端·elasticsearch·全文检索
_extraordinary_2 小时前
Java 继承
java·开发语言·继承
小鹭同学_2 小时前
Java基础 Day17
java·开发语言
设计师小聂!2 小时前
Spring ---IOC容器和DI的具体应用
java·后端·spring
徐子宸3 小时前
docker面试题(4)
java·spring cloud·docker
潇凝子潇3 小时前
IntelliJ IDEA设置编码集
java·ide·intellij-idea
❀͜͡傀儡师3 小时前
IntelliJ IDEA 接入 DeepSeek帮助你更好编码
java·ide·intellij-idea