dsh-db-mcp 0.7.0 → 0.7.1

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.zh.md CHANGED
@@ -61,7 +61,7 @@ dsh plugin --profile web add dsh-db-mcp # Web 端
61
61
 
62
62
  | 文档 | 内容 |
63
63
  | ---- | ---- |
64
- | [docs/guide.md](docs/guide.md) | 面板操作、连接串、分组、Docker 测试库、排错(英文) |
64
+ | [docs/guide.zh.md](docs/guide.zh.md) | 面板操作、连接串、分组、Docker 测试库、排错(中文) |
65
65
  | [docs/reference.md](docs/reference.md) | HTTP 接口、工具契约、注册表与设置文件、插件配置(英文) |
66
66
  | [docs/development.md](docs/development.md) | 目录结构、热重载、测试套件、发布流程(英文) |
67
67
  | [docs/design-notes.md](docs/design-notes.md) | 设计日志(P1 → P7)与决策记录(英文) |
package/docs/guide.md CHANGED
@@ -1,3 +1,5 @@
1
+ English | [中文](guide.zh.md)
2
+
1
3
  # Guide
2
4
 
3
5
  Everything the panel can do, in the order you meet it. For the wire protocol and
@@ -0,0 +1,178 @@
1
+ [English](guide.md) | 中文
2
+
3
+ # 使用指南
4
+
5
+ 面板能做的事,按你会遇到的顺序排列。协议与文件格式见 [reference.md](reference.md)(英文);构建与发布见 [development.md](development.md)(英文)。
6
+
7
+ ## 安装与升级
8
+
9
+ `dsh plugin` 会把参数原样转给 profile 目录里的 pnpm,所以 `--profile` 必须写在 pnpm 动词之前:
10
+
11
+ ```sh
12
+ dsh plugin --profile desktop add dsh-db-mcp # 全新安装,桌面端
13
+ dsh plugin --profile web add dsh-db-mcp # Web 界面的 profile
14
+ dsh plugin --profile desktop add dsh-db-mcp@0.7.0 # 锁定某个版本
15
+ dsh plugin --profile desktop remove dsh-db-mcp # 卸载
16
+ dsh plugin --profile desktop why dsh-db-mcp # 当前装的是哪个版本
17
+ ```
18
+
19
+ `desktop` 与 `web` 随 DSH 自带;其它名字会在首次使用时自动创建(`--profile scratch add dsh-db-mcp`)。查看某个 profile 装了什么:`dsh plugin --profile desktop list dsh-db-mcp`。
20
+
21
+ 从源码目录安装则传目录路径,profile 会以链接方式接入,改代码即时生效:
22
+
23
+ ```sh
24
+ dsh plugin --profile desktop add C:\path\to\dsh-db-mcp
25
+ ```
26
+
27
+ 升级有三点要注意:
28
+
29
+ - pnpm 会缓存包的元数据。刚发布几分钟的版本可能还不在 dist-tag 里;直接写出版本号可以绕过。
30
+ - pnpm 对新版本有冷却期(`minimumReleaseAge`,默认 1440 分钟),避免刚发布、可能已被投毒的新版本被自动解析。点名具体版本时,pnpm 会把该版本记进 profile 的 `pnpm-workspace.yaml` 里的 `minimumReleaseAgeExclude`,这就是你看到的 `Added 1 entry to minimumReleaseAgeExclude …`。这不是错误:豁免只针对那一个版本,只放行这一次 pin。想改成先询问,在 profile 的 `pnpm-workspace.yaml` 里设 `minimumReleaseAgeStrict: true`;想清理过期条目,设 `minimumReleaseAgeExcludePrune: true`。
31
+ - 升级要么写精确版本,要么先删掉 lock 条目:只要依赖还满足原有范围,`add` 不会动它。
32
+
33
+ 如果重启后侧栏入口消失,见 [排错](#排错)。
34
+
35
+ ## 面板
36
+
37
+ 侧栏 → **数据库**。页头有 **‹ 返回会话**、标题,以及连接状态和版本两个药丸。下面五个标签:
38
+
39
+ | 标签 | 作用 |
40
+ | ---- | ---- |
41
+ | 数据源列表 | 注册表:按分组标签分行的列表、批量操作、刷新、重启 MCP |
42
+ | 新建数据源 | 一条连接串、用户、口令;编辑也复用这个表单 |
43
+ | 从 DBeaver 导入 | 扫描、勾选、导入 |
44
+ | 运行时诊断 | Host 信息、工具数量、全局写开关、venv 与驱动版本 |
45
+ | 审计日志 | 最近十几次工具调用,按你的时区显示 |
46
+
47
+ **新建数据源** 与 **从 DBeaver 导入** 只替换标签下方那张卡片——页头和标签保持不动,之前所在的位置始终只隔一个标签。
48
+
49
+ ## 新增数据源
50
+
51
+ 表单和 DBeaver 的连接对话框一样精简:**连接串**、**用户**、**口令**(带 *保存密码* 勾选)。粘贴连接串会自动填出类型、主机、端口、库名/服务名、显示名称与标识符;推导出来的值只出现在 **更多设置** 里,那里还有模式、SID、SSL 模式、字符集和只读开关。
52
+
53
+ 可接受的写法——DSN、JDBC URL、Oracle TNS 描述符、JDBC thin 描述符,或者干脆一个数据库文件路径:
54
+
55
+ ```
56
+ postgresql://alice:secret@db.example.com:6543/orders?sslmode=require
57
+ jdbc:postgresql://localhost:5432/appdb
58
+ mysql://root:pw@127.0.0.1:3306/shop?charset=utf8mb4
59
+ jdbc:mariadb://10.0.0.5:3307/legacy
60
+ oracle://system:oracle@localhost:1521/XEPDB1
61
+ jdbc:oracle:thin:@//ora:1521/ORCLPDB1 # 服务名
62
+ jdbc:oracle:thin:@ora:1521:ORCL # SID
63
+ (DESCRIPTION=(ADDRESS=(HOST=tns-host)(PORT=1522))(CONNECT_DATA=(SERVICE_NAME=svc1)))
64
+ jdbc:sqlite:C:\data\app.db
65
+ sqlite:///C:/data/chinook.db
66
+ C:\data\app.db / /var/lib/app.sqlite3
67
+ ```
68
+
69
+ 连接串里带的密码只发送一次,之后仅保存,永远不会回显。`id` 创建后不可修改——要改的是 *显示名称*(双击行上的名字)。
70
+
71
+ ## 从 DBeaver 导入
72
+
73
+ 导入器会在 `%APPDATA%\DBeaverData\workspace*\General\.dbeaver\` 下找 `data-sources.json`,同时兼容 `General` 布局与 Linux/macOS 布局。同目录存在 `credentials-config.json` 时,会用 DBeaver 内置密钥解密(AES-128-CBC、零 IV、前 16 字节前缀——参考 [Tatsh/dbeaver-creds](https://github.com/Tatsh/dbeaver-creds))。勾选的连接被复制进注册表,口令写入 `secrets.json`。
74
+
75
+ 两个细节让真实的 DBeaver 工作区可用:
76
+
77
+ - 通过驱动对话框创建的连接常常只有一条 JDBC URL(`configurationType: URL`),既没有 `database` 字段也没有 `user`。导入器会从 URL 里解析表单缺失的部分,用户名则取自解密后的凭据。
78
+ - 已经配置过的连接不会重复出现。数据源按 类型 + 主机 + 端口 + 库名/服务名/SID + 用户 识别(SQLite 用文件路径);命中时返回 `alreadyImported`,复选框被禁用,行上写着 *已作为 … 添加*。
79
+
80
+ 从未保存过口令的工作区也没问题:连接会以空口令导入,行上显示 **无密码**。补填一次就会一直保留。
81
+
82
+ ## 测试连接
83
+
84
+ **测试** 执行的是真实登录:SQLite 通过 `node:sqlite` 以只读方式打开,其它引擎走 Python 驱动,完成认证并执行 `select 1`(Oracle 是 `select 1 from dual`)。之后行上会显示版本横幅、用户、数据库,以及登录耗时:
85
+
86
+ ```
87
+ Oracle AI Database 26ai Free Release 23.26.3.0.0 · DEMO@FREEPDB1 · select 1 from dual ok
88
+ PostgreSQL 15.19 · demo@demo · select version() ok
89
+ MySQL 8.0.46 · demo@%@demo · select version() ok
90
+ ```
91
+
92
+ 口令错误现在会直接显示驱动自己的报错(`ORA-01017`、`DPY-6005`、`password authentication failed`),而不是笼统的「端口可达」。还没保存的表单也能测试。Python 服务起不来时,探测会退化成 TCP 检查,并明确说明这一点。
93
+
94
+ ## 分组与批量操作
95
+
96
+ 每个数据源都可以带一个可选的 **分组** 标签,在 *更多设置* 里编辑。
97
+
98
+ - **新建** —— 在 **新建分组** 里输入名字。
99
+ - **重命名** —— 双击组名;**删除** 只解除该组下成员的标签。
100
+ - **移动** —— 把数据源行拖到某个分组上,拖到 **未分组** 即清除标签。
101
+ - **重命名数据源** —— 双击它的名字;`id` 永远不变。
102
+ - **选择** —— 每个组头都有全选框,分组内的数据源和散落的一样能参与批量操作。
103
+
104
+ 选中之后,批量条可以测试、切换只读、移入分组,或删除(两步确认)。
105
+
106
+ ## 结构浏览
107
+
108
+ 数据源行上的 **结构** 会打开只读视图:左边是表和视图,右边是所选表的列(名称、类型、可空、默认值、主键标记)。它由 `GET /schema` 提供,该接口只把 `list_tables` / `describe_table` 转发给 MCP 服务——页面无法执行 SQL,也不做缓存。
109
+
110
+ ## 两道安全闸门
111
+
112
+ 两条独立的闸门决定一条语句能不能写入:
113
+
114
+ 1. **全局开关** —— `<runtime>/settings.json` 里的 `allowWrite`,在 *运行时诊断* 标签切换。默认关闭;关闭期间所有写入都会以 `user_error` 拒绝,并提示去打开开关。
115
+ 2. **单源开关** —— 数据源上的 `readOnly`,默认开启。点行上的 *只读* / *读写* 药丸可一键切换;该源允许写入时药丸变成琥珀色。
116
+
117
+ Python 服务在启动时读取全局开关,开关一动 Host 就重启它。在引擎允许的地方,只读还会在驱动层再拦一次(SQLite 用 `mode=ro`,PostgreSQL 用 `default_transaction_read_only`,MySQL 用 `SET SESSION TRANSACTION READ ONLY`)。
118
+
119
+ ## 审计日志
120
+
121
+ 每次 MCP 工具调用都会往 `<runtime>/logs/audit.jsonl`(0600)追加一行 JSON:时间、工具、数据源、耗时、行数、截断到 2000 字符的 SQL,以及失败时的错误。结果行本身从不记录。文件到 512 KiB 会轮转为 `audit.1.jsonl`,日志可用 `auditLog: false` 关闭。
122
+
123
+ 面板按 **你本机的时钟** 显示最近十几次记录;文件里保存的是 UTC,以便跨机器比较。每条记录的时间戳在悬停时会显示完整的本地时间。
124
+
125
+ ## 运行时目录
126
+
127
+ ```
128
+ $DSH_HOME/db-mcp/
129
+ datasources.json # 数据源与分组,从不含口令,0600
130
+ secrets.json # 每个数据源的口令,0600,仅 Host 可读
131
+ settings.json # 全局写开关 + 审计开关,0600
132
+ logs/mcp.log # 脱敏日志
133
+ logs/audit.jsonl # 每次 MCP 工具调用一行,512 KiB 轮转
134
+ .venv/ # 插件内 venv:mcp、psycopg、PyMySQL、oracledb
135
+ ```
136
+
137
+ ## 用 Docker 起测试库
138
+
139
+ 本机什么都不用装;下面每个引擎都是一个用完即弃的容器,带演示账号(`demo` / `dbmcp-demo`)。
140
+
141
+ ```sh
142
+ # PostgreSQL —— 镜像最小、启动最快,推荐先试它
143
+ docker run -d --name dbmcp-pg -p 5432:5432 \
144
+ -e POSTGRES_USER=demo -e POSTGRES_PASSWORD=dbmcp-demo -e POSTGRES_DB=demo \
145
+ postgres:15-alpine
146
+
147
+ # MySQL
148
+ docker run -d --name dbmcp-mysql -p 3306:3306 \
149
+ -e MYSQL_USER=demo -e MYSQL_PASSWORD=dbmcp-demo -e MYSQL_DATABASE=demo \
150
+ -e MYSQL_ROOT_PASSWORD=dbmcp-root mysql:8.0-debian
151
+
152
+ # Oracle Free —— 几个 GB,需要一两分钟才健康
153
+ docker run -d --name dbmcp-oracle -p 1521:1521 -e ORACLE_PASSWORD=dbmcp-demo \
154
+ gvenzl/oracle-free:23-slim
155
+ ```
156
+
157
+ 在 Windows 上,1521 常常落在 Hyper-V 的保留端口段里,`docker run` 会报 *"An attempt was made to access a socket in a way forbidden by its access permissions"*。用 `netsh interface ipv4 show excludedportrange protocol=tcp` 确认,然后映射到别的宿主端口,例如 `-p 11521:1521`,再把数据源指向 11521。
158
+
159
+ 然后用对应的连接串导入或新建数据源:
160
+
161
+ ```
162
+ postgresql://demo:dbmcp-demo@127.0.0.1:5432/demo
163
+ mysql://demo:dbmcp-demo@127.0.0.1:3306/demo
164
+ oracle://system:dbmcp-demo@127.0.0.1:1521/FREEPDB1
165
+ ```
166
+
167
+ ## 排错
168
+
169
+ | 现象 | 原因 | 处理 |
170
+ | ---- | ---- | ---- |
171
+ | 侧栏入口消失,插件卡片显示 *未安装* | `package.json` 带 UTF-8 BOM,Host 的 `JSON.parse` 失败,整个 bundle 被静默跳过 | 用编辑器另存为「UTF-8 无 BOM」,再到 **插件** 面板把插件关掉再打开 |
172
+ | 面板显示 *Host 接口未连接* | Host 半边没挂载(插件被禁用,或 bundle 加载失败) | 检查插件卡片,然后刷新页面 |
173
+ | 改了 `src/host.js` 但没有变化 | Cordis 入口在每个进程里只导入一次 | `POST /plugins/db-mcp/api/reload`;改过 `index.js` 则需重启 DSH |
174
+ | 智能体看不到工具 | Python 服务没起来 | *运行时诊断* 里看工具数与错误,然后点 **重启 MCP** |
175
+ | 重启后立刻报 `unknown tool "mcp__db__…"` | 工具注册随旧进程被释放 | 重启一次 DSH,之后的重启都用 **重启 MCP** |
176
+ | Oracle 登录报 `DPY-6005` | 监听不可达,或主机/端口不对 | `docker ps` 确认容器,再点 *测试* |
177
+ | 首次写入报 `ORA-01950` | Oracle 用户没有表空间配额 | `alter user <用户> quota unlimited on users` |
178
+ | 导入行标红:*incomplete host/database* | 该 DBeaver 条目既没有字段也没有可解析的 URL | 先用 DBeaver 打开一次这个连接,或手工新建数据源 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-db-mcp",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "DSH plugin: import the connections DBeaver already knows, then let the agent query SQLite, PostgreSQL, MySQL and Oracle through eight MCP tools. No DSH files to edit, no data source to retype.",
5
5
  "keywords": [
6
6
  "dsh",
package/src/host.js CHANGED
@@ -33,7 +33,7 @@ const { createBridge } = await import(new URL(`./tools.js?v=${GENERATION}`, impo
33
33
 
34
34
  const API_PREFIX = '/plugins/db-mcp/api'
35
35
  const PLUGIN_NAME = 'dsh-db-mcp'
36
- const PLUGIN_VERSION = '0.7.0'
36
+ const PLUGIN_VERSION = '0.7.1'
37
37
  const SCHEMA_VERSION = 1
38
38
  const REGISTRY_FILE = 'datasources.json'
39
39
  const SECRETS_FILE = 'secrets.json'