sqlsaber 0.29.1__tar.gz → 0.30.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.

Potentially problematic release.


This version of sqlsaber might be problematic. Click here for more details.

Files changed (131) hide show
  1. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/AGENT.md +1 -2
  2. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/CLAUDE.md +1 -2
  3. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/PKG-INFO +3 -44
  4. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/README.md +2 -42
  5. sqlsaber-0.30.0/docs/src/assets/sqlsaber.gif +0 -0
  6. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/changelog.md +14 -0
  7. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/guides/getting-started.mdx +2 -2
  8. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/index.mdx +6 -7
  9. sqlsaber-0.30.0/docs/src/styles/global.css +243 -0
  10. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/pyproject.toml +1 -4
  11. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/agents/base.py +1 -1
  12. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/agents/pydantic_ai_agent.py +39 -17
  13. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/models.py +1 -1
  14. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/streaming.py +2 -11
  15. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/theme/manager.py +4 -1
  16. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/tools/__init__.py +0 -5
  17. sqlsaber-0.30.0/src/sqlsaber/tools/base.py +42 -0
  18. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/tools/registry.py +6 -39
  19. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/tools/sql_tools.py +0 -42
  20. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_theme/test_manager.py +1 -3
  21. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_tools/test_base.py +0 -3
  22. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_tools/test_registry.py +1 -19
  23. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_tools/test_sql_tools.py +0 -4
  24. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/uv.lock +852 -947
  25. sqlsaber-0.29.1/docs/src/assets/sqlsaber-hero.svg +0 -48
  26. sqlsaber-0.29.1/docs/src/styles/global.css +0 -209
  27. sqlsaber-0.29.1/src/sqlsaber/agents/mcp.py +0 -21
  28. sqlsaber-0.29.1/src/sqlsaber/mcp/__init__.py +0 -5
  29. sqlsaber-0.29.1/src/sqlsaber/mcp/mcp.py +0 -129
  30. sqlsaber-0.29.1/src/sqlsaber/tools/base.py +0 -73
  31. sqlsaber-0.29.1/src/sqlsaber/tools/enums.py +0 -19
  32. sqlsaber-0.29.1/src/sqlsaber/tools/instructions.py +0 -231
  33. sqlsaber-0.29.1/tests/test_tools/test_instructions.py +0 -206
  34. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/.github/workflows/claude-code-review.yml +0 -0
  35. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/.github/workflows/claude.yml +0 -0
  36. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/.github/workflows/deploy-docs.yml +0 -0
  37. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/.github/workflows/publish.yml +0 -0
  38. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/.github/workflows/test.yml +0 -0
  39. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/.gitignore +0 -0
  40. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/.python-version +0 -0
  41. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/LICENSE +0 -0
  42. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/.gitignore +0 -0
  43. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/.vscode/extensions.json +0 -0
  44. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/.vscode/launch.json +0 -0
  45. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/CLAUDE.md +0 -0
  46. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/astro.config.mjs +0 -0
  47. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/package-lock.json +0 -0
  48. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/package.json +0 -0
  49. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/public/CNAME +0 -0
  50. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/public/favicon.svg +0 -0
  51. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/guides/authentication.mdx +0 -0
  52. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/guides/database-setup.mdx +0 -0
  53. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/guides/memory.mdx +0 -0
  54. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/guides/models.mdx +0 -0
  55. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/guides/queries.mdx +0 -0
  56. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/guides/threads.md +0 -0
  57. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/installation.mdx +0 -0
  58. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content/docs/reference/commands.md +0 -0
  59. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/src/content.config.ts +0 -0
  60. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/docs/tsconfig.json +0 -0
  61. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/legislators.db +0 -0
  62. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/pytest.ini +0 -0
  63. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/sqlsaber.gif +0 -0
  64. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/sqlsaber.svg +0 -0
  65. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/__init__.py +0 -0
  66. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/__main__.py +0 -0
  67. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/agents/__init__.py +0 -0
  68. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/application/__init__.py +0 -0
  69. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/application/auth_setup.py +0 -0
  70. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/application/db_setup.py +0 -0
  71. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/application/model_selection.py +0 -0
  72. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/application/prompts.py +0 -0
  73. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/__init__.py +0 -0
  74. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/auth.py +0 -0
  75. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/commands.py +0 -0
  76. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/completers.py +0 -0
  77. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/database.py +0 -0
  78. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/display.py +0 -0
  79. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/interactive.py +0 -0
  80. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/memory.py +0 -0
  81. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/onboarding.py +0 -0
  82. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/theme.py +0 -0
  83. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/cli/threads.py +0 -0
  84. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/config/__init__.py +0 -0
  85. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/config/api_keys.py +0 -0
  86. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/config/auth.py +0 -0
  87. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/config/database.py +0 -0
  88. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/config/oauth_flow.py +0 -0
  89. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/config/oauth_tokens.py +0 -0
  90. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/config/providers.py +0 -0
  91. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/config/settings.py +0 -0
  92. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/database/__init__.py +0 -0
  93. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/database/base.py +0 -0
  94. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/database/csv.py +0 -0
  95. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/database/duckdb.py +0 -0
  96. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/database/mysql.py +0 -0
  97. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/database/postgresql.py +0 -0
  98. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/database/resolver.py +0 -0
  99. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/database/schema.py +0 -0
  100. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/database/sqlite.py +0 -0
  101. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/memory/__init__.py +0 -0
  102. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/memory/manager.py +0 -0
  103. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/memory/storage.py +0 -0
  104. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/theme/__init__.py +0 -0
  105. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/threads/__init__.py +0 -0
  106. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/threads/storage.py +0 -0
  107. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/src/sqlsaber/tools/sql_guard.py +0 -0
  108. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/__init__.py +0 -0
  109. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/conftest.py +0 -0
  110. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_cli/__init__.py +0 -0
  111. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_cli/test_auth_reset.py +0 -0
  112. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_cli/test_commands.py +0 -0
  113. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_cli/test_threads.py +0 -0
  114. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_config/__init__.py +0 -0
  115. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_config/test_database.py +0 -0
  116. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_config/test_oauth.py +0 -0
  117. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_config/test_providers.py +0 -0
  118. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_config/test_settings.py +0 -0
  119. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database/__init__.py +0 -0
  120. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database/test_connection.py +0 -0
  121. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database/test_csv_connection.py +0 -0
  122. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database/test_csv_module.py +0 -0
  123. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database/test_duckdb_module.py +0 -0
  124. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database/test_schema.py +0 -0
  125. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database/test_schema_display.py +0 -0
  126. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database/test_sqlite_module.py +0 -0
  127. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database/test_timeout.py +0 -0
  128. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_database_resolver.py +0 -0
  129. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_threads_storage.py +0 -0
  130. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/tests/test_tools/__init__.py +0 -0
  131. {sqlsaber-0.29.1 → sqlsaber-0.30.0}/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), `mcp/` (Model Context Protocol server)
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), `mcp/` (Model Context Protocol server)
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.29.1
3
+ Version: 0.30.0
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
- - [MCP Server Integration](#mcp-server-integration)
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
- - MCP (Model Context Protocol) server support
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
- - [MCP Server Integration](#mcp-server-integration)
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
- - MCP (Model Context Protocol) server support
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:
@@ -7,6 +7,20 @@ All notable changes to SQLsaber will be documented here.
7
7
 
8
8
  ### Unreleased
9
9
 
10
+ ---
11
+
12
+ ### v0.30.0 - 2025-10-07
13
+
14
+ #### Removed
15
+
16
+ - MCP server support and related console scripts (`saber-mcp`, `sqlsaber-mcp`)
17
+
18
+ > `sqlsaber` can still be used as a cli in coding agents like Claude Code, Codex, or Amp.
19
+ >
20
+ > Just *ask* the coding agent to invoke `sqlsaber` cli with your question.
21
+
22
+
23
+
10
24
  ### v0.29.1 - 2025-10-05
11
25
 
12
26
  #### 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/739399.js"
98
- id="asciicast-739399"
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
- file: ../../assets/sqlsaber-hero.svg
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/741230.js"
29
- id="asciicast-741230"
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>MCP integration for Claude Code and other AI tools</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 "what's our revenue this quarter?"`}
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.29.1"
3
+ version = "0.30.0"
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(category="sql"):
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(category="sql"):
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
- """Set up the dynamic system prompt for the agent."""
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
- instructions = self.instruction_builder.build_instructions(
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="bold accent", justify="center")
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
- instructions = sqlsaber_agent.instruction_builder.build_instructions(
148
- db_type=sqlsaber_agent.db_type
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
@@ -9,7 +9,7 @@ from typing import Dict
9
9
  from platformdirs import user_config_dir
10
10
  from prompt_toolkit.styles import Style as PTStyle
11
11
  from prompt_toolkit.styles.pygments import style_from_pygments_cls
12
- from pygments.styles import get_style_by_name
12
+ from pygments.styles import get_all_styles, get_style_by_name
13
13
  from pygments.token import Token
14
14
  from pygments.util import ClassNotFound
15
15
  from rich.console import Console
@@ -200,6 +200,9 @@ def get_theme_manager() -> ThemeManager:
200
200
  user_cfg = _load_user_theme_config()
201
201
  env_name = os.getenv("SQLSABER_THEME")
202
202
 
203
+ if env_name and env_name.lower() not in get_all_styles():
204
+ env_name = None
205
+
203
206
  name = (
204
207
  env_name or user_cfg.get("theme", {}).get("name") or DEFAULT_THEME_NAME
205
208
  ).lower()