mcp-database 0.1.0__tar.gz

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.
@@ -0,0 +1,19 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .venv/
10
+ venv/
11
+ env/
12
+ .env
13
+ .mypy_cache/
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ *.db
17
+ *.sqlite
18
+ *.sqlite3
19
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mergewall Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-database
3
+ Version: 0.1.0
4
+ Summary: MCP server for multi-database access — SQLite, PostgreSQL, MySQL. Query, inspect schema, and manage databases through Claude.
5
+ Author: Mergewall Team
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: claude,database,mcp,model-context-protocol,mysql,postgresql,sqlite
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Database
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Requires-Python: >=3.10
19
+ Requires-Dist: mcp>=1.0.0
20
+ Requires-Dist: pyyaml>=6.0
21
+ Provides-Extra: all
22
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == 'all'
23
+ Requires-Dist: pymysql>=1.1.0; extra == 'all'
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
26
+ Requires-Dist: pytest>=8.0; extra == 'dev'
27
+ Requires-Dist: ruff>=0.4.0; extra == 'dev'
28
+ Provides-Extra: mysql
29
+ Requires-Dist: pymysql>=1.1.0; extra == 'mysql'
30
+ Provides-Extra: postgres
31
+ Requires-Dist: psycopg2-binary>=2.9.0; extra == 'postgres'
32
+ Description-Content-Type: text/markdown
33
+
34
+ # mcp-database
35
+
36
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
37
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
38
+ [![MCP](https://img.shields.io/badge/MCP-compatible-orange)](https://modelcontextprotocol.io)
39
+
40
+ **MCP server for multi-database access — query, inspect schema, and manage SQLite, PostgreSQL, and MySQL databases through Claude.**
41
+
42
+ ## Why mcp-database?
43
+
44
+ | Problem | Solution |
45
+ |---------|----------|
46
+ | Need to query a database from Claude Code / Claude Desktop | One MCP server, multiple database support |
47
+ | Existing database MCP servers are JS/Go only | Pure Python, uses official `mcp` SDK |
48
+ | Worried about accidental writes | Read-only by default, writes opt-in |
49
+ | Don't know the schema | Built-in schema inspection, table info, search |
50
+
51
+ ## Quick Start
52
+
53
+ ```bash
54
+ # Install
55
+ pip install mcp-database
56
+
57
+ # Run with a SQLite database
58
+ MCP_DATABASE_URL=sqlite:///path/to/your.db mcp-database
59
+ ```
60
+
61
+ ### Claude Code Integration
62
+
63
+ ```bash
64
+ # Add to Claude Code
65
+ claude mcp add mcp-database -- mcp-database
66
+
67
+ # Or with a specific database
68
+ claude mcp add mcp-database -e MCP_DATABASE_URL=sqlite:///path/to/db.sqlite -- mcp-database
69
+ ```
70
+
71
+ ### Claude Desktop Integration
72
+
73
+ Add to your `claude_desktop_config.json`:
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "database": {
79
+ "command": "mcp-database",
80
+ "env": {
81
+ "MCP_DATABASE_URL": "sqlite:///path/to/your.db"
82
+ }
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ ## Supported Databases
89
+
90
+ | Database | Status | Install |
91
+ |----------|--------|---------|
92
+ | **SQLite** | Built-in | `pip install mcp-database` |
93
+ | **PostgreSQL** | Optional | `pip install 'mcp-database[postgres]'` |
94
+ | **MySQL** | Optional | `pip install 'mcp-database[mysql]'` |
95
+ | **All** | Optional | `pip install 'mcp-database[all]'` |
96
+
97
+ ## Configuration
98
+
99
+ ### Environment Variables
100
+
101
+ | Variable | Default | Description |
102
+ |----------|---------|-------------|
103
+ | `MCP_DATABASE_URL` | `sqlite:///:memory:` | Database connection URL |
104
+ | `MCP_DATABASE_TYPE` | `sqlite` | Database type: `sqlite`, `postgresql`, `mysql` |
105
+ | `MCP_DATABASE_READ_ONLY` | `true` | Enable read-only mode |
106
+ | `MCP_MAX_ROWS` | `100` | Maximum rows returned per query |
107
+
108
+ ### Connection URLs
109
+
110
+ ```bash
111
+ # SQLite
112
+ MCP_DATABASE_URL=sqlite:///path/to/db.sqlite
113
+ MCP_DATABASE_URL=sqlite:///:memory:
114
+
115
+ # PostgreSQL
116
+ MCP_DATABASE_URL=postgres://user:password@localhost:5432/mydb
117
+ MCP_DATABASE_TYPE=postgresql
118
+
119
+ # MySQL
120
+ MCP_DATABASE_URL=mysql://user:password@localhost:3306/mydb
121
+ MCP_DATABASE_TYPE=mysql
122
+ ```
123
+
124
+ ## Available Tools
125
+
126
+ Once connected, Claude can use these tools:
127
+
128
+ | Tool | Description |
129
+ |------|-------------|
130
+ | `list_databases` | List all configured database connections |
131
+ | `list_tables` | List all tables in a database |
132
+ | `get_table_info` | Get detailed table info (columns, types, row count) |
133
+ | `get_schema` | Get full database schema (CREATE TABLE statements) |
134
+ | `query` | Execute a read-only SQL query (SELECT, SHOW, DESCRIBE) |
135
+ | `execute` | Execute a write statement (INSERT, UPDATE, DELETE) — opt-in only |
136
+ | `sample_rows` | Get sample rows from a table |
137
+ | `search_tables` | Search for tables or columns by keyword |
138
+
139
+ ## Examples
140
+
141
+ Ask Claude things like:
142
+
143
+ - "What tables are in my database?"
144
+ - "Show me the schema for the users table"
145
+ - "Query the top 10 orders by amount"
146
+ - "Find all columns related to 'email'"
147
+ - "Sample some rows from the products table"
148
+
149
+ ## Security
150
+
151
+ - **Read-only by default** — queries are safe, no data modification
152
+ - **Write opt-in** — set `allow_writes=True` and `MCP_DATABASE_READ_ONLY=false` to enable
153
+ - **Read-only detection** — write tool rejects SELECT statements (use `query` instead)
154
+ - **Row limits** — configurable max rows to prevent accidental large result sets
155
+
156
+ ## Development
157
+
158
+ ```bash
159
+ # Clone and install
160
+ git clone https://github.com/Jansen003/mcp-database.git
161
+ cd mcp-database
162
+ pip install -e ".[dev]"
163
+
164
+ # Run tests
165
+ pytest
166
+
167
+ # Run with Inspector UI
168
+ mcp dev src/mcp_database/server.py
169
+ ```
170
+
171
+ ## License
172
+
173
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,140 @@
1
+ # mcp-database
2
+
3
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
4
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
5
+ [![MCP](https://img.shields.io/badge/MCP-compatible-orange)](https://modelcontextprotocol.io)
6
+
7
+ **MCP server for multi-database access — query, inspect schema, and manage SQLite, PostgreSQL, and MySQL databases through Claude.**
8
+
9
+ ## Why mcp-database?
10
+
11
+ | Problem | Solution |
12
+ |---------|----------|
13
+ | Need to query a database from Claude Code / Claude Desktop | One MCP server, multiple database support |
14
+ | Existing database MCP servers are JS/Go only | Pure Python, uses official `mcp` SDK |
15
+ | Worried about accidental writes | Read-only by default, writes opt-in |
16
+ | Don't know the schema | Built-in schema inspection, table info, search |
17
+
18
+ ## Quick Start
19
+
20
+ ```bash
21
+ # Install
22
+ pip install mcp-database
23
+
24
+ # Run with a SQLite database
25
+ MCP_DATABASE_URL=sqlite:///path/to/your.db mcp-database
26
+ ```
27
+
28
+ ### Claude Code Integration
29
+
30
+ ```bash
31
+ # Add to Claude Code
32
+ claude mcp add mcp-database -- mcp-database
33
+
34
+ # Or with a specific database
35
+ claude mcp add mcp-database -e MCP_DATABASE_URL=sqlite:///path/to/db.sqlite -- mcp-database
36
+ ```
37
+
38
+ ### Claude Desktop Integration
39
+
40
+ Add to your `claude_desktop_config.json`:
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "database": {
46
+ "command": "mcp-database",
47
+ "env": {
48
+ "MCP_DATABASE_URL": "sqlite:///path/to/your.db"
49
+ }
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ ## Supported Databases
56
+
57
+ | Database | Status | Install |
58
+ |----------|--------|---------|
59
+ | **SQLite** | Built-in | `pip install mcp-database` |
60
+ | **PostgreSQL** | Optional | `pip install 'mcp-database[postgres]'` |
61
+ | **MySQL** | Optional | `pip install 'mcp-database[mysql]'` |
62
+ | **All** | Optional | `pip install 'mcp-database[all]'` |
63
+
64
+ ## Configuration
65
+
66
+ ### Environment Variables
67
+
68
+ | Variable | Default | Description |
69
+ |----------|---------|-------------|
70
+ | `MCP_DATABASE_URL` | `sqlite:///:memory:` | Database connection URL |
71
+ | `MCP_DATABASE_TYPE` | `sqlite` | Database type: `sqlite`, `postgresql`, `mysql` |
72
+ | `MCP_DATABASE_READ_ONLY` | `true` | Enable read-only mode |
73
+ | `MCP_MAX_ROWS` | `100` | Maximum rows returned per query |
74
+
75
+ ### Connection URLs
76
+
77
+ ```bash
78
+ # SQLite
79
+ MCP_DATABASE_URL=sqlite:///path/to/db.sqlite
80
+ MCP_DATABASE_URL=sqlite:///:memory:
81
+
82
+ # PostgreSQL
83
+ MCP_DATABASE_URL=postgres://user:password@localhost:5432/mydb
84
+ MCP_DATABASE_TYPE=postgresql
85
+
86
+ # MySQL
87
+ MCP_DATABASE_URL=mysql://user:password@localhost:3306/mydb
88
+ MCP_DATABASE_TYPE=mysql
89
+ ```
90
+
91
+ ## Available Tools
92
+
93
+ Once connected, Claude can use these tools:
94
+
95
+ | Tool | Description |
96
+ |------|-------------|
97
+ | `list_databases` | List all configured database connections |
98
+ | `list_tables` | List all tables in a database |
99
+ | `get_table_info` | Get detailed table info (columns, types, row count) |
100
+ | `get_schema` | Get full database schema (CREATE TABLE statements) |
101
+ | `query` | Execute a read-only SQL query (SELECT, SHOW, DESCRIBE) |
102
+ | `execute` | Execute a write statement (INSERT, UPDATE, DELETE) — opt-in only |
103
+ | `sample_rows` | Get sample rows from a table |
104
+ | `search_tables` | Search for tables or columns by keyword |
105
+
106
+ ## Examples
107
+
108
+ Ask Claude things like:
109
+
110
+ - "What tables are in my database?"
111
+ - "Show me the schema for the users table"
112
+ - "Query the top 10 orders by amount"
113
+ - "Find all columns related to 'email'"
114
+ - "Sample some rows from the products table"
115
+
116
+ ## Security
117
+
118
+ - **Read-only by default** — queries are safe, no data modification
119
+ - **Write opt-in** — set `allow_writes=True` and `MCP_DATABASE_READ_ONLY=false` to enable
120
+ - **Read-only detection** — write tool rejects SELECT statements (use `query` instead)
121
+ - **Row limits** — configurable max rows to prevent accidental large result sets
122
+
123
+ ## Development
124
+
125
+ ```bash
126
+ # Clone and install
127
+ git clone https://github.com/Jansen003/mcp-database.git
128
+ cd mcp-database
129
+ pip install -e ".[dev]"
130
+
131
+ # Run tests
132
+ pytest
133
+
134
+ # Run with Inspector UI
135
+ mcp dev src/mcp_database/server.py
136
+ ```
137
+
138
+ ## License
139
+
140
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,158 @@
1
+ # mcp-database
2
+
3
+ [![Python](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org/)
4
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
5
+ [![MCP](https://img.shields.io/badge/MCP-compatible-orange)](https://modelcontextprotocol.io)
6
+
7
+ **多数据库 MCP 服务器 —— 让 Claude 直接查询、探索和管理你的 SQLite、PostgreSQL、MySQL 数据库。**
8
+
9
+ [English](README.md) | 中文
10
+
11
+ ## 为什么需要 mcp-database?
12
+
13
+ | 你的问题 | mcp-database 的解决方案 |
14
+ |---------|----------------------|
15
+ | 想在 Claude Code 里直接查数据库 | 一个 MCP 服务器,支持多种数据库 |
16
+ | 现有的数据库 MCP 要么是 JS 要么是 Go | 纯 Python,基于官方 `mcp` SDK |
17
+ | 担心 Claude 误操作数据 | 默认只读模式,写入需要显式开启 |
18
+ | 不熟悉数据库结构 | 内置表结构探索、列信息、关键词搜索 |
19
+
20
+ ## 快速开始
21
+
22
+ ```bash
23
+ # 安装
24
+ pip install mcp-database
25
+
26
+ # 连接 SQLite 数据库
27
+ MCP_DATABASE_URL=sqlite:///path/to/your.db mcp-database
28
+ ```
29
+
30
+ ### 接入 Claude Code
31
+
32
+ ```bash
33
+ # 一行命令接入
34
+ claude mcp add mcp-database -- mcp-database
35
+
36
+ # 指定数据库文件
37
+ claude mcp add mcp-database -e MCP_DATABASE_URL=sqlite:///path/to/db.sqlite -- mcp-database
38
+ ```
39
+
40
+ ### 接入 Claude Desktop
41
+
42
+ 在 `claude_desktop_config.json` 中添加:
43
+
44
+ ```json
45
+ {
46
+ "mcpServers": {
47
+ "database": {
48
+ "command": "mcp-database",
49
+ "env": {
50
+ "MCP_DATABASE_URL": "sqlite:///path/to/your.db"
51
+ }
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ## 支持的数据库
58
+
59
+ | 数据库 | 状态 | 安装方式 |
60
+ |--------|------|----------|
61
+ | **SQLite** | 内置 | `pip install mcp-database` |
62
+ | **PostgreSQL** | 可选 | `pip install 'mcp-database[postgres]'` |
63
+ | **MySQL** | 可选 | `pip install 'mcp-database[mysql]'` |
64
+ | **全部** | 可选 | `pip install 'mcp-database[all]'` |
65
+
66
+ ## 配置方式
67
+
68
+ ### 环境变量
69
+
70
+ | 变量名 | 默认值 | 说明 |
71
+ |--------|--------|------|
72
+ | `MCP_DATABASE_URL` | `sqlite:///:memory:` | 数据库连接地址 |
73
+ | `MCP_DATABASE_TYPE` | `sqlite` | 数据库类型:`sqlite`、`postgresql`、`mysql` |
74
+ | `MCP_DATABASE_READ_ONLY` | `true` | 是否启用只读模式 |
75
+ | `MCP_MAX_ROWS` | `100` | 单次查询最大返回行数 |
76
+
77
+ ### 连接地址格式
78
+
79
+ ```bash
80
+ # SQLite
81
+ MCP_DATABASE_URL=sqlite:///path/to/db.sqlite
82
+ MCP_DATABASE_URL=sqlite:///:memory:
83
+
84
+ # PostgreSQL
85
+ MCP_DATABASE_URL=postgres://user:password@localhost:5432/mydb
86
+ MCP_DATABASE_TYPE=postgresql
87
+
88
+ # MySQL
89
+ MCP_DATABASE_URL=mysql://user:password@localhost:3306/mydb
90
+ MCP_DATABASE_TYPE=mysql
91
+ ```
92
+
93
+ ## 可用工具
94
+
95
+ 接入后,Claude 可以使用以下工具:
96
+
97
+ | 工具名 | 说明 |
98
+ |--------|------|
99
+ | `list_databases` | 列出所有已配置的数据库连接 |
100
+ | `list_tables` | 列出数据库中的所有表 |
101
+ | `get_table_info` | 获取表的详细信息(列名、类型、行数) |
102
+ | `get_schema` | 获取完整的数据库建表语句 |
103
+ | `query` | 执行只读 SQL 查询(SELECT、SHOW、DESCRIBE) |
104
+ | `execute` | 执行写入语句(INSERT、UPDATE、DELETE)—— 需手动开启 |
105
+ | `sample_rows` | 获取表中的示例数据 |
106
+ | `search_tables` | 按关键词搜索表名和列名 |
107
+
108
+ ## 使用示例
109
+
110
+ 你可以这样问 Claude:
111
+
112
+ - "我的数据库里有哪些表?"
113
+ - "看一下 users 表的结构"
114
+ - "查询金额最大的 10 笔订单"
115
+ - "找一下所有跟 email 相关的字段"
116
+ - "看看 products 表里长什么样"
117
+
118
+ ## 安全设计
119
+
120
+ - **默认只读** —— 查询操作不会修改任何数据
121
+ - **写入需授权** —— 必须设置 `allow_writes=True` 和 `MCP_DATABASE_READ_ONLY=false`
122
+ - **语句检测** —— 写入工具会拒绝 SELECT 语句(应该用 `query`)
123
+ - **行数限制** —— 可配置最大返回行数,防止意外拉取全表
124
+
125
+ ## 开发
126
+
127
+ ```bash
128
+ # 克隆并安装
129
+ git clone https://github.com/Jansen003/mcp-database.git
130
+ cd mcp-database
131
+ pip install -e ".[dev]"
132
+
133
+ # 运行测试
134
+ pytest
135
+
136
+ # 使用 Inspector 调试
137
+ mcp dev src/mcp_database/server.py
138
+ ```
139
+
140
+ ## 项目结构
141
+
142
+ ```
143
+ src/mcp_database/
144
+ __init__.py # 版本信息
145
+ server.py # MCP 服务器主入口(8 个工具)
146
+ config.py # 配置加载(环境变量、URL 解析)
147
+ adapters/
148
+ base.py # 适配器基类(DatabaseAdapter)
149
+ sqlite.py # SQLite 适配器
150
+ postgres.py # PostgreSQL 适配器
151
+ mysql.py # MySQL 适配器
152
+ tests/
153
+ test_sqlite_adapter.py # SQLite 适配器测试(17 个用例)
154
+ ```
155
+
156
+ ## 许可证
157
+
158
+ MIT — 详见 [LICENSE](LICENSE)。
@@ -0,0 +1,53 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "mcp-database"
7
+ version = "0.1.0"
8
+ description = "MCP server for multi-database access — SQLite, PostgreSQL, MySQL. Query, inspect schema, and manage databases through Claude."
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ {name = "Mergewall Team"}
14
+ ]
15
+ keywords = ["mcp", "database", "sqlite", "postgresql", "mysql", "claude", "model-context-protocol"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Programming Language :: Python :: 3.13",
24
+ "Topic :: Database",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ ]
27
+ dependencies = [
28
+ "mcp>=1.0.0",
29
+ "pyyaml>=6.0",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ postgres = ["psycopg2-binary>=2.9.0"]
34
+ mysql = ["pymysql>=1.1.0"]
35
+ all = ["mcp-database[postgres,mysql]"]
36
+ dev = [
37
+ "pytest>=8.0",
38
+ "pytest-asyncio>=0.23",
39
+ "ruff>=0.4.0",
40
+ ]
41
+
42
+ [project.scripts]
43
+ mcp-database = "mcp_database.server:main"
44
+
45
+ [tool.hatch.build.targets.wheel]
46
+ packages = ["src/mcp_database"]
47
+
48
+ [tool.pytest.ini_options]
49
+ asyncio_mode = "auto"
50
+
51
+ [tool.ruff]
52
+ line-length = 120
53
+ target-version = "py310"
@@ -0,0 +1,3 @@
1
+ """mcp-database — MCP server for multi-database access."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,5 @@
1
+ """Allow running as: python -m mcp_database"""
2
+
3
+ from mcp_database.server import main
4
+
5
+ main()
@@ -0,0 +1,6 @@
1
+ """Database adapters for mcp-database."""
2
+
3
+ from mcp_database.adapters.base import DatabaseAdapter
4
+ from mcp_database.adapters.sqlite import SQLiteAdapter
5
+
6
+ __all__ = ["DatabaseAdapter", "SQLiteAdapter"]
@@ -0,0 +1,108 @@
1
+ """Base adapter interface for database connections."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from dataclasses import dataclass
5
+ from typing import Any
6
+
7
+
8
+ @dataclass
9
+ class QueryResult:
10
+ """Result of a SQL query."""
11
+
12
+ columns: list[str]
13
+ rows: list[list[Any]]
14
+ row_count: int
15
+ truncated: bool = False
16
+
17
+ def to_table(self, max_rows: int = 100) -> str:
18
+ """Format result as an aligned text table."""
19
+ if not self.columns:
20
+ return "No results."
21
+
22
+ rows = self.rows[:max_rows]
23
+ # Calculate column widths
24
+ widths = [len(c) for c in self.columns]
25
+ for row in rows:
26
+ for i, val in enumerate(row):
27
+ if i < len(widths):
28
+ widths[i] = max(widths[i], len(str(val)))
29
+
30
+ # Header
31
+ header = " | ".join(c.ljust(widths[i]) for i, c in enumerate(self.columns))
32
+ separator = "-+-".join("-" * w for w in widths)
33
+ lines = [header, separator]
34
+
35
+ # Rows
36
+ for row in rows:
37
+ line = " | ".join(str(row[i]).ljust(widths[i]) if i < len(row) else "" for i in range(len(self.columns)))
38
+ lines.append(line)
39
+
40
+ result = "\n".join(lines)
41
+ if self.truncated:
42
+ result += f"\n... ({self.row_count} total rows, showing first {max_rows})"
43
+ return result
44
+
45
+
46
+ @dataclass
47
+ class TableInfo:
48
+ """Information about a database table."""
49
+
50
+ name: str
51
+ columns: list[dict[str, Any]]
52
+ row_count: int | None = None
53
+ create_sql: str | None = None
54
+
55
+
56
+ class DatabaseAdapter(ABC):
57
+ """Abstract base class for database adapters."""
58
+
59
+ @abstractmethod
60
+ def connect(self) -> None:
61
+ """Establish connection to the database."""
62
+
63
+ @abstractmethod
64
+ def disconnect(self) -> None:
65
+ """Close the database connection."""
66
+
67
+ @abstractmethod
68
+ def test_connection(self) -> bool:
69
+ """Test if the connection is alive."""
70
+
71
+ @abstractmethod
72
+ def list_databases(self) -> list[str]:
73
+ """List available databases (if applicable)."""
74
+
75
+ @abstractmethod
76
+ def list_tables(self, database: str | None = None) -> list[str]:
77
+ """List all tables in the database."""
78
+
79
+ @abstractmethod
80
+ def get_table_info(self, table: str, database: str | None = None) -> TableInfo:
81
+ """Get detailed information about a table."""
82
+
83
+ @abstractmethod
84
+ def get_schema(self, database: str | None = None) -> str:
85
+ """Get the full database schema as formatted text."""
86
+
87
+ @abstractmethod
88
+ def execute_query(self, sql: str, database: str | None = None, max_rows: int = 100) -> QueryResult:
89
+ """Execute a read-only SQL query."""
90
+
91
+ def execute_write(self, sql: str, database: str | None = None) -> int:
92
+ """Execute a write SQL query (INSERT/UPDATE/DELETE). Returns affected rows.
93
+
94
+ Raises NotImplementedError if the adapter is read-only.
95
+ """
96
+ raise NotImplementedError("This adapter is read-only. Use execute_query() for SELECT statements.")
97
+
98
+ @property
99
+ @abstractmethod
100
+ def db_type(self) -> str:
101
+ """Return the database type name (e.g., 'sqlite', 'postgresql', 'mysql')."""
102
+
103
+ def _is_read_only_query(self, sql: str) -> bool:
104
+ """Check if a query is read-only (SELECT, SHOW, DESCRIBE, EXPLAIN)."""
105
+ normalized = sql.strip().upper()
106
+ # Allow SELECT, SHOW, DESCRIBE, EXPLAIN, WITH (CTE leading to SELECT)
107
+ read_only_prefixes = ("SELECT", "SHOW", "DESCRIBE", "DESC", "EXPLAIN", "WITH", "PRAGMA")
108
+ return any(normalized.startswith(prefix) for prefix in read_only_prefixes)