@timmy_hu/pg-query-tool 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.xpertai-plugin/plugin.json +9 -0
- package/README.md +157 -0
- package/index.js +27 -0
- package/package.json +32 -0
- package/src/mcp/pg-query-tool.mcp-server.js +108 -0
- package/src/pg-query-tool.module.js +31 -0
- package/src/pg-query-tool.service.js +140 -0
- package/src/schemas/config.schema.js +34 -0
- package/src/schemas/tool.schema.js +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# PostgreSQL 查询工具 (pg-query-tool)
|
|
2
|
+
|
|
3
|
+
> XpertAI 插件 — 提供 PostgreSQL 数据库查询能力,支持自定义 SQL 执行、连接池管理和结果格式化。
|
|
4
|
+
|
|
5
|
+
## 插件简介
|
|
6
|
+
|
|
7
|
+
`pg-query-tool` 是一个 XpertAI 平台插件,通过 MCP Tool 形式暴露 PostgreSQL 数据库查询能力。它基于 `pg` 驱动和连接池实现,支持参数化查询、表结构查看和自动行数限制。
|
|
8
|
+
|
|
9
|
+
## 适用场景
|
|
10
|
+
|
|
11
|
+
- 在 XpertAI 对话中直接查询 PostgreSQL 数据库
|
|
12
|
+
- 通过自然语言驱动的 AI Agent 执行结构化 SQL 查询
|
|
13
|
+
- 数据探查、表结构查看、快速验证查询结果
|
|
14
|
+
- 业务数据检索与报表数据获取
|
|
15
|
+
|
|
16
|
+
## 目录结构
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
pg-query-tool/
|
|
20
|
+
├── .xpertai-plugin/
|
|
21
|
+
│ └── plugin.json # 插件清单
|
|
22
|
+
├── index.js # 入口文件,导出 XpertPlugin
|
|
23
|
+
├── package.json # npm 包定义
|
|
24
|
+
├── README.md
|
|
25
|
+
├── src/
|
|
26
|
+
│ ├── pg-query-tool.module.js # NestJS 模块
|
|
27
|
+
│ ├── pg-query-tool.service.js # 核心服务(连接池 + 查询)
|
|
28
|
+
│ ├── schemas/
|
|
29
|
+
│ │ ├── config.schema.js # 配置 Schema
|
|
30
|
+
│ │ └── tool.schema.js # 工具输入输出 Schema
|
|
31
|
+
│ └── mcp/
|
|
32
|
+
│ ├── pg-query-tool.mcp-server.js # MCP Server 工具集
|
|
33
|
+
│ └── tools/
|
|
34
|
+
└── examples/
|
|
35
|
+
└── request.example.json # 请求示例
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 安装
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# 在 XpertAI 平台通过 npm 安装
|
|
42
|
+
# 插件包名: @timmy_hu/pg-query-tool
|
|
43
|
+
# 版本: 1.0.0
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 配置项
|
|
47
|
+
|
|
48
|
+
| 配置项 | 类型 | 必填 | 默认值 | 说明 |
|
|
49
|
+
|--------|------|------|--------|------|
|
|
50
|
+
| `host` | string | 是 | - | PostgreSQL 主机地址 |
|
|
51
|
+
| `port` | number | 否 | 5432 | PostgreSQL 端口 |
|
|
52
|
+
| `database` | string | 是 | - | 数据库名称 |
|
|
53
|
+
| `username` | string | 是 | - | 数据库用户名 |
|
|
54
|
+
| `password` | string | 是 | - | 数据库密码 |
|
|
55
|
+
| `maxPoolSize` | number | 否 | 10 | 连接池最大连接数 |
|
|
56
|
+
| `queryTimeout` | number | 否 | 30000 | 查询超时(毫秒) |
|
|
57
|
+
| `ssl` | boolean | 否 | false | 是否启用 SSL |
|
|
58
|
+
|
|
59
|
+
## MCP 工具列表
|
|
60
|
+
|
|
61
|
+
### 1. execute-query(执行SQL查询)
|
|
62
|
+
|
|
63
|
+
执行任意 SQL 查询语句,支持参数化查询和自动行数限制。
|
|
64
|
+
|
|
65
|
+
**入参:**
|
|
66
|
+
|
|
67
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
68
|
+
|------|------|------|------|
|
|
69
|
+
| `sql` | string | 是 | SQL 查询语句 |
|
|
70
|
+
| `params` | array | 否 | 参数化查询参数 |
|
|
71
|
+
| `limit` | number | 否 | 返回行数限制,默认 1000 |
|
|
72
|
+
|
|
73
|
+
**返回示例:**
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"success": true,
|
|
78
|
+
"rowCount": 2,
|
|
79
|
+
"rows": [
|
|
80
|
+
{ "id": 1, "name": "Alice" },
|
|
81
|
+
{ "id": 2, "name": "Bob" }
|
|
82
|
+
],
|
|
83
|
+
"fields": [
|
|
84
|
+
{ "name": "id", "dataTypeID": 23 },
|
|
85
|
+
{ "name": "name", "dataTypeID": 1043 }
|
|
86
|
+
],
|
|
87
|
+
"duration": 12
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 2. list-tables(获取表列表)
|
|
92
|
+
|
|
93
|
+
获取指定 schema 下的所有表。
|
|
94
|
+
|
|
95
|
+
**入参:**
|
|
96
|
+
|
|
97
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
98
|
+
|------|------|------|------|
|
|
99
|
+
| `schema` | string | 否 | schema 名称,默认 public |
|
|
100
|
+
|
|
101
|
+
### 3. describe-table(获取表结构)
|
|
102
|
+
|
|
103
|
+
获取指定表的列结构信息。
|
|
104
|
+
|
|
105
|
+
**入参:**
|
|
106
|
+
|
|
107
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
108
|
+
|------|------|------|------|
|
|
109
|
+
| `tableName` | string | 是 | 表名称 |
|
|
110
|
+
| `schema` | string | 否 | schema 名称,默认 public |
|
|
111
|
+
|
|
112
|
+
## 调用示例
|
|
113
|
+
|
|
114
|
+
```javascript
|
|
115
|
+
// 执行查询
|
|
116
|
+
const result = await mcpTool['execute-query'].handler({
|
|
117
|
+
sql: 'SELECT * FROM users WHERE id = $1',
|
|
118
|
+
params: [1],
|
|
119
|
+
limit: 100
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// 获取表列表
|
|
123
|
+
const tables = await mcpTool['list-tables'].handler({
|
|
124
|
+
schema: 'public'
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// 获取表结构
|
|
128
|
+
const columns = await mcpTool['describe-table'].handler({
|
|
129
|
+
tableName: 'users'
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## 常见错误和处理
|
|
134
|
+
|
|
135
|
+
| 错误 | 原因 | 处理方式 |
|
|
136
|
+
|------|------|----------|
|
|
137
|
+
| `ECONNREFUSED` | 数据库未启动或地址错误 | 检查 host/port 配置和数据库状态 |
|
|
138
|
+
| `password authentication failed` | 用户名或密码错误 | 检查 username/password 配置 |
|
|
139
|
+
| `relation does not exist` | 表名不存在 | 先用 list-tables 确认表名 |
|
|
140
|
+
| `timeout` | 查询超时 | 调整 queryTimeout 或优化 SQL |
|
|
141
|
+
|
|
142
|
+
## 安全注意事项
|
|
143
|
+
|
|
144
|
+
- **参数化查询**:始终使用 `$1, $2` 参数化查询,避免 SQL 注入
|
|
145
|
+
- **只读建议**:生产环境建议配置只读数据库用户
|
|
146
|
+
- **行数限制**:默认限制 1000 行,防止大量数据传输
|
|
147
|
+
- **密码安全**:密码通过 XpertAI 配置项管理,不会暴露在代码中
|
|
148
|
+
|
|
149
|
+
## 使用方式
|
|
150
|
+
|
|
151
|
+
1. 在 XpertAI 平台安装此插件
|
|
152
|
+
2. 配置 PostgreSQL 连接信息(host、port、database、username、password)
|
|
153
|
+
3. 在 AI 对话中通过自然语言触发 SQL 查询
|
|
154
|
+
|
|
155
|
+
## 下一步
|
|
156
|
+
|
|
157
|
+
前往 [XpertAI 平台](https://app.xpertai.cn) 使用此插件。
|
package/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL 查询工具 - XpertAI 插件入口
|
|
3
|
+
* 导出有效的 XpertPlugin 对象,供 XpertAI 平台加载
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { PgQueryToolModule } = require('./src/pg-query-tool.module');
|
|
7
|
+
|
|
8
|
+
const plugin = {
|
|
9
|
+
meta: {
|
|
10
|
+
name: "pg-query-tool",
|
|
11
|
+
version: "1.0.0",
|
|
12
|
+
displayName: "PostgreSQL 查询工具",
|
|
13
|
+
description: "提供 PostgreSQL 数据库查询能力,支持自定义 SQL 执行、连接池管理和结果格式化",
|
|
14
|
+
author: "fpi"
|
|
15
|
+
},
|
|
16
|
+
register(ctx) {
|
|
17
|
+
return {
|
|
18
|
+
module: PgQueryToolModule,
|
|
19
|
+
providers: [],
|
|
20
|
+
controllers: [],
|
|
21
|
+
exports: []
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
module.exports = plugin;
|
|
27
|
+
module.exports.default = plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@timmy_hu/pg-query-tool",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "PostgreSQL 查询工具 XpertAI 插件,支持自定义 SQL 执行、连接池管理和结果格式化",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc",
|
|
8
|
+
"test": "node test/self-check.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"xpertai",
|
|
12
|
+
"plugin",
|
|
13
|
+
"postgresql",
|
|
14
|
+
"pg",
|
|
15
|
+
"query",
|
|
16
|
+
"database"
|
|
17
|
+
],
|
|
18
|
+
"author": "fpi",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"pg": "^8.13.0"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@xpert-ai/plugin-sdk": "^3.14.0"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"index.js",
|
|
28
|
+
"src/",
|
|
29
|
+
".xpertai-plugin/",
|
|
30
|
+
"README.md"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL 查询工具 - MCP Server
|
|
3
|
+
* 暴露 MCP Tool 能力供 XpertAI 平台调用
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { PgQueryToolService } = require('../pg-query-tool.service');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 创建 MCP Server 工具集
|
|
10
|
+
* @param {Object} config - 插件配置(来自 configSchema)
|
|
11
|
+
* @returns {Object} 工具定义对象
|
|
12
|
+
*/
|
|
13
|
+
function createMcpTools(config) {
|
|
14
|
+
const service = new PgQueryToolService(config);
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
/** 执行 SQL 查询 */
|
|
18
|
+
'execute-query': {
|
|
19
|
+
name: '执行SQL查询',
|
|
20
|
+
code: 'execute-query',
|
|
21
|
+
description: '在 PostgreSQL 数据库上执行 SQL 查询语句,支持参数化查询',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
sql: { type: 'string', description: '要执行的 SQL 查询语句' },
|
|
26
|
+
params: {
|
|
27
|
+
type: 'array',
|
|
28
|
+
description: 'SQL 参数化查询的参数数组',
|
|
29
|
+
items: {}
|
|
30
|
+
},
|
|
31
|
+
limit: { type: 'integer', description: '返回结果最大行数限制,默认 1000' }
|
|
32
|
+
},
|
|
33
|
+
required: ['sql']
|
|
34
|
+
},
|
|
35
|
+
outputSchema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
success: { type: 'boolean' },
|
|
39
|
+
rowCount: { type: 'integer' },
|
|
40
|
+
rows: { type: 'array' },
|
|
41
|
+
fields: { type: 'array' },
|
|
42
|
+
duration: { type: 'number' },
|
|
43
|
+
error: { type: 'string' }
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
handler: async (args) => {
|
|
47
|
+
const { sql, params = [], limit = 1000 } = args;
|
|
48
|
+
return service.executeQuery(sql, params, limit);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/** 获取表列表 */
|
|
53
|
+
'list-tables': {
|
|
54
|
+
name: '获取表列表',
|
|
55
|
+
code: 'list-tables',
|
|
56
|
+
description: '获取 PostgreSQL 数据库中指定 schema 下的所有表列表',
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
schema: { type: 'string', description: 'schema 名称,默认 public' }
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
outputSchema: {
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: {
|
|
66
|
+
success: { type: 'boolean' },
|
|
67
|
+
rowCount: { type: 'integer' },
|
|
68
|
+
rows: { type: 'array' },
|
|
69
|
+
duration: { type: 'number' }
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
handler: async (args) => {
|
|
73
|
+
const { schema = 'public' } = args;
|
|
74
|
+
return service.listTables(schema);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
/** 获取表结构 */
|
|
79
|
+
'describe-table': {
|
|
80
|
+
name: '获取表结构',
|
|
81
|
+
code: 'describe-table',
|
|
82
|
+
description: '获取 PostgreSQL 指定表的列结构信息(列名、类型、是否可空等)',
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
tableName: { type: 'string', description: '表名称' },
|
|
87
|
+
schema: { type: 'string', description: 'schema 名称,默认 public' }
|
|
88
|
+
},
|
|
89
|
+
required: ['tableName']
|
|
90
|
+
},
|
|
91
|
+
outputSchema: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
success: { type: 'boolean' },
|
|
95
|
+
rowCount: { type: 'integer' },
|
|
96
|
+
rows: { type: 'array' },
|
|
97
|
+
duration: { type: 'number' }
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
handler: async (args) => {
|
|
101
|
+
const { tableName, schema = 'public' } = args;
|
|
102
|
+
return service.describeTable(tableName, schema);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = { createMcpTools };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL 查询工具 - NestJS 模块定义
|
|
3
|
+
* 注册 service 和 MCP 工具
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { PgQueryToolService } = require('./pg-query-tool.service');
|
|
7
|
+
const { createMcpTools } = require('./mcp/pg-query-tool.mcp-server');
|
|
8
|
+
|
|
9
|
+
class PgQueryToolModule {
|
|
10
|
+
static register(config) {
|
|
11
|
+
const service = new PgQueryToolService(config);
|
|
12
|
+
const mcpTools = createMcpTools(config);
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
module: PgQueryToolModule,
|
|
16
|
+
providers: [
|
|
17
|
+
{ provide: 'PG_QUERY_TOOL_CONFIG', useValue: config },
|
|
18
|
+
{ provide: 'PG_QUERY_TOOL_SERVICE', useValue: service },
|
|
19
|
+
{ provide: 'PG_QUERY_TOOL_MCP_TOOLS', useValue: mcpTools }
|
|
20
|
+
],
|
|
21
|
+
controllers: [],
|
|
22
|
+
exports: [
|
|
23
|
+
'PG_QUERY_TOOL_CONFIG',
|
|
24
|
+
'PG_QUERY_TOOL_SERVICE',
|
|
25
|
+
'PG_QUERY_TOOL_MCP_TOOLS'
|
|
26
|
+
]
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = { PgQueryToolModule };
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL 查询工具 - 核心服务
|
|
3
|
+
* 封装 pg.Pool 连接池管理和查询执行逻辑
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 延迟加载 pg 模块,避免插件加载时因缺少运行时依赖而失败
|
|
7
|
+
let Pool = null;
|
|
8
|
+
|
|
9
|
+
class PgQueryToolService {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.config = config;
|
|
12
|
+
this.pool = null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 初始化连接池
|
|
17
|
+
*/
|
|
18
|
+
async init() {
|
|
19
|
+
if (this.pool) return;
|
|
20
|
+
|
|
21
|
+
if (!Pool) {
|
|
22
|
+
const pg = require('pg');
|
|
23
|
+
Pool = pg.Pool;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
this.pool = new Pool({
|
|
27
|
+
host: this.config.host,
|
|
28
|
+
port: this.config.port || 5432,
|
|
29
|
+
database: this.config.database,
|
|
30
|
+
user: this.config.username,
|
|
31
|
+
password: this.config.password,
|
|
32
|
+
max: this.config.maxPoolSize || 10,
|
|
33
|
+
idleTimeoutMillis: 30000,
|
|
34
|
+
connectionTimeoutMillis: this.config.queryTimeout || 30000,
|
|
35
|
+
ssl: this.config.ssl ? { rejectUnauthorized: false } : false
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.pool.on('error', (err) => {
|
|
39
|
+
console.error('[pg-query-tool] 连接池异常:', err.message);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 执行 SQL 查询
|
|
45
|
+
* @param {string} sql - SQL 语句
|
|
46
|
+
* @param {Array} params - 参数化查询参数
|
|
47
|
+
* @param {number} limit - 返回行数限制
|
|
48
|
+
* @returns {Object} 查询结果
|
|
49
|
+
*/
|
|
50
|
+
async executeQuery(sql, params = [], limit = 1000) {
|
|
51
|
+
await this.init();
|
|
52
|
+
|
|
53
|
+
// 自动追加 LIMIT(如果原 SQL 没有且 limit > 0)
|
|
54
|
+
let finalSql = sql.trim();
|
|
55
|
+
if (limit > 0 && !/^SELECT\s+/i.test(finalSql)) {
|
|
56
|
+
// 非 SELECT 语句不加 LIMIT
|
|
57
|
+
} else if (limit > 0 && !/\bLIMIT\b/i.test(finalSql)) {
|
|
58
|
+
finalSql = `${finalSql} LIMIT ${limit}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
try {
|
|
63
|
+
const result = await this.pool.query(finalSql, params);
|
|
64
|
+
const duration = Date.now() - startTime;
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
success: true,
|
|
68
|
+
rowCount: result.rowCount,
|
|
69
|
+
rows: result.rows,
|
|
70
|
+
fields: result.fields ? result.fields.map(f => ({
|
|
71
|
+
name: f.name,
|
|
72
|
+
dataTypeID: f.dataTypeID
|
|
73
|
+
})) : [],
|
|
74
|
+
duration
|
|
75
|
+
};
|
|
76
|
+
} catch (err) {
|
|
77
|
+
const duration = Date.now() - startTime;
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
rowCount: 0,
|
|
81
|
+
rows: [],
|
|
82
|
+
duration,
|
|
83
|
+
error: err.message
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 获取表列表
|
|
90
|
+
* @param {string} schema - schema 名称,默认 public
|
|
91
|
+
* @returns {Object} 查询结果
|
|
92
|
+
*/
|
|
93
|
+
async listTables(schema = 'public') {
|
|
94
|
+
const sql = `
|
|
95
|
+
SELECT
|
|
96
|
+
table_name AS "tableName",
|
|
97
|
+
table_schema AS "schema",
|
|
98
|
+
table_type AS "type"
|
|
99
|
+
FROM information_schema.tables
|
|
100
|
+
WHERE table_schema = $1
|
|
101
|
+
ORDER BY table_name
|
|
102
|
+
`;
|
|
103
|
+
return this.executeQuery(sql, [schema], 0);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 获取表结构
|
|
108
|
+
* @param {string} tableName - 表名
|
|
109
|
+
* @param {string} schema - schema 名称
|
|
110
|
+
* @returns {Object} 查询结果
|
|
111
|
+
*/
|
|
112
|
+
async describeTable(tableName, schema = 'public') {
|
|
113
|
+
const sql = `
|
|
114
|
+
SELECT
|
|
115
|
+
column_name AS "columnName",
|
|
116
|
+
data_type AS "dataType",
|
|
117
|
+
character_maximum_length AS "maxLength",
|
|
118
|
+
is_nullable AS "nullable",
|
|
119
|
+
column_default AS "defaultValue",
|
|
120
|
+
ordinal_position AS "position"
|
|
121
|
+
FROM information_schema.columns
|
|
122
|
+
WHERE table_name = $1 AND table_schema = $2
|
|
123
|
+
ORDER BY ordinal_position
|
|
124
|
+
`;
|
|
125
|
+
return this.executeQuery(sql, [tableName, schema], 0);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 销毁连接池
|
|
130
|
+
*/
|
|
131
|
+
async destroy() {
|
|
132
|
+
if (this.pool) {
|
|
133
|
+
await this.pool.end();
|
|
134
|
+
this.pool = null;
|
|
135
|
+
console.log('[pg-query-tool] 连接池已关闭');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = { PgQueryToolService };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL 查询工具 - 配置 Schema
|
|
3
|
+
* 使用 zod 声明插件配置项
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { z } = require('zod');
|
|
7
|
+
|
|
8
|
+
const configSchema = z.object({
|
|
9
|
+
/** PostgreSQL 主机地址 */
|
|
10
|
+
host: z.string().describe('PostgreSQL 主机地址,例如 127.0.0.1'),
|
|
11
|
+
|
|
12
|
+
/** PostgreSQL 端口 */
|
|
13
|
+
port: z.number().int().default(5432).describe('PostgreSQL 端口,默认 5432'),
|
|
14
|
+
|
|
15
|
+
/** 数据库名称 */
|
|
16
|
+
database: z.string().describe('数据库名称'),
|
|
17
|
+
|
|
18
|
+
/** 用户名 */
|
|
19
|
+
username: z.string().describe('数据库用户名'),
|
|
20
|
+
|
|
21
|
+
/** 密码 */
|
|
22
|
+
password: z.string().describe('数据库密码'),
|
|
23
|
+
|
|
24
|
+
/** 连接池最大连接数 */
|
|
25
|
+
maxPoolSize: z.number().int().default(10).describe('连接池最大连接数,默认 10'),
|
|
26
|
+
|
|
27
|
+
/** 查询超时时间(毫秒) */
|
|
28
|
+
queryTimeout: z.number().int().default(30000).describe('查询超时时间(毫秒),默认 30000'),
|
|
29
|
+
|
|
30
|
+
/** 是否启用 SSL */
|
|
31
|
+
ssl: z.boolean().default(false).describe('是否启用 SSL 连接,默认 false')
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
module.exports = { configSchema };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL 查询工具 - 查询工具 Schema
|
|
3
|
+
* 定义 MCP 工具的输入输出 Schema
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { z } = require('zod');
|
|
7
|
+
|
|
8
|
+
/** 执行查询工具入参 */
|
|
9
|
+
const executeQuerySchema = z.object({
|
|
10
|
+
sql: z.string().describe('要执行的 SQL 查询语句'),
|
|
11
|
+
params: z.array(z.any()).optional().describe('SQL 参数化查询的参数数组,例如 [1, "test"]'),
|
|
12
|
+
limit: z.number().int().optional().describe('返回结果最大行数限制,不传则使用默认值 1000')
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/** 执行查询工具出参 */
|
|
16
|
+
const queryResultSchema = z.object({
|
|
17
|
+
success: z.boolean().describe('查询是否成功'),
|
|
18
|
+
rowCount: z.number().int().describe('返回的行数'),
|
|
19
|
+
rows: z.array(z.record(z.any())).describe('查询结果行数组'),
|
|
20
|
+
fields: z.array(z.object({
|
|
21
|
+
name: z.string(),
|
|
22
|
+
dataTypeID: z.number().optional()
|
|
23
|
+
})).optional().describe('字段元信息'),
|
|
24
|
+
duration: z.number().describe('查询耗时(毫秒)'),
|
|
25
|
+
error: z.string().optional().describe('错误信息(仅失败时存在)')
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/** 获取表列表工具入参 */
|
|
29
|
+
const listTablesSchema = z.object({
|
|
30
|
+
schema: z.string().optional().describe('指定 schema 名称,默认为 public')
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/** 获取表结构工具入参 */
|
|
34
|
+
const describeTableSchema = z.object({
|
|
35
|
+
tableName: z.string().describe('表名称'),
|
|
36
|
+
schema: z.string().optional().describe('指定 schema 名称,默认为 public')
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
executeQuerySchema,
|
|
41
|
+
queryResultSchema,
|
|
42
|
+
listTablesSchema,
|
|
43
|
+
describeTableSchema
|
|
44
|
+
};
|