sqlsaber 0.29.1__tar.gz → 0.30.1__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.
Potentially problematic release.
This version of sqlsaber might be problematic. Click here for more details.
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/AGENT.md +1 -2
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/CLAUDE.md +1 -2
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/PKG-INFO +3 -44
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/README.md +2 -42
- sqlsaber-0.30.1/docs/src/assets/sqlsaber.gif +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/changelog.md +18 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/guides/getting-started.mdx +2 -2
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/index.mdx +6 -7
- sqlsaber-0.30.1/docs/src/styles/global.css +243 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/pyproject.toml +1 -4
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/agents/base.py +1 -1
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/agents/pydantic_ai_agent.py +39 -17
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/models.py +1 -1
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/streaming.py +2 -11
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/config/api_keys.py +1 -1
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/config/oauth_tokens.py +2 -2
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/config/settings.py +1 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/theme/manager.py +4 -1
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/tools/__init__.py +0 -5
- sqlsaber-0.30.1/src/sqlsaber/tools/base.py +42 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/tools/registry.py +6 -39
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/tools/sql_tools.py +0 -42
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_theme/test_manager.py +1 -3
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_tools/test_base.py +0 -3
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_tools/test_registry.py +1 -19
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_tools/test_sql_tools.py +0 -4
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/uv.lock +852 -947
- sqlsaber-0.29.1/docs/src/assets/sqlsaber-hero.svg +0 -48
- sqlsaber-0.29.1/docs/src/styles/global.css +0 -209
- sqlsaber-0.29.1/src/sqlsaber/agents/mcp.py +0 -21
- sqlsaber-0.29.1/src/sqlsaber/mcp/__init__.py +0 -5
- sqlsaber-0.29.1/src/sqlsaber/mcp/mcp.py +0 -129
- sqlsaber-0.29.1/src/sqlsaber/tools/base.py +0 -73
- sqlsaber-0.29.1/src/sqlsaber/tools/enums.py +0 -19
- sqlsaber-0.29.1/src/sqlsaber/tools/instructions.py +0 -231
- sqlsaber-0.29.1/tests/test_tools/test_instructions.py +0 -206
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/.github/workflows/claude-code-review.yml +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/.github/workflows/claude.yml +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/.github/workflows/deploy-docs.yml +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/.github/workflows/publish.yml +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/.github/workflows/test.yml +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/.gitignore +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/.python-version +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/LICENSE +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/.gitignore +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/.vscode/extensions.json +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/.vscode/launch.json +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/CLAUDE.md +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/astro.config.mjs +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/package-lock.json +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/package.json +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/public/CNAME +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/public/favicon.svg +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/guides/authentication.mdx +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/guides/database-setup.mdx +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/guides/memory.mdx +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/guides/models.mdx +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/guides/queries.mdx +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/guides/threads.md +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/installation.mdx +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content/docs/reference/commands.md +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/src/content.config.ts +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/docs/tsconfig.json +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/legislators.db +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/pytest.ini +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/sqlsaber.gif +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/sqlsaber.svg +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/__main__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/agents/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/application/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/application/auth_setup.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/application/db_setup.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/application/model_selection.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/application/prompts.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/auth.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/commands.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/completers.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/database.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/display.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/interactive.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/memory.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/onboarding.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/theme.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/cli/threads.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/config/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/config/auth.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/config/database.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/config/oauth_flow.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/config/providers.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/database/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/database/base.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/database/csv.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/database/duckdb.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/database/mysql.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/database/postgresql.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/database/resolver.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/database/schema.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/database/sqlite.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/memory/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/memory/manager.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/memory/storage.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/theme/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/threads/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/threads/storage.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/src/sqlsaber/tools/sql_guard.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/conftest.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_cli/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_cli/test_auth_reset.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_cli/test_commands.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_cli/test_threads.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_config/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_config/test_database.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_config/test_oauth.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_config/test_providers.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_config/test_settings.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database/test_connection.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database/test_csv_connection.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database/test_csv_module.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database/test_duckdb_module.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database/test_schema.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database/test_schema_display.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database/test_sqlite_module.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database/test_timeout.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_database_resolver.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_threads_storage.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_tools/__init__.py +0 -0
- {sqlsaber-0.29.1 → sqlsaber-0.30.1}/tests/test_tools/test_sql_guard.py +0 -0
|
@@ -11,9 +11,8 @@
|
|
|
11
11
|
## Architecture
|
|
12
12
|
|
|
13
13
|
- **CLI App**: Agentic SQL assistant for natural language to SQL
|
|
14
|
-
- **Core modules**: `agents/` (AI logic), `cli/` (commands), `database/` (connections)
|
|
14
|
+
- **Core modules**: `agents/` (AI logic), `cli/` (commands), `database/` (connections)
|
|
15
15
|
- **Database support**: PostgreSQL, SQLite, MySQL via asyncpg/aiosqlite/aiomysql
|
|
16
|
-
- **MCP integration**: Exposes tools via `sqlsaber-mcp` for Claude Code and other MCP clients
|
|
17
16
|
|
|
18
17
|
## Code Style
|
|
19
18
|
|
|
@@ -9,9 +9,8 @@
|
|
|
9
9
|
## Architecture
|
|
10
10
|
|
|
11
11
|
- **CLI App**: Agentic SQL assistant for natural language to SQL
|
|
12
|
-
- **Core modules**: `agents/` (AI logic), `cli/` (commands), `database/` (connections)
|
|
12
|
+
- **Core modules**: `agents/` (AI logic), `cli/` (commands), `database/` (connections)
|
|
13
13
|
- **Database support**: PostgreSQL, SQLite, MySQL via asyncpg/aiosqlite/aiomysql
|
|
14
|
-
- **MCP integration**: Exposes tools via `sqlsaber-mcp` for Claude Code and other MCP clients
|
|
15
14
|
|
|
16
15
|
## Code Style
|
|
17
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlsaber
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.30.1
|
|
4
4
|
Summary: SQLsaber - Open-source agentic SQL assistant
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -9,7 +9,6 @@ Requires-Dist: aiosqlite>=0.21.0
|
|
|
9
9
|
Requires-Dist: asyncpg>=0.30.0
|
|
10
10
|
Requires-Dist: cyclopts>=3.22.1
|
|
11
11
|
Requires-Dist: duckdb>=0.9.2
|
|
12
|
-
Requires-Dist: fastmcp>=2.9.0
|
|
13
12
|
Requires-Dist: httpx>=0.28.1
|
|
14
13
|
Requires-Dist: keyring>=25.6.0
|
|
15
14
|
Requires-Dist: platformdirs>=4.0.0
|
|
@@ -44,10 +43,7 @@ Ask your questions in natural language and `sqlsaber` will gather the right cont
|
|
|
44
43
|
- [Resume Past Conversation](#resume-past-conversation)
|
|
45
44
|
- [Database Selection](#database-selection)
|
|
46
45
|
- [Examples](#examples)
|
|
47
|
-
|
|
48
|
-
- [Starting the MCP Server](#starting-the-mcp-server)
|
|
49
|
-
- [Configuring MCP Clients](#configuring-mcp-clients)
|
|
50
|
-
- [Available MCP Tools](#available-mcp-tools)
|
|
46
|
+
|
|
51
47
|
- [How It Works](#how-it-works)
|
|
52
48
|
- [Contributing](#contributing)
|
|
53
49
|
- [License](#license)
|
|
@@ -60,7 +56,7 @@ Ask your questions in natural language and `sqlsaber` will gather the right cont
|
|
|
60
56
|
- Interactive REPL mode
|
|
61
57
|
- Conversation threads (store, display, and resume conversations)
|
|
62
58
|
- Support for PostgreSQL, MySQL, SQLite, DuckDB, and CSVs
|
|
63
|
-
|
|
59
|
+
|
|
64
60
|
- Extended thinking mode for select models (Anthropic, OpenAI, Google, Groq)
|
|
65
61
|
- Beautiful formatted output
|
|
66
62
|
|
|
@@ -220,43 +216,6 @@ saber "show me orders with customer details for this week"
|
|
|
220
216
|
saber "which products had the highest sales growth last quarter?"
|
|
221
217
|
```
|
|
222
218
|
|
|
223
|
-
## MCP Server Integration
|
|
224
|
-
|
|
225
|
-
SQLSaber includes an MCP (Model Context Protocol) server that allows AI agents like Claude Code to directly leverage tools available in SQLSaber.
|
|
226
|
-
|
|
227
|
-
### Starting the MCP Server
|
|
228
|
-
|
|
229
|
-
Run the MCP server using uvx:
|
|
230
|
-
|
|
231
|
-
```bash
|
|
232
|
-
uvx --from sqlsaber saber-mcp
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
### Configuring MCP Clients
|
|
236
|
-
|
|
237
|
-
#### Claude Code
|
|
238
|
-
|
|
239
|
-
Add SQLSaber as an MCP server in Claude Code:
|
|
240
|
-
|
|
241
|
-
```bash
|
|
242
|
-
claude mcp add sqlsaber -- uvx --from sqlsaber saber-mcp
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
#### Other MCP Clients
|
|
246
|
-
|
|
247
|
-
For other MCP clients, configure them to run the command: `uvx --from sqlsaber saber-mcp`
|
|
248
|
-
|
|
249
|
-
### Available MCP Tools
|
|
250
|
-
|
|
251
|
-
Once connected, the MCP client will have access to these tools:
|
|
252
|
-
|
|
253
|
-
- `get_databases()` - Lists all configured databases
|
|
254
|
-
- `list_tables(database)` - Get all tables in a database with row counts
|
|
255
|
-
- `introspect_schema(database, table_pattern?)` - Get detailed schema information
|
|
256
|
-
- `execute_sql(database, query, limit?)` - Execute SQL queries (read-only)
|
|
257
|
-
|
|
258
|
-
The MCP server uses your existing SQLSaber database configurations, so make sure to set up your databases using `saber db add` first.
|
|
259
|
-
|
|
260
219
|
## How It Works
|
|
261
220
|
|
|
262
221
|
SQLsaber uses a multi-step agentic process to gather the right context and execute SQL queries to answer your questions:
|
|
@@ -22,10 +22,7 @@ Ask your questions in natural language and `sqlsaber` will gather the right cont
|
|
|
22
22
|
- [Resume Past Conversation](#resume-past-conversation)
|
|
23
23
|
- [Database Selection](#database-selection)
|
|
24
24
|
- [Examples](#examples)
|
|
25
|
-
|
|
26
|
-
- [Starting the MCP Server](#starting-the-mcp-server)
|
|
27
|
-
- [Configuring MCP Clients](#configuring-mcp-clients)
|
|
28
|
-
- [Available MCP Tools](#available-mcp-tools)
|
|
25
|
+
|
|
29
26
|
- [How It Works](#how-it-works)
|
|
30
27
|
- [Contributing](#contributing)
|
|
31
28
|
- [License](#license)
|
|
@@ -38,7 +35,7 @@ Ask your questions in natural language and `sqlsaber` will gather the right cont
|
|
|
38
35
|
- Interactive REPL mode
|
|
39
36
|
- Conversation threads (store, display, and resume conversations)
|
|
40
37
|
- Support for PostgreSQL, MySQL, SQLite, DuckDB, and CSVs
|
|
41
|
-
|
|
38
|
+
|
|
42
39
|
- Extended thinking mode for select models (Anthropic, OpenAI, Google, Groq)
|
|
43
40
|
- Beautiful formatted output
|
|
44
41
|
|
|
@@ -198,43 +195,6 @@ saber "show me orders with customer details for this week"
|
|
|
198
195
|
saber "which products had the highest sales growth last quarter?"
|
|
199
196
|
```
|
|
200
197
|
|
|
201
|
-
## MCP Server Integration
|
|
202
|
-
|
|
203
|
-
SQLSaber includes an MCP (Model Context Protocol) server that allows AI agents like Claude Code to directly leverage tools available in SQLSaber.
|
|
204
|
-
|
|
205
|
-
### Starting the MCP Server
|
|
206
|
-
|
|
207
|
-
Run the MCP server using uvx:
|
|
208
|
-
|
|
209
|
-
```bash
|
|
210
|
-
uvx --from sqlsaber saber-mcp
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### Configuring MCP Clients
|
|
214
|
-
|
|
215
|
-
#### Claude Code
|
|
216
|
-
|
|
217
|
-
Add SQLSaber as an MCP server in Claude Code:
|
|
218
|
-
|
|
219
|
-
```bash
|
|
220
|
-
claude mcp add sqlsaber -- uvx --from sqlsaber saber-mcp
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
#### Other MCP Clients
|
|
224
|
-
|
|
225
|
-
For other MCP clients, configure them to run the command: `uvx --from sqlsaber saber-mcp`
|
|
226
|
-
|
|
227
|
-
### Available MCP Tools
|
|
228
|
-
|
|
229
|
-
Once connected, the MCP client will have access to these tools:
|
|
230
|
-
|
|
231
|
-
- `get_databases()` - Lists all configured databases
|
|
232
|
-
- `list_tables(database)` - Get all tables in a database with row counts
|
|
233
|
-
- `introspect_schema(database, table_pattern?)` - Get detailed schema information
|
|
234
|
-
- `execute_sql(database, query, limit?)` - Execute SQL queries (read-only)
|
|
235
|
-
|
|
236
|
-
The MCP server uses your existing SQLSaber database configurations, so make sure to set up your databases using `saber db add` first.
|
|
237
|
-
|
|
238
198
|
## How It Works
|
|
239
199
|
|
|
240
200
|
SQLsaber uses a multi-step agentic process to gather the right context and execute SQL queries to answer your questions:
|
|
Binary file
|
|
@@ -7,6 +7,24 @@ All notable changes to SQLsaber will be documented here.
|
|
|
7
7
|
|
|
8
8
|
### Unreleased
|
|
9
9
|
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
### v0.30.1 - 2025-10-07
|
|
13
|
+
|
|
14
|
+
#### Fixed
|
|
15
|
+
|
|
16
|
+
- Minor styling fixes
|
|
17
|
+
|
|
18
|
+
### v0.30.0 - 2025-10-07
|
|
19
|
+
|
|
20
|
+
#### Removed
|
|
21
|
+
|
|
22
|
+
- MCP server support and related console scripts (`saber-mcp`, `sqlsaber-mcp`)
|
|
23
|
+
|
|
24
|
+
> `sqlsaber` can still be used as a cli in coding agents like Claude Code, Codex, or Amp.
|
|
25
|
+
>
|
|
26
|
+
> Just *ask* the coding agent to invoke `sqlsaber` cli with your question.
|
|
27
|
+
|
|
10
28
|
### v0.29.1 - 2025-10-05
|
|
11
29
|
|
|
12
30
|
#### Fixed
|
|
@@ -94,8 +94,8 @@ SQLsaber will:
|
|
|
94
94
|
4. Present the results and analysis
|
|
95
95
|
|
|
96
96
|
<script
|
|
97
|
-
src="https://asciinema.org/a/
|
|
98
|
-
id="asciicast-
|
|
97
|
+
src="https://asciinema.org/a/39e71PIiGJetHBPy08L1TOotB.js"
|
|
98
|
+
id="asciicast-39e71PIiGJetHBPy08L1TOotB"
|
|
99
99
|
async="true"
|
|
100
100
|
></script>
|
|
101
101
|
|
|
@@ -5,7 +5,8 @@ template: splash
|
|
|
5
5
|
hero:
|
|
6
6
|
tagline: Stop fighting your database. Gather the right context automatically and get insights with a collaborative SQL agent.
|
|
7
7
|
image:
|
|
8
|
-
|
|
8
|
+
alt: "Animated SQLsaber workflow"
|
|
9
|
+
file: ../../assets/sqlsaber.gif
|
|
9
10
|
actions:
|
|
10
11
|
- text: Get started
|
|
11
12
|
link: /installation/
|
|
@@ -25,8 +26,8 @@ import {
|
|
|
25
26
|
} from "@astrojs/starlight/components";
|
|
26
27
|
|
|
27
28
|
<script
|
|
28
|
-
src="https://asciinema.org/a/
|
|
29
|
-
id="asciicast-
|
|
29
|
+
src="https://asciinema.org/a/746773.js"
|
|
30
|
+
id="asciicast-746773"
|
|
30
31
|
async="true"
|
|
31
32
|
></script>
|
|
32
33
|
|
|
@@ -61,7 +62,7 @@ import {
|
|
|
61
62
|
</li>
|
|
62
63
|
<li class="flex items-center gap-3">
|
|
63
64
|
<Icon name="forward-slash" />
|
|
64
|
-
<span>
|
|
65
|
+
<span>Use as CLI in your coding agent of choice</span>
|
|
65
66
|
</li>
|
|
66
67
|
<li class="flex items-center gap-3">
|
|
67
68
|
<Icon name="sun" />
|
|
@@ -94,10 +95,8 @@ import {
|
|
|
94
95
|
<Code
|
|
95
96
|
code={`# Install SQLsaber
|
|
96
97
|
$ uv tool install sqlsaber
|
|
97
|
-
\n# Add your database
|
|
98
|
-
$ saber db add mydb
|
|
99
98
|
\n# Start asking questions
|
|
100
|
-
$ saber
|
|
99
|
+
$ saber`}
|
|
101
100
|
lang="bash"
|
|
102
101
|
/>
|
|
103
102
|
</div>
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/* Fonts (must be first to satisfy PostCSS import order) */
|
|
2
|
+
@import url("https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap");
|
|
3
|
+
|
|
4
|
+
@layer base, starlight, theme, components, utilities;
|
|
5
|
+
@import "@astrojs/starlight-tailwind";
|
|
6
|
+
@import "tailwindcss/theme.css" layer(theme);
|
|
7
|
+
@import "tailwindcss/utilities.css" layer(utilities);
|
|
8
|
+
|
|
9
|
+
/* Theme tokens (light) */
|
|
10
|
+
:root {
|
|
11
|
+
--background: oklch(1 0 0);
|
|
12
|
+
--foreground: oklch(0.1448 0 0);
|
|
13
|
+
--card: oklch(1 0 0);
|
|
14
|
+
--card-foreground: oklch(0.1448 0 0);
|
|
15
|
+
--popover: oklch(1 0 0);
|
|
16
|
+
--popover-foreground: oklch(0.1448 0 0);
|
|
17
|
+
--primary: oklch(0.5555 0 0);
|
|
18
|
+
--primary-foreground: oklch(0.9851 0 0);
|
|
19
|
+
--secondary: oklch(0.9702 0 0);
|
|
20
|
+
--secondary-foreground: oklch(0.2046 0 0);
|
|
21
|
+
--muted: oklch(0.9702 0 0);
|
|
22
|
+
--muted-foreground: oklch(0.5486 0 0);
|
|
23
|
+
--accent: oklch(0.9702 0 0);
|
|
24
|
+
--accent-foreground: oklch(0.2046 0 0);
|
|
25
|
+
--destructive: oklch(0.583 0.2387 28.4765);
|
|
26
|
+
--destructive-foreground: oklch(0.9702 0 0);
|
|
27
|
+
--border: oklch(0.9219 0 0);
|
|
28
|
+
--input: oklch(0.9219 0 0);
|
|
29
|
+
--ring: oklch(0.709 0 0);
|
|
30
|
+
--chart-1: oklch(0.5555 0 0);
|
|
31
|
+
--chart-2: oklch(0.5555 0 0);
|
|
32
|
+
--chart-3: oklch(0.5555 0 0);
|
|
33
|
+
--chart-4: oklch(0.5555 0 0);
|
|
34
|
+
--chart-5: oklch(0.5555 0 0);
|
|
35
|
+
--sidebar: oklch(0.9851 0 0);
|
|
36
|
+
--sidebar-foreground: oklch(0.1448 0 0);
|
|
37
|
+
--sidebar-primary: oklch(0.2046 0 0);
|
|
38
|
+
--sidebar-primary-foreground: oklch(0.9851 0 0);
|
|
39
|
+
--sidebar-accent: oklch(0.9702 0 0);
|
|
40
|
+
--sidebar-accent-foreground: oklch(0.2046 0 0);
|
|
41
|
+
--sidebar-border: oklch(0.9219 0 0);
|
|
42
|
+
--sidebar-ring: oklch(0.709 0 0);
|
|
43
|
+
--font-sans: Geist Mono, monospace;
|
|
44
|
+
--font-serif: Geist Mono, monospace;
|
|
45
|
+
--font-mono: Geist Mono, monospace;
|
|
46
|
+
--radius: 0rem;
|
|
47
|
+
--shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0);
|
|
48
|
+
--shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0);
|
|
49
|
+
--shadow-sm:
|
|
50
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 1px 2px -1px hsl(0 0% 0% / 0);
|
|
51
|
+
--shadow:
|
|
52
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 1px 2px -1px hsl(0 0% 0% / 0);
|
|
53
|
+
--shadow-md:
|
|
54
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 2px 4px -1px hsl(0 0% 0% / 0);
|
|
55
|
+
--shadow-lg:
|
|
56
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 4px 6px -1px hsl(0 0% 0% / 0);
|
|
57
|
+
--shadow-xl:
|
|
58
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 8px 10px -1px hsl(0 0% 0% / 0);
|
|
59
|
+
--shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0);
|
|
60
|
+
--tracking-normal: 0em;
|
|
61
|
+
--spacing: 0.25rem;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* Dark mode tokens — support common selectors used by Starlight and Tailwind */
|
|
65
|
+
:root.dark,
|
|
66
|
+
html.dark,
|
|
67
|
+
:root[data-theme="dark"],
|
|
68
|
+
html[data-theme="dark"],
|
|
69
|
+
body.dark,
|
|
70
|
+
body[data-theme="dark"] {
|
|
71
|
+
--background: oklch(0.1448 0 0);
|
|
72
|
+
--foreground: oklch(0.9851 0 0);
|
|
73
|
+
--card: oklch(0.2134 0 0);
|
|
74
|
+
--card-foreground: oklch(0.9851 0 0);
|
|
75
|
+
--popover: oklch(0.2686 0 0);
|
|
76
|
+
--popover-foreground: oklch(0.9851 0 0);
|
|
77
|
+
--primary: oklch(0.5555 0 0);
|
|
78
|
+
--primary-foreground: oklch(0.9851 0 0);
|
|
79
|
+
--secondary: oklch(0.2686 0 0);
|
|
80
|
+
--secondary-foreground: oklch(0.9851 0 0);
|
|
81
|
+
--muted: oklch(0.2686 0 0);
|
|
82
|
+
--muted-foreground: oklch(0.709 0 0);
|
|
83
|
+
--accent: oklch(0.3715 0 0);
|
|
84
|
+
--accent-foreground: oklch(0.9851 0 0);
|
|
85
|
+
--destructive: oklch(0.7022 0.1892 22.2279);
|
|
86
|
+
--destructive-foreground: oklch(0.2686 0 0);
|
|
87
|
+
--border: oklch(0.3407 0 0);
|
|
88
|
+
--input: oklch(0.4386 0 0);
|
|
89
|
+
--ring: oklch(0.5555 0 0);
|
|
90
|
+
--chart-1: oklch(0.5555 0 0);
|
|
91
|
+
--chart-2: oklch(0.5555 0 0);
|
|
92
|
+
--chart-3: oklch(0.5555 0 0);
|
|
93
|
+
--chart-4: oklch(0.5555 0 0);
|
|
94
|
+
--chart-5: oklch(0.5555 0 0);
|
|
95
|
+
--sidebar: oklch(0.2046 0 0);
|
|
96
|
+
--sidebar-foreground: oklch(0.9851 0 0);
|
|
97
|
+
--sidebar-primary: oklch(0.9851 0 0);
|
|
98
|
+
--sidebar-primary-foreground: oklch(0.2046 0 0);
|
|
99
|
+
--sidebar-accent: oklch(0.2686 0 0);
|
|
100
|
+
--sidebar-accent-foreground: oklch(0.9851 0 0);
|
|
101
|
+
--sidebar-border: oklch(1 0 0);
|
|
102
|
+
--sidebar-ring: oklch(0.4386 0 0);
|
|
103
|
+
--font-sans: Geist Mono, monospace;
|
|
104
|
+
--font-serif: Geist Mono, monospace;
|
|
105
|
+
--font-mono: Geist Mono, monospace;
|
|
106
|
+
--radius: 0rem;
|
|
107
|
+
--shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0);
|
|
108
|
+
--shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0);
|
|
109
|
+
--shadow-sm:
|
|
110
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 1px 2px -1px hsl(0 0% 0% / 0);
|
|
111
|
+
--shadow:
|
|
112
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 1px 2px -1px hsl(0 0% 0% / 0);
|
|
113
|
+
--shadow-md:
|
|
114
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 2px 4px -1px hsl(0 0% 0% / 0);
|
|
115
|
+
--shadow-lg:
|
|
116
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 4px 6px -1px hsl(0 0% 0% / 0);
|
|
117
|
+
--shadow-xl:
|
|
118
|
+
0px 1px 0px 0px hsl(0 0% 0% / 0), 0px 8px 10px -1px hsl(0 0% 0% / 0);
|
|
119
|
+
--shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Map tokens into Tailwind theme so utilities like bg-background work */
|
|
123
|
+
@theme inline {
|
|
124
|
+
--color-background: var(--background);
|
|
125
|
+
--color-foreground: var(--foreground);
|
|
126
|
+
--color-card: var(--card);
|
|
127
|
+
--color-card-foreground: var(--card-foreground);
|
|
128
|
+
--color-popover: var(--popover);
|
|
129
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
130
|
+
--color-primary: var(--primary);
|
|
131
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
132
|
+
--color-secondary: var(--secondary);
|
|
133
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
134
|
+
--color-muted: var(--muted);
|
|
135
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
136
|
+
--color-accent: var(--accent);
|
|
137
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
138
|
+
--color-destructive: var(--destructive);
|
|
139
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
140
|
+
--color-border: var(--border);
|
|
141
|
+
--color-input: var(--input);
|
|
142
|
+
--color-ring: var(--ring);
|
|
143
|
+
--color-chart-1: var(--chart-1);
|
|
144
|
+
--color-chart-2: var(--chart-2);
|
|
145
|
+
--color-chart-3: var(--chart-3);
|
|
146
|
+
--color-chart-4: var(--chart-4);
|
|
147
|
+
--color-chart-5: var(--chart-5);
|
|
148
|
+
--color-sidebar: var(--sidebar);
|
|
149
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
150
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
151
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
152
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
153
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
154
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
155
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
156
|
+
|
|
157
|
+
/* Accent scale for Starlight (use your accent tone across the scale) */
|
|
158
|
+
--color-accent-50: var(--accent);
|
|
159
|
+
--color-accent-100: var(--accent);
|
|
160
|
+
--color-accent-200: var(--accent);
|
|
161
|
+
--color-accent-300: var(--accent);
|
|
162
|
+
--color-accent-400: var(--accent);
|
|
163
|
+
--color-accent-500: var(--accent);
|
|
164
|
+
--color-accent-600: var(--accent);
|
|
165
|
+
--color-accent-700: var(--accent);
|
|
166
|
+
--color-accent-800: var(--accent);
|
|
167
|
+
--color-accent-900: var(--accent);
|
|
168
|
+
--color-accent-950: var(--accent);
|
|
169
|
+
|
|
170
|
+
--font-sans: var(--font-sans);
|
|
171
|
+
--font-mono: var(--font-mono);
|
|
172
|
+
--font-serif: var(--font-serif);
|
|
173
|
+
|
|
174
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
175
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
176
|
+
--radius-lg: var(--radius);
|
|
177
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
178
|
+
|
|
179
|
+
--shadow-2xs: var(--shadow-2xs);
|
|
180
|
+
--shadow-xs: var(--shadow-xs);
|
|
181
|
+
--shadow-sm: var(--shadow-sm);
|
|
182
|
+
--shadow: var(--shadow);
|
|
183
|
+
--shadow-md: var(--shadow-md);
|
|
184
|
+
--shadow-lg: var(--shadow-lg);
|
|
185
|
+
--shadow-xl: var(--shadow-xl);
|
|
186
|
+
--shadow-2xl: var(--shadow-2xl);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Base rules */
|
|
190
|
+
@layer base {
|
|
191
|
+
* {
|
|
192
|
+
@apply border-border outline-ring/50;
|
|
193
|
+
}
|
|
194
|
+
body {
|
|
195
|
+
@apply bg-background text-foreground;
|
|
196
|
+
}
|
|
197
|
+
/* Square buttons & form controls to match theme radius */
|
|
198
|
+
button,
|
|
199
|
+
[role="button"],
|
|
200
|
+
a[role="button"],
|
|
201
|
+
.btn,
|
|
202
|
+
.Button,
|
|
203
|
+
.link-button,
|
|
204
|
+
.sl-button,
|
|
205
|
+
input,
|
|
206
|
+
select,
|
|
207
|
+
textarea {
|
|
208
|
+
border-radius: var(--radius);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Link contrast: use accent-foreground (dark in light mode, light in dark mode) */
|
|
213
|
+
@layer theme {
|
|
214
|
+
a:not([role="button"]):not(.sl-button):not(.link-button) {
|
|
215
|
+
color: var(--accent-foreground);
|
|
216
|
+
}
|
|
217
|
+
a:not([role="button"]):not(.sl-button):not(.link-button):visited {
|
|
218
|
+
color: var(--accent-foreground);
|
|
219
|
+
}
|
|
220
|
+
a:not([role="button"]):not(.sl-button):not(.link-button):hover {
|
|
221
|
+
color: var(--primary);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@layer components {
|
|
226
|
+
/* Expand hero "image" area so embeds like Asciinema have more room */
|
|
227
|
+
.hero > .hero-html {
|
|
228
|
+
width: min(100%, 40rem);
|
|
229
|
+
margin-inline: auto;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.hero > picture,
|
|
233
|
+
.hero > img {
|
|
234
|
+
width: min(100%, 40rem);
|
|
235
|
+
margin-inline: auto;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.hero > picture img,
|
|
239
|
+
.hero > img {
|
|
240
|
+
width: 100%;
|
|
241
|
+
height: auto;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sqlsaber"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.30.1"
|
|
4
4
|
description = "SQLsaber - Open-source agentic SQL assistant"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -15,7 +15,6 @@ dependencies = [
|
|
|
15
15
|
"aiomysql>=0.2.0",
|
|
16
16
|
"aiosqlite>=0.21.0",
|
|
17
17
|
"duckdb>=0.9.2",
|
|
18
|
-
"fastmcp>=2.9.0",
|
|
19
18
|
"cyclopts>=3.22.1",
|
|
20
19
|
"prompt-toolkit>3.0.51",
|
|
21
20
|
"sqlglot[rs]>=27.20.0",
|
|
@@ -34,5 +33,3 @@ packages = ["src/sqlsaber"]
|
|
|
34
33
|
[project.scripts]
|
|
35
34
|
sqlsaber = "sqlsaber.cli.commands:main"
|
|
36
35
|
saber = "sqlsaber.cli.commands:main"
|
|
37
|
-
sqlsaber-mcp = "sqlsaber.mcp.mcp:main"
|
|
38
|
-
saber-mcp = "sqlsaber.mcp.mcp:main"
|
|
@@ -61,7 +61,7 @@ class BaseSQLAgent(ABC):
|
|
|
61
61
|
def _init_tools(self) -> None:
|
|
62
62
|
"""Initialize SQL tools with database connection."""
|
|
63
63
|
# Get all SQL tools and set their database connection
|
|
64
|
-
for tool_name in tool_registry.list_tools(
|
|
64
|
+
for tool_name in tool_registry.list_tools():
|
|
65
65
|
tool = tool_registry.get_tool(tool_name)
|
|
66
66
|
if isinstance(tool, SQLTool):
|
|
67
67
|
tool.set_connection(self.db)
|
|
@@ -24,7 +24,6 @@ from sqlsaber.database import (
|
|
|
24
24
|
SQLiteConnection,
|
|
25
25
|
)
|
|
26
26
|
from sqlsaber.memory.manager import MemoryManager
|
|
27
|
-
from sqlsaber.tools.instructions import InstructionBuilder
|
|
28
27
|
from sqlsaber.tools.registry import tool_registry
|
|
29
28
|
from sqlsaber.tools.sql_tools import SQLTool
|
|
30
29
|
|
|
@@ -43,7 +42,6 @@ class SQLSaberAgent:
|
|
|
43
42
|
self.database_name = database_name
|
|
44
43
|
self.config = Config()
|
|
45
44
|
self.memory_manager = memory_manager or MemoryManager()
|
|
46
|
-
self.instruction_builder = InstructionBuilder(tool_registry)
|
|
47
45
|
self.db_type = self._get_database_type_name()
|
|
48
46
|
|
|
49
47
|
# Thinking configuration (CLI override or config default)
|
|
@@ -61,7 +59,7 @@ class SQLSaberAgent:
|
|
|
61
59
|
|
|
62
60
|
def _configure_sql_tools(self) -> None:
|
|
63
61
|
"""Ensure SQL tools receive the active database connection."""
|
|
64
|
-
for tool_name in tool_registry.list_tools(
|
|
62
|
+
for tool_name in tool_registry.list_tools():
|
|
65
63
|
tool = tool_registry.get_tool(tool_name)
|
|
66
64
|
if isinstance(tool, SQLTool):
|
|
67
65
|
tool.set_connection(self.db_connection)
|
|
@@ -165,30 +163,54 @@ class SQLSaberAgent:
|
|
|
165
163
|
return Agent(model_obj, name="sqlsaber")
|
|
166
164
|
|
|
167
165
|
def _setup_system_prompt(self, agent: Agent) -> None:
|
|
168
|
-
"""
|
|
166
|
+
"""Configure the agent's system prompt using a simple prompt string."""
|
|
169
167
|
if not self.is_oauth:
|
|
170
168
|
|
|
171
169
|
@agent.system_prompt(dynamic=True)
|
|
172
170
|
async def sqlsaber_system_prompt(ctx: RunContext) -> str:
|
|
173
|
-
|
|
174
|
-
db_type=self.db_type
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
# Add memory context if available
|
|
178
|
-
mem = ""
|
|
179
|
-
if self.database_name:
|
|
180
|
-
mem = self.memory_manager.format_memories_for_prompt(
|
|
181
|
-
self.database_name
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
parts = [p for p in (instructions, mem) if p and p.strip()]
|
|
185
|
-
return "\n\n".join(parts) if parts else ""
|
|
171
|
+
return self.system_prompt_text(include_memory=True)
|
|
186
172
|
else:
|
|
187
173
|
|
|
188
174
|
@agent.system_prompt(dynamic=True)
|
|
189
175
|
async def sqlsaber_system_prompt(ctx: RunContext) -> str:
|
|
176
|
+
# OAuth clients (Claude Code) ignore custom system prompts; we inject later.
|
|
190
177
|
return "You are Claude Code, Anthropic's official CLI for Claude."
|
|
191
178
|
|
|
179
|
+
def system_prompt_text(self, include_memory: bool = True) -> str:
|
|
180
|
+
"""Return the original SQLSaber system prompt as a single string."""
|
|
181
|
+
db = self.db_type
|
|
182
|
+
base = (
|
|
183
|
+
f"You are a helpful SQL assistant that helps users query their {db} database.\n\n"
|
|
184
|
+
"Your responsibilities:\n"
|
|
185
|
+
"1. Understand user's natural language requests, think and convert them to SQL\n"
|
|
186
|
+
"2. Use the provided tools efficiently to explore database schema\n"
|
|
187
|
+
"3. Generate appropriate SQL queries\n"
|
|
188
|
+
"4. Execute queries safely - queries that modify the database are not allowed\n"
|
|
189
|
+
"5. Format and explain results clearly\n\n"
|
|
190
|
+
"IMPORTANT - Tool Usage Strategy:\n"
|
|
191
|
+
"1. ALWAYS start with 'list_tables' to see available tables and row counts. Use this first to discover available tables.\n"
|
|
192
|
+
"2. Use 'introspect_schema' with a table_pattern to get details ONLY for relevant tables. Use table patterns like 'sample%' or '%experiment%' to filter related tables.\n"
|
|
193
|
+
"3. Execute SQL queries safely with automatic LIMIT clauses for SELECT statements. Only SELECT queries are permitted for security.\n\n"
|
|
194
|
+
"Tool-Specific Guidelines:\n"
|
|
195
|
+
"- introspect_schema: Use 'introspect_schema' with a table_pattern to get details ONLY for relevant tables. Use table patterns like 'sample%' or '%experiment%' to filter related tables.\n"
|
|
196
|
+
"- execute_sql: Execute SQL queries safely with automatic LIMIT clauses for SELECT statements. Only SELECT queries are permitted for security.\n\n"
|
|
197
|
+
"Guidelines:\n"
|
|
198
|
+
"- Use proper JOIN syntax and avoid cartesian products\n"
|
|
199
|
+
"- Include appropriate WHERE clauses to limit results\n"
|
|
200
|
+
"- Explain what the query does in simple terms\n"
|
|
201
|
+
"- Handle errors gracefully and suggest fixes\n"
|
|
202
|
+
"- Be security conscious - use parameterized queries when needed\n"
|
|
203
|
+
"- Timestamp columns must be converted to text when you write queries\n"
|
|
204
|
+
"- Use table patterns like 'sample%' or '%experiment%' to filter related tables"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
if include_memory and self.database_name:
|
|
208
|
+
mem = self.memory_manager.format_memories_for_prompt(self.database_name)
|
|
209
|
+
mem = mem.strip()
|
|
210
|
+
if mem:
|
|
211
|
+
return f"{base}\n\n{mem}"
|
|
212
|
+
return base
|
|
213
|
+
|
|
192
214
|
def _register_tools(self, agent: Agent) -> None:
|
|
193
215
|
"""Register all the SQL tools with the agent."""
|
|
194
216
|
|
|
@@ -152,7 +152,7 @@ def list():
|
|
|
152
152
|
table.add_column("Name", style="success")
|
|
153
153
|
table.add_column("Description", style="info")
|
|
154
154
|
table.add_column("Context", style="warning", justify="right")
|
|
155
|
-
table.add_column("Current", style="
|
|
155
|
+
table.add_column("Current", style="accent", justify="center")
|
|
156
156
|
|
|
157
157
|
current_model = model_manager.get_current_model()
|
|
158
158
|
|
|
@@ -144,17 +144,8 @@ class StreamingQueryHandler:
|
|
|
144
144
|
prepared_prompt: str | list[str] = user_query
|
|
145
145
|
no_history = not message_history
|
|
146
146
|
if sqlsaber_agent.is_oauth and no_history:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
)
|
|
150
|
-
mem = ""
|
|
151
|
-
if sqlsaber_agent.database_name:
|
|
152
|
-
mem = sqlsaber_agent.memory_manager.format_memories_for_prompt(
|
|
153
|
-
sqlsaber_agent.database_name
|
|
154
|
-
)
|
|
155
|
-
parts = [p for p in (instructions, mem) if p and str(p).strip()]
|
|
156
|
-
if parts:
|
|
157
|
-
injected = "\n\n".join(parts)
|
|
147
|
+
injected = sqlsaber_agent.system_prompt_text(include_memory=True)
|
|
148
|
+
if injected and str(injected).strip():
|
|
158
149
|
prepared_prompt = [injected, user_query]
|
|
159
150
|
|
|
160
151
|
# Show a transient status until events start streaming
|
|
@@ -36,7 +36,7 @@ class APIKeyManager:
|
|
|
36
36
|
return api_key
|
|
37
37
|
except Exception as e:
|
|
38
38
|
# Keyring access failed, continue to prompt
|
|
39
|
-
console.print(f"Keyring access failed: {e}", style="
|
|
39
|
+
console.print(f"Keyring access failed: {e}", style="warning")
|
|
40
40
|
|
|
41
41
|
# 3. Prompt user for API key
|
|
42
42
|
return self._prompt_and_store_key(provider, env_var_name, service_name)
|
|
@@ -97,14 +97,14 @@ class OAuthTokenManager:
|
|
|
97
97
|
if token.is_expired():
|
|
98
98
|
console.print(
|
|
99
99
|
f"OAuth token for {provider} has expired and needs refresh",
|
|
100
|
-
style="muted
|
|
100
|
+
style="muted",
|
|
101
101
|
)
|
|
102
102
|
return token # Return anyway for refresh attempt
|
|
103
103
|
|
|
104
104
|
if token.expires_soon():
|
|
105
105
|
console.print(
|
|
106
106
|
f"OAuth token for {provider} expires soon, consider refreshing",
|
|
107
|
-
style="muted
|
|
107
|
+
style="muted",
|
|
108
108
|
)
|
|
109
109
|
|
|
110
110
|
return token
|