@yeyuan98/opencode-bioresearcher-plugin 1.4.1 → 1.5.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/README.md +49 -22
- package/dist/db-tools/backends/index.d.ts +11 -0
- package/dist/db-tools/backends/index.js +48 -0
- package/dist/db-tools/backends/mongodb/backend.d.ts +15 -0
- package/dist/db-tools/backends/mongodb/backend.js +76 -0
- package/dist/db-tools/backends/mongodb/connection.d.ts +27 -0
- package/dist/db-tools/backends/mongodb/connection.js +107 -0
- package/dist/db-tools/backends/mongodb/index.d.ts +4 -0
- package/dist/db-tools/backends/mongodb/index.js +3 -0
- package/dist/db-tools/backends/mongodb/translator.d.ts +30 -0
- package/dist/db-tools/backends/mongodb/translator.js +407 -0
- package/dist/db-tools/backends/mysql/backend.d.ts +15 -0
- package/dist/db-tools/backends/mysql/backend.js +57 -0
- package/dist/db-tools/backends/mysql/connection.d.ts +25 -0
- package/dist/db-tools/backends/mysql/connection.js +83 -0
- package/dist/db-tools/backends/mysql/index.d.ts +3 -0
- package/dist/db-tools/backends/mysql/index.js +2 -0
- package/dist/db-tools/backends/mysql/translator.d.ts +7 -0
- package/dist/db-tools/backends/mysql/translator.js +67 -0
- package/dist/db-tools/core/base.d.ts +17 -0
- package/dist/db-tools/core/base.js +51 -0
- package/dist/db-tools/core/config-loader.d.ts +3 -0
- package/dist/db-tools/core/config-loader.js +46 -0
- package/dist/db-tools/core/index.d.ts +2 -0
- package/dist/db-tools/core/index.js +2 -0
- package/dist/db-tools/core/jsonc-parser.d.ts +2 -0
- package/dist/db-tools/core/jsonc-parser.js +77 -0
- package/dist/db-tools/core/validator.d.ts +16 -0
- package/dist/db-tools/core/validator.js +118 -0
- package/dist/db-tools/executor.d.ts +13 -0
- package/dist/db-tools/executor.js +54 -0
- package/dist/db-tools/index.d.ts +51 -0
- package/dist/db-tools/index.js +27 -0
- package/dist/db-tools/interface/backend.d.ts +24 -0
- package/dist/db-tools/interface/backend.js +1 -0
- package/dist/db-tools/interface/connection.d.ts +21 -0
- package/dist/db-tools/interface/connection.js +11 -0
- package/dist/db-tools/interface/index.d.ts +4 -0
- package/dist/db-tools/interface/index.js +4 -0
- package/dist/db-tools/interface/query.d.ts +60 -0
- package/dist/db-tools/interface/query.js +1 -0
- package/dist/db-tools/interface/schema.d.ts +22 -0
- package/dist/db-tools/interface/schema.js +1 -0
- package/dist/db-tools/pool.d.ts +8 -0
- package/dist/db-tools/pool.js +49 -0
- package/dist/db-tools/tools/index.d.ts +27 -0
- package/dist/db-tools/tools/index.js +191 -0
- package/dist/db-tools/tools.d.ts +27 -0
- package/dist/db-tools/tools.js +111 -0
- package/dist/db-tools/types.d.ts +94 -0
- package/dist/db-tools/types.js +40 -0
- package/dist/db-tools/utils.d.ts +33 -0
- package/dist/db-tools/utils.js +94 -0
- package/dist/index.js +5 -1
- package/dist/parser-tools/obo/index.d.ts +2 -0
- package/dist/parser-tools/obo/index.js +2 -0
- package/dist/parser-tools/obo/obo.d.ts +17 -0
- package/dist/parser-tools/obo/obo.js +216 -0
- package/dist/parser-tools/obo/types.d.ts +166 -0
- package/dist/parser-tools/obo/types.js +1 -0
- package/dist/parser-tools/obo/utils.d.ts +21 -0
- package/dist/parser-tools/obo/utils.js +411 -0
- package/dist/skills/env-jsonc-setup/SKILL.md +206 -0
- package/dist/skills/long-table-summary/SKILL.md +437 -374
- package/dist/skills/long-table-summary/combine_outputs.py +5 -14
- package/dist/skills/long-table-summary/generate_prompts.py +211 -0
- package/dist/skills/long-table-summary/pyproject.toml +8 -11
- package/package.json +3 -1
- package/dist/skills/long-table-summary/__init__.py +0 -3
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as mysql from 'mysql2/promise';
|
|
2
|
+
let pool = null;
|
|
3
|
+
let currentConfig = null;
|
|
4
|
+
function createPoolConfig(config) {
|
|
5
|
+
return {
|
|
6
|
+
host: config.host,
|
|
7
|
+
port: config.port,
|
|
8
|
+
user: config.user,
|
|
9
|
+
password: config.password,
|
|
10
|
+
database: config.database,
|
|
11
|
+
charset: config.charset,
|
|
12
|
+
connectTimeout: config.connectionTimeout,
|
|
13
|
+
waitForConnections: true,
|
|
14
|
+
connectionLimit: 10,
|
|
15
|
+
queueLimit: 0,
|
|
16
|
+
namedPlaceholders: true,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function getPool(config) {
|
|
20
|
+
if (pool && currentConfig && isSameConfig(currentConfig, config)) {
|
|
21
|
+
return pool;
|
|
22
|
+
}
|
|
23
|
+
if (pool) {
|
|
24
|
+
pool.end().catch(err => console.error('[db-tools] Previous pool cleanup failed:', err.message));
|
|
25
|
+
}
|
|
26
|
+
pool = mysql.createPool(createPoolConfig(config));
|
|
27
|
+
currentConfig = config;
|
|
28
|
+
return pool;
|
|
29
|
+
}
|
|
30
|
+
function isSameConfig(a, b) {
|
|
31
|
+
return (a.host === b.host &&
|
|
32
|
+
a.port === b.port &&
|
|
33
|
+
a.user === b.user &&
|
|
34
|
+
a.password === b.password &&
|
|
35
|
+
a.database === b.database);
|
|
36
|
+
}
|
|
37
|
+
export async function closePool() {
|
|
38
|
+
if (pool) {
|
|
39
|
+
await pool.end().catch(err => console.error('[db-tools] Pool close failed:', err.message));
|
|
40
|
+
pool = null;
|
|
41
|
+
currentConfig = null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function getPoolStatus() {
|
|
45
|
+
return {
|
|
46
|
+
initialized: pool !== null,
|
|
47
|
+
hasConfig: currentConfig !== null,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ToolContext } from '@opencode-ai/plugin/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const dbQuery: {
|
|
4
|
+
description: string;
|
|
5
|
+
args: {
|
|
6
|
+
sql: z.ZodString;
|
|
7
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
8
|
+
};
|
|
9
|
+
execute(args: {
|
|
10
|
+
sql: string;
|
|
11
|
+
params?: Record<string, any> | undefined;
|
|
12
|
+
}, context: ToolContext): Promise<string>;
|
|
13
|
+
};
|
|
14
|
+
export declare const dbListTables: {
|
|
15
|
+
description: string;
|
|
16
|
+
args: {};
|
|
17
|
+
execute(args: Record<string, never>, context: ToolContext): Promise<string>;
|
|
18
|
+
};
|
|
19
|
+
export declare const dbDescribeTable: {
|
|
20
|
+
description: string;
|
|
21
|
+
args: {
|
|
22
|
+
table_name: z.ZodString;
|
|
23
|
+
};
|
|
24
|
+
execute(args: {
|
|
25
|
+
table_name: string;
|
|
26
|
+
}, context: ToolContext): Promise<string>;
|
|
27
|
+
};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { initializeBackend, getDefaultBackend } from '../backends';
|
|
4
|
+
import { validateReadOnlyQuery, validateCollectionName } from '../core/validator';
|
|
5
|
+
import { getConfigFromEnv } from '../core/base';
|
|
6
|
+
async function ensureBackend() {
|
|
7
|
+
let backend = getDefaultBackend();
|
|
8
|
+
if (!backend) {
|
|
9
|
+
const config = getConfigFromEnv();
|
|
10
|
+
backend = await initializeBackend(config);
|
|
11
|
+
}
|
|
12
|
+
return backend;
|
|
13
|
+
}
|
|
14
|
+
export const dbQuery = tool({
|
|
15
|
+
description: `Execute a SELECT query on the database with named parameters.
|
|
16
|
+
|
|
17
|
+
**Examples:**
|
|
18
|
+
- Simple query: \`SELECT * FROM users LIMIT 10\`
|
|
19
|
+
- With named params: \`SELECT * FROM users WHERE status = :status AND created_at > :date\`
|
|
20
|
+
- Join query: \`SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id\`
|
|
21
|
+
- MongoDB query: \`SELECT * FROM users WHERE status = 'active' LIMIT 10\`
|
|
22
|
+
|
|
23
|
+
**Parameters:**
|
|
24
|
+
- Use named placeholders like \`:paramName\` for parameters
|
|
25
|
+
- Pass parameter values in the \`params\` object: \`{ "status": "active", "date": "2024-01-01" }\`
|
|
26
|
+
|
|
27
|
+
**Note:** Only SELECT statements are allowed (read-only access).
|
|
28
|
+
|
|
29
|
+
**Backend Support:**
|
|
30
|
+
- MySQL: Native SQL execution
|
|
31
|
+
- MongoDB: SQL is translated to MongoDB queries automatically`,
|
|
32
|
+
args: {
|
|
33
|
+
sql: z.string().describe('SELECT SQL query to execute. Use named placeholders like :name for parameters.'),
|
|
34
|
+
params: z.record(z.string(), z.any()).optional().describe('Named parameters as key-value pairs (e.g., { "status": "active", "limit": 10 })'),
|
|
35
|
+
},
|
|
36
|
+
execute: async (args, _context) => {
|
|
37
|
+
try {
|
|
38
|
+
const backend = await ensureBackend();
|
|
39
|
+
const validation = validateReadOnlyQuery(args.sql, backend.type);
|
|
40
|
+
if (!validation.valid && validation.error) {
|
|
41
|
+
return JSON.stringify(validation.error, null, 2);
|
|
42
|
+
}
|
|
43
|
+
const result = await backend.executeQuery({
|
|
44
|
+
sql: args.sql,
|
|
45
|
+
params: args.params,
|
|
46
|
+
});
|
|
47
|
+
return JSON.stringify(result, null, 2);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
51
|
+
return JSON.stringify({
|
|
52
|
+
success: false,
|
|
53
|
+
error: {
|
|
54
|
+
code: 'BACKEND_ERROR',
|
|
55
|
+
message: errorMessage,
|
|
56
|
+
},
|
|
57
|
+
metadata: { executionTimeMs: 0, backend: 'unknown' },
|
|
58
|
+
}, null, 2);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
export const dbListTables = tool({
|
|
63
|
+
description: `List all tables/collections in the current database with metadata.
|
|
64
|
+
|
|
65
|
+
**Returns:**
|
|
66
|
+
- Table/collection names and types (TABLE, VIEW, COLLECTION, etc.)
|
|
67
|
+
- For MySQL: Storage engine (InnoDB, MyISAM, etc.), row count, creation time
|
|
68
|
+
- For MongoDB: Collection type information
|
|
69
|
+
|
|
70
|
+
**Usage:** Call this first to discover what tables/collections are available before using dbDescribeTable or dbQuery.`,
|
|
71
|
+
args: {},
|
|
72
|
+
execute: async (_args, _context) => {
|
|
73
|
+
const startTime = Date.now();
|
|
74
|
+
try {
|
|
75
|
+
const backend = await ensureBackend();
|
|
76
|
+
const collections = await backend.listCollections();
|
|
77
|
+
const result = {
|
|
78
|
+
success: true,
|
|
79
|
+
data: {
|
|
80
|
+
tables: collections,
|
|
81
|
+
count: collections.length,
|
|
82
|
+
},
|
|
83
|
+
metadata: {
|
|
84
|
+
executionTimeMs: Date.now() - startTime,
|
|
85
|
+
backend: backend.type,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
return JSON.stringify(result, null, 2);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const backend = getDefaultBackend();
|
|
92
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
93
|
+
return JSON.stringify({
|
|
94
|
+
success: false,
|
|
95
|
+
error: {
|
|
96
|
+
code: 'LIST_TABLES_ERROR',
|
|
97
|
+
message: errorMessage,
|
|
98
|
+
hints: [
|
|
99
|
+
'Check that the database server is running',
|
|
100
|
+
'Verify your database credentials',
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
metadata: {
|
|
104
|
+
executionTimeMs: Date.now() - startTime,
|
|
105
|
+
backend: backend?.type || 'unknown',
|
|
106
|
+
},
|
|
107
|
+
}, null, 2);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
export const dbDescribeTable = tool({
|
|
112
|
+
description: `Get the column/field schema for a specific table/collection.
|
|
113
|
+
|
|
114
|
+
**Returns for each column/field:**
|
|
115
|
+
- Field name
|
|
116
|
+
- Data type (VARCHAR, INT, TEXT, DATETIME, etc. for MySQL; BSON types for MongoDB)
|
|
117
|
+
- Nullable status
|
|
118
|
+
- Key type (PRI for primary key, etc.)
|
|
119
|
+
- Default value
|
|
120
|
+
- Extra info (auto_increment, etc.)
|
|
121
|
+
- Comments/description
|
|
122
|
+
|
|
123
|
+
**Workflow:**
|
|
124
|
+
1. Use \`dbListTables\` first to see available tables/collections
|
|
125
|
+
2. Use this tool to understand the column/field structure
|
|
126
|
+
3. Use \`dbQuery\` to query the data`,
|
|
127
|
+
args: {
|
|
128
|
+
table_name: z.string().describe('Name of the table/collection to describe. Use dbListTables to see available names.'),
|
|
129
|
+
},
|
|
130
|
+
execute: async (args, _context) => {
|
|
131
|
+
const startTime = Date.now();
|
|
132
|
+
try {
|
|
133
|
+
const backend = await ensureBackend();
|
|
134
|
+
const validation = validateCollectionName(args.table_name, backend.type);
|
|
135
|
+
if (!validation.valid && validation.error) {
|
|
136
|
+
return JSON.stringify(validation.error, null, 2);
|
|
137
|
+
}
|
|
138
|
+
const columns = await backend.describeCollection(args.table_name);
|
|
139
|
+
if (!columns || columns.length === 0) {
|
|
140
|
+
return JSON.stringify({
|
|
141
|
+
success: false,
|
|
142
|
+
error: {
|
|
143
|
+
code: 'TABLE_NOT_FOUND',
|
|
144
|
+
message: `Table/collection '${args.table_name}' not found in database`,
|
|
145
|
+
hints: [
|
|
146
|
+
'Use dbListTables to see available tables/collections',
|
|
147
|
+
'Check the name for typos',
|
|
148
|
+
'Make sure you are connected to the correct database',
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
metadata: {
|
|
152
|
+
executionTimeMs: Date.now() - startTime,
|
|
153
|
+
backend: backend.type,
|
|
154
|
+
},
|
|
155
|
+
}, null, 2);
|
|
156
|
+
}
|
|
157
|
+
const result = {
|
|
158
|
+
success: true,
|
|
159
|
+
data: {
|
|
160
|
+
table: args.table_name,
|
|
161
|
+
columns,
|
|
162
|
+
columnCount: columns.length,
|
|
163
|
+
},
|
|
164
|
+
metadata: {
|
|
165
|
+
executionTimeMs: Date.now() - startTime,
|
|
166
|
+
backend: backend.type,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
return JSON.stringify(result, null, 2);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
const backend = getDefaultBackend();
|
|
173
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
174
|
+
return JSON.stringify({
|
|
175
|
+
success: false,
|
|
176
|
+
error: {
|
|
177
|
+
code: 'DESCRIBE_TABLE_ERROR',
|
|
178
|
+
message: errorMessage,
|
|
179
|
+
hints: [
|
|
180
|
+
'Use dbListTables to see available tables/collections',
|
|
181
|
+
'Check the table name for typos',
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
metadata: {
|
|
185
|
+
executionTimeMs: Date.now() - startTime,
|
|
186
|
+
backend: backend?.type || 'unknown',
|
|
187
|
+
},
|
|
188
|
+
}, null, 2);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ToolContext } from '@opencode-ai/plugin/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const dbQuery: {
|
|
4
|
+
description: string;
|
|
5
|
+
args: {
|
|
6
|
+
sql: z.ZodString;
|
|
7
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
8
|
+
};
|
|
9
|
+
execute(args: {
|
|
10
|
+
sql: string;
|
|
11
|
+
params?: Record<string, any> | undefined;
|
|
12
|
+
}, context: ToolContext): Promise<string>;
|
|
13
|
+
};
|
|
14
|
+
export declare const dbListTables: {
|
|
15
|
+
description: string;
|
|
16
|
+
args: {};
|
|
17
|
+
execute(args: Record<string, never>, context: ToolContext): Promise<string>;
|
|
18
|
+
};
|
|
19
|
+
export declare const dbDescribeTable: {
|
|
20
|
+
description: string;
|
|
21
|
+
args: {
|
|
22
|
+
table_name: z.ZodString;
|
|
23
|
+
};
|
|
24
|
+
execute(args: {
|
|
25
|
+
table_name: string;
|
|
26
|
+
}, context: ToolContext): Promise<string>;
|
|
27
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { getDefaultBackend, initializeBackend } from './backends';
|
|
4
|
+
import { getConfigFromEnv } from './core/base';
|
|
5
|
+
import { validateReadOnlyQuery, validateCollectionName } from './core/validator';
|
|
6
|
+
import { formatCollectionsResult, formatSchemaResult, formatCollectionNotFound, formatInitError } from './utils';
|
|
7
|
+
async function getOrCreateBackend() {
|
|
8
|
+
let backend = getDefaultBackend();
|
|
9
|
+
if (!backend) {
|
|
10
|
+
const config = getConfigFromEnv();
|
|
11
|
+
backend = await initializeBackend(config);
|
|
12
|
+
}
|
|
13
|
+
return backend;
|
|
14
|
+
}
|
|
15
|
+
export const dbQuery = tool({
|
|
16
|
+
description: `Execute a SELECT query on the database with named parameters.
|
|
17
|
+
|
|
18
|
+
**Examples:**
|
|
19
|
+
- Simple query: \`SELECT * FROM users LIMIT 10\`
|
|
20
|
+
- With named params: \`SELECT * FROM users WHERE status = :status AND created_at > :date\`
|
|
21
|
+
- Join query: \`SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id\`
|
|
22
|
+
|
|
23
|
+
**Parameters:**
|
|
24
|
+
- Use named placeholders like \`:paramName\` for parameters
|
|
25
|
+
- Pass parameter values in the \`params\` object: \`{ "status": "active", "date": "2024-01-01" }\`
|
|
26
|
+
|
|
27
|
+
**Note:** Only SELECT statements are allowed (read-only access).`,
|
|
28
|
+
args: {
|
|
29
|
+
sql: z.string().describe('SELECT SQL query to execute. Use named placeholders like :name for parameters.'),
|
|
30
|
+
params: z.record(z.string(), z.any()).optional().describe('Named parameters as key-value pairs (e.g., { "status": "active", "limit": 10 })'),
|
|
31
|
+
},
|
|
32
|
+
execute: async (args, _context) => {
|
|
33
|
+
try {
|
|
34
|
+
const backend = await getOrCreateBackend();
|
|
35
|
+
const validation = validateReadOnlyQuery(args.sql, backend.type);
|
|
36
|
+
if (!validation.valid && validation.error) {
|
|
37
|
+
return JSON.stringify(validation.error, null, 2);
|
|
38
|
+
}
|
|
39
|
+
const result = await backend.executeQuery({
|
|
40
|
+
sql: args.sql,
|
|
41
|
+
params: args.params,
|
|
42
|
+
});
|
|
43
|
+
return JSON.stringify(result, null, 2);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return JSON.stringify(formatInitError(error), null, 2);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
export const dbListTables = tool({
|
|
51
|
+
description: `List all tables/collections in the current database with metadata.
|
|
52
|
+
|
|
53
|
+
**Returns:**
|
|
54
|
+
- Table/collection names and types (TABLE, VIEW, COLLECTION)
|
|
55
|
+
- Storage engine (InnoDB, MyISAM, etc.) for MySQL
|
|
56
|
+
- Approximate row count
|
|
57
|
+
- Creation time (MySQL only)
|
|
58
|
+
- Table comments
|
|
59
|
+
|
|
60
|
+
**Usage:** Call this first to discover what tables are available before using dbDescribeTable or dbQuery.`,
|
|
61
|
+
args: {},
|
|
62
|
+
execute: async (_args, _context) => {
|
|
63
|
+
try {
|
|
64
|
+
const backend = await getOrCreateBackend();
|
|
65
|
+
const collections = await backend.listCollections();
|
|
66
|
+
const result = formatCollectionsResult(collections, 0, backend.type);
|
|
67
|
+
return JSON.stringify(result, null, 2);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
return JSON.stringify(formatInitError(error), null, 2);
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
export const dbDescribeTable = tool({
|
|
75
|
+
description: `Get the column schema for a specific table/collection.
|
|
76
|
+
|
|
77
|
+
**Returns for each column/field:**
|
|
78
|
+
- Field name
|
|
79
|
+
- Data type (VARCHAR, INT, TEXT, DATETIME, etc.)
|
|
80
|
+
- Nullable status
|
|
81
|
+
- Key type (PRI for primary key, UNI for unique, MUL for multiple)
|
|
82
|
+
- Default value
|
|
83
|
+
- Extra info (auto_increment, etc.)
|
|
84
|
+
- Column comments
|
|
85
|
+
|
|
86
|
+
**Workflow:**
|
|
87
|
+
1. Use \`dbListTables\` first to see available tables/collections
|
|
88
|
+
2. Use this tool to understand the column/field structure
|
|
89
|
+
3. Use \`dbQuery\` to query the data`,
|
|
90
|
+
args: {
|
|
91
|
+
table_name: z.string().describe('Name of the table/collection to describe. Use dbListTables to see available tables.'),
|
|
92
|
+
},
|
|
93
|
+
execute: async (args, _context) => {
|
|
94
|
+
try {
|
|
95
|
+
const backend = await getOrCreateBackend();
|
|
96
|
+
const validation = validateCollectionName(args.table_name, backend.type);
|
|
97
|
+
if (!validation.valid && validation.error) {
|
|
98
|
+
return JSON.stringify(validation.error, null, 2);
|
|
99
|
+
}
|
|
100
|
+
const columns = await backend.describeCollection(args.table_name);
|
|
101
|
+
if (columns.length === 0) {
|
|
102
|
+
return JSON.stringify(formatCollectionNotFound(args.table_name, backend.type), null, 2);
|
|
103
|
+
}
|
|
104
|
+
const result = formatSchemaResult(columns, args.table_name, 0, backend.type);
|
|
105
|
+
return JSON.stringify(result, null, 2);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return JSON.stringify(formatInitError(error), null, 2);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { RowDataPacket } from 'mysql2/promise';
|
|
2
|
+
export interface DbConfig {
|
|
3
|
+
host: string;
|
|
4
|
+
port: number;
|
|
5
|
+
user: string;
|
|
6
|
+
password: string;
|
|
7
|
+
database: string;
|
|
8
|
+
charset?: string;
|
|
9
|
+
connectionTimeout?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface QueryOptions {
|
|
12
|
+
sql: string;
|
|
13
|
+
params?: Record<string, unknown> | unknown[];
|
|
14
|
+
}
|
|
15
|
+
export interface FieldInfo {
|
|
16
|
+
name: string;
|
|
17
|
+
type: string;
|
|
18
|
+
nullable: boolean;
|
|
19
|
+
key: string | null;
|
|
20
|
+
default: unknown;
|
|
21
|
+
extra: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface StructuredError {
|
|
24
|
+
code: string;
|
|
25
|
+
message: string;
|
|
26
|
+
details?: string;
|
|
27
|
+
hints?: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface ToolResponse<T> {
|
|
30
|
+
success: boolean;
|
|
31
|
+
data?: T;
|
|
32
|
+
error?: StructuredError;
|
|
33
|
+
metadata?: {
|
|
34
|
+
executionTimeMs: number;
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export interface QueryResultData {
|
|
39
|
+
rows: RowDataPacket[];
|
|
40
|
+
rowCount: number;
|
|
41
|
+
fields: FieldInfo[];
|
|
42
|
+
}
|
|
43
|
+
export interface QueryResult extends ToolResponse<QueryResultData> {
|
|
44
|
+
success: true;
|
|
45
|
+
data: QueryResultData;
|
|
46
|
+
}
|
|
47
|
+
export interface QueryError extends ToolResponse<never> {
|
|
48
|
+
success: false;
|
|
49
|
+
error: StructuredError;
|
|
50
|
+
}
|
|
51
|
+
export type QueryResponse = QueryResult | QueryError;
|
|
52
|
+
export interface TableInfo {
|
|
53
|
+
tableName: string;
|
|
54
|
+
tableType: string;
|
|
55
|
+
engine: string | null;
|
|
56
|
+
rowCount: number | null;
|
|
57
|
+
createdAt: string | null;
|
|
58
|
+
comment: string | null;
|
|
59
|
+
}
|
|
60
|
+
export interface TablesResultData {
|
|
61
|
+
tables: TableInfo[];
|
|
62
|
+
count: number;
|
|
63
|
+
}
|
|
64
|
+
export interface ColumnSchema {
|
|
65
|
+
field: string;
|
|
66
|
+
type: string;
|
|
67
|
+
nullable: boolean;
|
|
68
|
+
key: string | null;
|
|
69
|
+
defaultValue: unknown;
|
|
70
|
+
extra: string | null;
|
|
71
|
+
comment: string | null;
|
|
72
|
+
}
|
|
73
|
+
export interface SchemaResultData {
|
|
74
|
+
table: string;
|
|
75
|
+
columns: ColumnSchema[];
|
|
76
|
+
columnCount: number;
|
|
77
|
+
}
|
|
78
|
+
export type MySQLError = Error & {
|
|
79
|
+
code?: string;
|
|
80
|
+
errno?: number;
|
|
81
|
+
sql?: string;
|
|
82
|
+
sqlState?: string;
|
|
83
|
+
};
|
|
84
|
+
export declare const DEFAULT_CONFIG: Partial<DbConfig>;
|
|
85
|
+
export declare const ERROR_HINTS: {
|
|
86
|
+
readonly CONNECTION: readonly ["Check that the database server is running", "Verify DB_HOST and DB_PORT environment variables", "Ensure network connectivity to the database"];
|
|
87
|
+
readonly AUTH: readonly ["Verify DB_USER and DB_PASSWORD environment variables", "Check that the user has permissions for the database"];
|
|
88
|
+
readonly UNKNOWN_TABLE: readonly ["Use dbListTables to see available tables", "Check the table name for typos"];
|
|
89
|
+
readonly UNKNOWN_COLUMN: readonly ["Use dbDescribeTable to see available columns", "Check the column name for typos"];
|
|
90
|
+
readonly SYNTAX: readonly ["Check your SQL syntax", "Ensure all identifiers are properly quoted if needed"];
|
|
91
|
+
readonly GENERIC: readonly ["Check your query syntax", "Use dbListTables to see available tables", "Use dbDescribeTable to check column names"];
|
|
92
|
+
};
|
|
93
|
+
export declare function isQueryResult(response: QueryResponse): response is QueryResult;
|
|
94
|
+
export declare function isQueryError(response: QueryResponse): response is QueryError;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const DEFAULT_CONFIG = {
|
|
2
|
+
host: 'localhost',
|
|
3
|
+
port: 3306,
|
|
4
|
+
charset: 'utf8mb4',
|
|
5
|
+
connectionTimeout: 10000,
|
|
6
|
+
};
|
|
7
|
+
export const ERROR_HINTS = {
|
|
8
|
+
CONNECTION: [
|
|
9
|
+
'Check that the database server is running',
|
|
10
|
+
'Verify DB_HOST and DB_PORT environment variables',
|
|
11
|
+
'Ensure network connectivity to the database',
|
|
12
|
+
],
|
|
13
|
+
AUTH: [
|
|
14
|
+
'Verify DB_USER and DB_PASSWORD environment variables',
|
|
15
|
+
'Check that the user has permissions for the database',
|
|
16
|
+
],
|
|
17
|
+
UNKNOWN_TABLE: [
|
|
18
|
+
'Use dbListTables to see available tables',
|
|
19
|
+
'Check the table name for typos',
|
|
20
|
+
],
|
|
21
|
+
UNKNOWN_COLUMN: [
|
|
22
|
+
'Use dbDescribeTable to see available columns',
|
|
23
|
+
'Check the column name for typos',
|
|
24
|
+
],
|
|
25
|
+
SYNTAX: [
|
|
26
|
+
'Check your SQL syntax',
|
|
27
|
+
'Ensure all identifiers are properly quoted if needed',
|
|
28
|
+
],
|
|
29
|
+
GENERIC: [
|
|
30
|
+
'Check your query syntax',
|
|
31
|
+
'Use dbListTables to see available tables',
|
|
32
|
+
'Use dbDescribeTable to check column names',
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
export function isQueryResult(response) {
|
|
36
|
+
return response.success === true;
|
|
37
|
+
}
|
|
38
|
+
export function isQueryError(response) {
|
|
39
|
+
return response.success === false;
|
|
40
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { IQueryResult, ICollectionInfo, IColumnSchema } from './interface';
|
|
2
|
+
export declare function formatCollectionsResult(collections: ICollectionInfo[], executionTimeMs: number, backendType: string): {
|
|
3
|
+
success: boolean;
|
|
4
|
+
data: {
|
|
5
|
+
tables: {
|
|
6
|
+
tableName: string;
|
|
7
|
+
tableType: "table" | "view" | "collection";
|
|
8
|
+
engine: string | null;
|
|
9
|
+
rowCount: number | null;
|
|
10
|
+
createdAt: string | null;
|
|
11
|
+
comment: string | null;
|
|
12
|
+
}[];
|
|
13
|
+
count: number;
|
|
14
|
+
};
|
|
15
|
+
metadata: {
|
|
16
|
+
executionTimeMs: number;
|
|
17
|
+
backend: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export declare function formatSchemaResult(columns: IColumnSchema[], tableName: string, executionTimeMs: number, backendType: string): {
|
|
21
|
+
success: boolean;
|
|
22
|
+
data: {
|
|
23
|
+
table: string;
|
|
24
|
+
columns: IColumnSchema[];
|
|
25
|
+
columnCount: number;
|
|
26
|
+
};
|
|
27
|
+
metadata: {
|
|
28
|
+
executionTimeMs: number;
|
|
29
|
+
backend: string;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
export declare function formatCollectionNotFound(tableName: string, backendType: string): IQueryResult;
|
|
33
|
+
export declare function formatInitError(error: unknown, backendType?: string): IQueryResult;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { ERROR_HINTS, getErrorHints } from './core/validator';
|
|
2
|
+
export function formatCollectionsResult(collections, executionTimeMs, backendType) {
|
|
3
|
+
return {
|
|
4
|
+
success: true,
|
|
5
|
+
data: {
|
|
6
|
+
tables: collections.map(c => ({
|
|
7
|
+
tableName: c.name,
|
|
8
|
+
tableType: c.type,
|
|
9
|
+
engine: c.engine ?? null,
|
|
10
|
+
rowCount: c.rowCount ?? null,
|
|
11
|
+
createdAt: c.createdAt ?? null,
|
|
12
|
+
comment: c.comment ?? null,
|
|
13
|
+
})),
|
|
14
|
+
count: collections.length,
|
|
15
|
+
},
|
|
16
|
+
metadata: { executionTimeMs, backend: backendType },
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function formatSchemaResult(columns, tableName, executionTimeMs, backendType) {
|
|
20
|
+
return {
|
|
21
|
+
success: true,
|
|
22
|
+
data: {
|
|
23
|
+
table: tableName,
|
|
24
|
+
columns,
|
|
25
|
+
columnCount: columns.length,
|
|
26
|
+
},
|
|
27
|
+
metadata: { executionTimeMs, backend: backendType },
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function formatCollectionNotFound(tableName, backendType) {
|
|
31
|
+
return {
|
|
32
|
+
success: false,
|
|
33
|
+
error: {
|
|
34
|
+
code: 'COLLECTION_NOT_FOUND',
|
|
35
|
+
message: `Collection/table '${tableName}' not found`,
|
|
36
|
+
hints: [
|
|
37
|
+
'Use dbListTables to see available collections/tables',
|
|
38
|
+
'Check the name for typos',
|
|
39
|
+
'Make sure you are connected to the correct database',
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
metadata: { executionTimeMs: 0, backend: backendType },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function formatInitError(error, backendType = 'unknown') {
|
|
46
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
47
|
+
const errorCode = err.code || 'INIT_ERROR';
|
|
48
|
+
const hints = getInitErrorHints(errorCode, backendType);
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: {
|
|
52
|
+
code: errorCode,
|
|
53
|
+
message: `Database initialization failed: ${err.message}`,
|
|
54
|
+
hints,
|
|
55
|
+
},
|
|
56
|
+
metadata: { executionTimeMs: 0, backend: backendType },
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function getInitErrorHints(code, backendType) {
|
|
60
|
+
const codeStr = String(code || '');
|
|
61
|
+
if (backendType === 'mongodb' || codeStr.includes('Mongo')) {
|
|
62
|
+
const mongoHints = {
|
|
63
|
+
'MongoNetworkError': [
|
|
64
|
+
'MongoDB network error - check connection string',
|
|
65
|
+
'For Atlas: verify IP whitelist in Atlas dashboard',
|
|
66
|
+
'Check MONGODB_URI format: mongodb+srv://user:pass@cluster/db',
|
|
67
|
+
],
|
|
68
|
+
'MongoServerError': [
|
|
69
|
+
'MongoDB authentication failed',
|
|
70
|
+
'Check credentials in MONGODB_URI',
|
|
71
|
+
'Verify authSource if using custom auth database',
|
|
72
|
+
],
|
|
73
|
+
'ECONNREFUSED': [
|
|
74
|
+
'MongoDB server is not running or not reachable',
|
|
75
|
+
'Check DB_HOST and DB_PORT environment variables',
|
|
76
|
+
'For local: ensure mongod is running',
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
return mongoHints[code] || [...ERROR_HINTS.CONNECTION];
|
|
80
|
+
}
|
|
81
|
+
const mysqlHints = {
|
|
82
|
+
'ECONNREFUSED': [
|
|
83
|
+
'MySQL server is not running or not reachable',
|
|
84
|
+
'Check DB_HOST and DB_PORT environment variables',
|
|
85
|
+
'For Docker: ensure container is running and port is mapped',
|
|
86
|
+
],
|
|
87
|
+
'ER_ACCESS_DENIED_ERROR': [
|
|
88
|
+
'MySQL authentication failed',
|
|
89
|
+
'Check DB_USER and DB_PASSWORD environment variables',
|
|
90
|
+
'Verify user has permissions for the database',
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
return mysqlHints[code] || [...getErrorHints(code)];
|
|
94
|
+
}
|