diff --git a/README.md b/README.md index ede4162..559a11b 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The official `@modelcontextprotocol/server-postgres` supports only **one databas - **One config file** with all connections - **Hot reload** — add a database to config, it's immediately available (no restart) - **`--label` filter** — restrict access to a single database per project (isolation) -- **Read-only** — all queries run inside `BEGIN TRANSACTION READ ONLY` (safety) +- **Read-only by default** — queries run inside `BEGIN TRANSACTION READ ONLY` unless a connection sets `readOnly: false` ### Architecture @@ -47,8 +47,8 @@ The official `@modelcontextprotocol/server-postgres` supports only **one databas | Tool | Parameters | Description | |---|---|---| -| `pg_list_databases` | — | List all available databases | -| `pg_query` | `database`, `query` | Execute read-only SQL query | +| `pg_list_databases` | — | List all available databases with `RO`/`RW` access mode | +| `pg_query` | `database`, `query` | Execute SQL query (`readOnly: true` blocks writes; `readOnly: false` allows writes) | | `pg_list_tables` | `database`, `schema?` | List tables with row counts | | `pg_describe_table` | `database`, `table`, `schema?` | Column types, PK, FK, defaults | @@ -76,7 +76,8 @@ Create `~/.mcp-postgres/config.json` (or any path you like): "user": "readonly_user", "password": "secret", "database": "myapp", - "enabled": true + "enabled": true, + "readOnly": true }, { "label": "staging", @@ -85,12 +86,15 @@ Create `~/.mcp-postgres/config.json` (or any path you like): "user": "dev", "password": "dev123", "database": "myapp_staging", - "enabled": true + "enabled": true, + "readOnly": false } ] } ``` +`readOnly` defaults to `true`. Set it to `false` only for databases where write queries are intentionally allowed. + Object format also supported (keyed by any identifier): ```json @@ -214,8 +218,8 @@ AI calls: pg_query(database="production", query="SELECT * FROM orders ORDER BY c ### Security -- All queries execute inside `READ ONLY` transactions -- Write operations (`INSERT`, `UPDATE`, `DELETE`, `DROP`) will fail +- Connections are read-only by default and execute inside `READ ONLY` transactions +- Write operations (`INSERT`, `UPDATE`, `DELETE`, `DROP`) fail unless that connection sets `readOnly: false` - Connection timeout: 10 seconds - Query timeout: 30 seconds - Config file with passwords is excluded from git via `.gitignore` @@ -229,7 +233,7 @@ AI calls: pg_query(database="production", query="SELECT * FROM orders ORDER BY c | Config format | Connection string in args | JSON file | | Hot reload | No (restart required) | Yes | | Per-project isolation | N/A | `--label` filter | -| Query safety | Full access | Read-only only | +| Query safety | Full access | Read-only by default; per-connection write mode with `readOnly: false` | | Processes needed for 5 DBs | 5 | 1 | --- @@ -245,7 +249,7 @@ AI calls: pg_query(database="production", query="SELECT * FROM orders ORDER BY c - **Один конфиг** со всеми подключениями - **Hot reload** — добавил БД в конфиг, она сразу доступна (без рестарта) - **Фильтр `--label`** — ограничивает доступ одной БД для проекта (изоляция) -- **Только чтение** — все запросы в `BEGIN TRANSACTION READ ONLY` (безопасность) +- **Только чтение по умолчанию** — запросы выполняются в `BEGIN TRANSACTION READ ONLY`, если подключение не указано с `readOnly: false` ### Архитектура @@ -275,8 +279,8 @@ AI calls: pg_query(database="production", query="SELECT * FROM orders ORDER BY c | Инструмент | Параметры | Описание | |---|---|---| -| `pg_list_databases` | — | Список доступных БД | -| `pg_query` | `database`, `query` | Read-only SQL запрос | +| `pg_list_databases` | — | Список доступных БД с режимом доступа `RO`/`RW` | +| `pg_query` | `database`, `query` | SQL запрос (`readOnly: true` запрещает запись; `readOnly: false` разрешает запись) | | `pg_list_tables` | `database`, `schema?` | Таблицы с количеством строк | | `pg_describe_table` | `database`, `table`, `schema?` | Колонки, типы, PK, FK, defaults | @@ -304,12 +308,25 @@ npm install "user": "readonly_user", "password": "secret", "database": "myapp", - "enabled": true + "enabled": true, + "readOnly": true + }, + { + "label": "staging", + "host": "localhost", + "port": 5432, + "user": "dev", + "password": "dev123", + "database": "myapp_staging", + "enabled": true, + "readOnly": false } ] } ``` +`readOnly` по умолчанию равен `true`. Указывайте `false` только для БД, где запись через MCP действительно нужна. + #### 3. Добавьте в Claude Code Одной командой: @@ -368,13 +385,13 @@ MCP_POSTGRES_CONFIG=/path/to/config.json node dist/index.js | Формат конфига | Connection string в аргументах | JSON файл | | Hot reload | Нет (нужен рестарт) | Да | | Изоляция по проектам | Нет | Фильтр `--label` | -| Безопасность запросов | Полный доступ | Только чтение | +| Безопасность запросов | Полный доступ | Только чтение по умолчанию; режим записи через `readOnly: false` для конкретного подключения | | Процессов для 5 БД | 5 | 1 | ### Безопасность -- Все запросы выполняются в `READ ONLY` транзакциях -- Операции записи (`INSERT`, `UPDATE`, `DELETE`, `DROP`) завершатся ошибкой +- Подключения по умолчанию работают в `READ ONLY` транзакциях +- Операции записи (`INSERT`, `UPDATE`, `DELETE`, `DROP`) завершатся ошибкой, если для подключения не указано `readOnly: false` - Таймаут подключения: 10 сек, таймаут запроса: 30 сек - Конфиг с паролями исключён из git через `.gitignore` - `--label` не даёт ИИ видеть чужие базы данных @@ -392,7 +409,7 @@ MCP_POSTGRES_CONFIG=/path/to/config.json node dist/index.js - **一个配置文件**包含所有连接 - **热重载** — 在配置中添加数据库,立即可用(无需重启) - **`--label` 过滤器** — 限制项目只能访问单个数据库(隔离) -- **只读模式** — 所有查询在 `BEGIN TRANSACTION READ ONLY` 中执行(安全) +- **默认只读模式** — 查询在 `BEGIN TRANSACTION READ ONLY` 中执行,除非连接配置为 `readOnly: false` ### 架构 @@ -422,8 +439,8 @@ MCP_POSTGRES_CONFIG=/path/to/config.json node dist/index.js | 工具 | 参数 | 描述 | |---|---|---| -| `pg_list_databases` | — | 列出所有可用数据库 | -| `pg_query` | `database`, `query` | 执行只读SQL查询 | +| `pg_list_databases` | — | 列出所有可用数据库及 `RO`/`RW` 访问模式 | +| `pg_query` | `database`, `query` | 执行 SQL 查询(`readOnly: true` 阻止写入;`readOnly: false` 允许写入) | | `pg_list_tables` | `database`, `schema?` | 列出表及行数估计 | | `pg_describe_table` | `database`, `table`, `schema?` | 列类型、主键、外键、默认值 | @@ -451,12 +468,25 @@ npm install "user": "readonly_user", "password": "secret", "database": "myapp", - "enabled": true + "enabled": true, + "readOnly": true + }, + { + "label": "staging", + "host": "localhost", + "port": 5432, + "user": "dev", + "password": "dev123", + "database": "myapp_staging", + "enabled": true, + "readOnly": false } ] } ``` +`readOnly` 默认为 `true`。只有在确实需要 MCP 写入该数据库时才设置为 `false`。 + #### 3. 添加到 Claude Code 一条命令: @@ -515,13 +545,13 @@ MCP_POSTGRES_CONFIG=/path/to/config.json node dist/index.js | 配置格式 | 参数中的连接字符串 | JSON 文件 | | 热重载 | 否(需重启) | 是 | | 按项目隔离 | 无 | `--label` 过滤器 | -| 查询安全性 | 完全访问 | 仅只读 | +| 查询安全性 | 完全访问 | 默认只读;可通过单个连接的 `readOnly: false` 开启写入模式 | | 5个数据库需要的进程数 | 5 | 1 | ### 安全性 -- 所有查询在 `READ ONLY` 事务中执行 -- 写操作(`INSERT`、`UPDATE`、`DELETE`、`DROP`)会失败 +- 连接默认在 `READ ONLY` 事务中执行查询 +- 写操作(`INSERT`、`UPDATE`、`DELETE`、`DROP`)会失败,除非该连接设置了 `readOnly: false` - 连接超时:10秒,查询超时:30秒 - 包含密码的配置文件通过 `.gitignore` 排除在git之外 - `--label` 防止AI访问无关数据库 diff --git a/src/index.ts b/src/index.ts index a5b8b92..e7b8b7e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -399,9 +399,8 @@ server.tool( const addr = c.url ? "(connection string)" : `${c.user}@${c.host}:${c.port}/${c.database}`; - const flags: string[] = []; + const flags: string[] = [c.readOnly ? "RO" : "RW"]; if (c.ssl) flags.push("SSL"); - if (!c.readOnly) flags.push("RW"); const suffix = flags.length ? ` [${flags.join(", ")}]` : ""; return `- ${c.label}: ${addr}${suffix}`; });