Browse Source

初始化mcp服务

master
张献维 1 month ago
commit
16a45de488
  1. 4
      .gitignore
  2. 16
      .goreleaser.yaml
  3. 1370
      GitHub - dm-mcp-server.u.zip.html
  4. 102
      README.md
  5. 1
      build.bat
  6. 1
      build.sh
  7. 16
      go.mod
  8. 90
      main.go
  9. 73
      tools/describe_table.go
  10. 88
      tools/execute_query.go
  11. 53
      tools/list_tables.go
  12. 73
      utils/config.go
  13. 26
      utils/db.go

4
.gitignore

@ -0,0 +1,4 @@
# 忽略 .idea 目录
.idea/
/go.sum
/dist/

16
.goreleaser.yaml

@ -0,0 +1,16 @@
project_name: go-markdown
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
# - darwin
- windows
goarch:
- "amd64"
# - "arm64"
# - "386"
release:
github:
owner: https://github.com/vivimanZhang/go-markdown.git
name: VivimanZhang

1370
GitHub - dm-mcp-server.u.zip.html

File diff suppressed because one or more lines are too long

102
README.md

@ -0,0 +1,102 @@
---
# 达梦数据库 MCP 服务
## 项目简介
本项目是一个基于达梦数据库的 MCP(Microservice Communication Protocol)服务,提供了以下功能:
- **列出数据库中的表**:通过 `list_tables` 工具列出指定数据库中的所有表。
- **执行只读 SQL 查询**:通过 `execute_query` 工具执行只读 SQL 查询(仅支持 `SELECT`、`SHOW`、`DESCRIBE` 和 `EXPLAIN` 语句)。
- **显示表结构**:通过 `describe_table` 工具显示指定表的结构。
## 使用方法
windows 下可以直接下载 dm-mcp-server 二进制文件,并添加到 PATH 中。
### 使用 go install 安装
```bash
go install github.com/wangzhaobo168/dm-mcp-server
```
### 达梦MCP Server 配置示例:
```
{
"mcpServers": {
"db-mcp-server": {
"command": "dm-mcp-server",
"args": [
],
"env": {
"DM_PORT": "端口号,默认5236",
"DM_HOST": "主机地址",
"DM_USERNAME": "账号",
"DM_PASSWORD": "密码",
"DM_SCHEMA": "模式名"
}
}
}
```
## 检查 dm-mcp-server 版本:
```bash
dm-mcp-server --version
```
## 快速开始
### 1. 环境要求
- **Go 版本**:1.16 或更高版本。
- **达梦数据库**:已安装并配置好达梦数据库。
- **依赖库**:确保安装了 `github.com/mark3labs/mcp-go` 库。
### 2. 安装依赖
在项目根目录下运行以下命令初始化 Go Modules 并下载依赖:
```bash
go mod init dm-mcp-server
go mod tidy
```
### 3. 配置数据库连接
在运行服务之前,需要配置达梦数据库的连接信息。可以通过以下方式设置:
- **命令行参数**:启动服务时通过命令行参数指定。
- **环境变量**:通过环境变量设置。
#### 命令行参数
```bash
go run main.go \
-username <用户名> \
-password <密码> \
-host <主机地址> \
-port <端口> \
-schema <模式名>
```
## 项目结构
```
dm-mcp-server/
├── main.go # 主程序入口
├── tools/ # 工具实现
│ ├── list_tables.go # 列出表工具
│ ├── execute_query.go # 执行查询工具
│ └── describe_table.go # 显示表结构工具
├── utils/ # 工具函数
│ ├── config.go # 配置管理
│ └── db.go # 数据库连接
├── go.mod # Go Modules 文件
└── README.md # 项目文档
```
## 贡献指南
欢迎提交 Issue 和 Pull Request 来改进本项目。请确保代码风格一致,并通过测试。
## 许可证
本项目采用 [MIT 许可证](LICENSE)。
---
### 补充说明
- 如果 `github.com/mark3labs/mcp-go` 库不可用,请替换为其他兼容的 MCP 实现。
- 如果需要支持更多数据库操作,可以在 `tools` 包中添加新的工具。
---

1
build.bat

@ -0,0 +1 @@
rd /s /q dist && goreleaser release --snapshot

1
build.sh

@ -0,0 +1 @@
rm -rf dist ; goreleaser release --snapshot

16
go.mod

@ -0,0 +1,16 @@
module github.com/wangzhaobo168/dm-mcp-server
go 1.23.4
require (
github.com/mark3labs/mcp-go v0.23.1
github.com/wangzhaobo168/dm v0.0.0-20250425092535-276eae067273
)
require (
github.com/golang/snappy v1.0.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
golang.org/x/text v0.24.0 // indirect
)

90
main.go

@ -0,0 +1,90 @@
package main
import (
"flag"
"fmt"
"os"
"github.com/wangzhaobo168/dm-mcp-server/tools"
"github.com/wangzhaobo168/dm-mcp-server/utils"
"github.com/mark3labs/mcp-go/server"
_ "github.com/wangzhaobo168/dm"
)
var (
Version = utils.Version
)
// 添加工具
func addTools(s *server.MCPServer) {
// 添加 list_tables 工具
s.AddTool(tools.ListTablesTool, tools.ListTablesToolHandelFunc)
// 添加 execute_query 工具
s.AddTool(tools.ExecuteQueryTool, tools.ExecuteQueryToolHandelFunc)
// 添加 describe_table 工具
s.AddTool(tools.DescribeTableTool, tools.DescribeTableToolHandelFunc)
}
// 创建一个新的MCP服务器
func newMCPServer() *server.MCPServer {
return server.NewMCPServer(
"达梦数据库MCP Server",
Version,
server.WithResourceCapabilities(true, true),
server.WithLogging(),
)
}
func run() error {
s := newMCPServer()
addTools(s)
// 启动服务器
if err := server.ServeStdio(s); err != nil {
return fmt.Errorf("服务器错误: %v", err)
}
return nil
}
func main() {
username := flag.String("username", "", "用户名")
password := flag.String("password", "", "密码")
host := flag.String("host", "", "主机")
port := flag.String("port", "", "端口")
schema := flag.String("schema", "", "模式名")
showVersion := flag.Bool("version", false, "显示版本号")
flag.Parse()
if *username != "" {
utils.SetUserName(*username)
} else {
*username = utils.GetUserName()
}
if *password != "" {
utils.SetPassword(*password)
} else {
*password = utils.GetPassWord()
}
if *host != "" {
utils.SetHost(*host)
} else {
*host = utils.GetHost()
}
if *port != "" {
utils.SetPort(*port)
} else {
*port = utils.GetPort()
}
if *schema != "" {
utils.SetSchema(*schema)
} else {
*schema = utils.GetSchema()
}
if *showVersion {
fmt.Printf("Dm MCP Server\n")
fmt.Printf("Version: %s\n", Version)
os.Exit(0)
}
if err := run(); err != nil {
panic(err)
}
}

73
tools/describe_table.go

@ -0,0 +1,73 @@
package tools
import (
"context"
"fmt"
"strings"
"github.com/mark3labs/mcp-go/mcp"
"github.com/wangzhaobo168/dm-mcp-server/utils"
)
const (
DescribeTableToolName = "describe_table"
)
var DescribeTableTool = mcp.NewTool(DescribeTableToolName,
mcp.WithDescription("显示表的结构"),
mcp.WithString(
"table",
mcp.Description("表名称"),
mcp.Required(),
),
mcp.WithString(
"database",
mcp.Description("数据库名称(如果未指定则使用DM_SCHEMA)"),
),
)
func DescribeTableToolHandelFunc(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
dbName, ok := request.Params.Arguments["schema"].(string)
if !ok {
dbName = utils.GetSchema()
}
tableName, ok := request.Params.Arguments["table"].(string)
if !ok {
return mcp.NewToolResultError("表名称不能为空"), nil
}
db, err := utils.ConnectDMDatabase()
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("连接达梦数据库失败: %v", err)), nil
}
defer db.Close()
// 使用达梦数据库系统表查询表结构
query := fmt.Sprintf(`
SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH, NULLABLE
FROM ALL_TAB_COLUMNS
WHERE OWNER = '%s' AND TABLE_NAME = '%s'
ORDER BY COLUMN_ID`, dbName, tableName)
rows, err := db.Query(query)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("获取表结构失败: %v", err)), nil
}
defer rows.Close()
var schema []string
for rows.Next() {
var columnName, dataType, nullable string
var dataLength int
if err := rows.Scan(&columnName, &dataType, &dataLength, &nullable); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("扫描列信息失败: %v", err)), nil
}
columnDesc := fmt.Sprintf("%s %s(%d) %s",
columnName,
dataType,
dataLength,
nullable)
schema = append(schema, columnDesc)
}
return mcp.NewToolResultText(strings.Join(schema, "\n")), nil
}

88
tools/execute_query.go

@ -0,0 +1,88 @@
package tools
import (
"context"
"fmt"
"strings"
"github.com/mark3labs/mcp-go/mcp"
"github.com/wangzhaobo168/dm-mcp-server/utils"
)
const (
ExecuteQueryToolName = "execute_query"
)
var ExecuteQueryTool = mcp.NewTool(ExecuteQueryToolName,
mcp.WithDescription("执行只读SQL查询"),
mcp.WithString(
"query",
mcp.Description("SQL查询(仅允许SELECT、SHOW、DESCRIBE和EXPLAIN语句)"),
mcp.Required(),
),
mcp.WithString(
"database",
mcp.Description("数据库名称(如果未指定则使用配置的DM_SCHEMA)"),
),
)
func ExecuteQueryToolHandelFunc(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
query := request.Params.Arguments["query"].(string)
dbName, ok := request.Params.Arguments["schema"].(string)
if !ok {
dbName = utils.GetSchema()
}
// 验证查询语句是否为只读操作
queryUpper := strings.ToUpper(strings.TrimSpace(query))
if !strings.HasPrefix(queryUpper, "SELECT") &&
!strings.HasPrefix(queryUpper, "SHOW") &&
!strings.HasPrefix(queryUpper, "DESCRIBE") &&
!strings.HasPrefix(queryUpper, "EXPLAIN") {
return mcp.NewToolResultError("仅允许SELECT、SHOW、DESCRIBE和EXPLAIN语句"), nil
}
db, err := utils.ConnectDMDatabase()
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("连接达梦数据库失败: %v", err)), nil
}
defer db.Close()
// 设置当前schema
if _, err := db.Exec(fmt.Sprintf("SET SCHEMA %s", dbName)); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("设置schema失败: %v", err)), nil
}
rows, err := db.Query(query)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("执行查询失败: %v", err)), nil
}
defer rows.Close()
columns, err := rows.Columns()
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("获取列失败: %v", err)), nil
}
// 构建表头
var results []string
results = append(results, strings.Join(columns, "\t"))
// 获取数据行
for rows.Next() {
values := make([]interface{}, len(columns))
valuePtrs := make([]interface{}, len(columns))
for i := range columns {
valuePtrs[i] = &values[i]
}
if err := rows.Scan(valuePtrs...); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("扫描行失败: %v", err)), nil
}
var rowValues []string
for _, v := range values {
rowValues = append(rowValues, fmt.Sprintf("%v", v))
}
results = append(results, strings.Join(rowValues, "\t"))
}
return mcp.NewToolResultText(strings.Join(results, "\n")), nil
}

53
tools/list_tables.go

@ -0,0 +1,53 @@
package tools
import (
"context"
"fmt"
"strings"
"github.com/mark3labs/mcp-go/mcp"
"github.com/wangzhaobo168/dm-mcp-server/utils"
)
const (
ListTablesToolName = "list_tables"
)
var ListTablesTool = mcp.NewTool(ListTablesToolName,
mcp.WithDescription("列出指定数据库中的所有表"),
mcp.WithString(
"database",
mcp.Description("数据库名称(可选)"),
),
)
func ListTablesToolHandelFunc(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
dbName, ok := request.Params.Arguments["schema"].(string)
if !ok {
dbName = utils.GetSchema()
}
db, err := utils.ConnectDMDatabase()
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("连接达梦数据库失败: %v", err)), nil
}
defer db.Close()
// 使用达梦数据库系统表查询指定schema下的所有表
query := fmt.Sprintf("SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER = '%s'", dbName)
rows, err := db.Query(query)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("获取表失败: %v", err)), nil
}
defer rows.Close()
var tables []string
for rows.Next() {
var tableName string
if err := rows.Scan(&tableName); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("扫描表名称失败: %v", err)), nil
}
tables = append(tables, tableName)
}
return mcp.NewToolResultText(strings.Join(tables, ", ")), nil
}

73
utils/config.go

@ -0,0 +1,73 @@
package utils
import (
"os"
)
var (
Version = "1.0.0"
DefaultPort = "5236"
userName string
password string
host string
port string
schema string
)
func GetUserName() string {
if userName != "" {
return userName
}
return os.Getenv("DM_USERNAME")
}
func GetPassWord() string {
if password != "" {
return password
}
return os.Getenv("DM_PASSWORD")
}
func GetHost() string {
if host != "" {
return host
}
return os.Getenv("DM_HOST")
}
func GetSchema() string {
if schema != "" {
return schema
}
return os.Getenv("DM_SCHEMA")
}
func GetPort() string {
if port != "" {
return port
}
if port := os.Getenv("DM_PORT"); port != "" {
return port
}
return DefaultPort
}
func SetHost(h string) {
host = h
}
func SetPort(p string) {
port = p
}
func SetUserName(name string) {
userName = name
}
func SetPassword(pwd string) {
password = pwd
}
func SetSchema(s string) {
schema = s
}

26
utils/db.go

@ -0,0 +1,26 @@
// utils/db.go
package utils
import (
"database/sql"
"fmt"
_ "github.com/wangzhaobo168/dm" // 引入达梦数据库驱动
)
// ConnectDMDatabase 连接达梦数据库
func ConnectDMDatabase() (*sql.DB, error) {
username := GetUserName()
password := GetPassWord()
host := GetHost()
port := GetPort()
dataSourceName := fmt.Sprintf("dm://%s:%s@%s:%s", username, password, host, port)
db, err := sql.Open("dm", dataSourceName)
if err != nil {
return nil, fmt.Errorf("打开达梦数据库失败: %w", err)
}
if err := db.Ping(); err != nil {
return nil, fmt.Errorf("无法连接达梦数据库: %w", err)
}
return db, nil
}
Loading…
Cancel
Save