sqlnow-mcp 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.
- sqlnow_mcp-0.1.0/LICENSE +21 -0
- sqlnow_mcp-0.1.0/PKG-INFO +341 -0
- sqlnow_mcp-0.1.0/README.md +310 -0
- sqlnow_mcp-0.1.0/pyproject.toml +63 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/__init__.py +3 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/cli.py +545 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/config.py +100 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/db.py +827 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/metadata.py +668 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/resource_limits.py +37 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/server.py +445 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/table_result.py +246 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/ui/table.html +355 -0
- sqlnow_mcp-0.1.0/sqlnow_mcp/ui.py +21 -0
sqlnow_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 David Raznick
|
|
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,341 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sqlnow-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for DuckDB with interactive table viewer
|
|
5
|
+
Keywords: mcp,duckdb,sql,llm,claude,cursor
|
|
6
|
+
Author: David Raznick
|
|
7
|
+
Author-email: David Raznick <david.raznick@globalenergymonitor.org>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Database
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Requires-Dist: click
|
|
20
|
+
Requires-Dist: duckdb
|
|
21
|
+
Requires-Dist: fastmcp
|
|
22
|
+
Requires-Dist: pyarrow
|
|
23
|
+
Requires-Dist: pytz
|
|
24
|
+
Requires-Dist: pyyaml
|
|
25
|
+
Requires-Dist: sqlglot
|
|
26
|
+
Requires-Python: >=3.11
|
|
27
|
+
Project-URL: Homepage, https://github.com/kindly/sqlnow-mcp
|
|
28
|
+
Project-URL: Repository, https://github.com/kindly/sqlnow-mcp
|
|
29
|
+
Project-URL: Issues, https://github.com/kindly/sqlnow-mcp/issues
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# sqlnow-mcp
|
|
33
|
+
|
|
34
|
+
An MCP server for DuckDB with an interactive table viewer. Point it at a data directory, attach files or external databases, and explore data through an LLM host such as Cursor or Claude Desktop. Query results from `run_query`, `sample_table`, and `value_counts` render as a scrollable grid inside the host via [MCP Apps](https://modelcontextprotocol.io/). Use `run_query_json` for small SELECT results the model needs as JSON (aggregations, counts, spot checks) — not for large exports.
|
|
35
|
+
|
|
36
|
+
## Requirements
|
|
37
|
+
|
|
38
|
+
- Python 3.11+
|
|
39
|
+
- [uv](https://docs.astral.sh/uv/) (recommended) or pip
|
|
40
|
+
- Node.js 18+ (only if you need to rebuild the table UI)
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
From a clone of this repo:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv sync
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or run without installing globally:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
uvx sqlnow-mcp --mode local --data-dir ~/mydata
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## MCP server
|
|
57
|
+
|
|
58
|
+
Two modes are available:
|
|
59
|
+
|
|
60
|
+
- **local** — read/write; point at a data directory, switch databases, attach files and external connections.
|
|
61
|
+
- **publish** — read-only; single database with curated `metadata.yaml` (and optional `stats.json`) for sharing a branded dataset.
|
|
62
|
+
|
|
63
|
+
Transport (`stdio`, HTTP) works the same in both modes.
|
|
64
|
+
|
|
65
|
+
### Local mode
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
sqlnow-mcp --mode local --data-dir ~/mydata
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`--data-dir` is the working directory for `.db` files and attachable data. Session state (active database, attached sources) persists for the lifetime of the server process.
|
|
72
|
+
|
|
73
|
+
### Publish mode
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
sqlnow-mcp --mode publish \
|
|
77
|
+
--db ~/datasets/gemlive.db \
|
|
78
|
+
--metadata ~/datasets/metadata.yaml \
|
|
79
|
+
--stats ~/datasets/stats.json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`--stats` is optional. The server name and instructions come from metadata (`short_name`, `welcome`, `example_questions`, etc.). Queries are SELECT-only with a default 10s timeout, 50% memory limit, and 1 DuckDB thread (all overridable).
|
|
83
|
+
|
|
84
|
+
**stdio (Claude Desktop):**
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"mcpServers": {
|
|
89
|
+
"gem-data": {
|
|
90
|
+
"command": "uvx",
|
|
91
|
+
"args": [
|
|
92
|
+
"sqlnow-mcp", "--mode", "publish",
|
|
93
|
+
"--db", "/path/to/gemlive.db",
|
|
94
|
+
"--metadata", "/path/to/metadata.yaml"
|
|
95
|
+
]
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**HTTP:**
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
sqlnow-mcp --mode publish \
|
|
105
|
+
--db ~/datasets/gemlive.db \
|
|
106
|
+
--metadata ~/datasets/metadata.yaml \
|
|
107
|
+
--transport streamable-http \
|
|
108
|
+
--host 127.0.0.1 \
|
|
109
|
+
--port 8000
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"mcpServers": {
|
|
115
|
+
"gem-data": {
|
|
116
|
+
"url": "http://127.0.0.1:8000/mcp"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Publish workflow: call `database_info()` first, then `list_tables` / `describe_table` (with metadata descriptions), then `run_query_json` or `run_query`. Publish mode exposes only native tables in the database file — attached databases (sidecar) are not supported.
|
|
123
|
+
|
|
124
|
+
Generate a starter metadata file from a local database:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
sqlnow-mcp --data-dir ./data -d demo generate-metadata -o metadata.yaml
|
|
128
|
+
sqlnow-mcp --data-dir ./data -d demo generate-stats -o stats.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
By default both commands include only native tables, not tables from attached databases.
|
|
132
|
+
|
|
133
|
+
### Options
|
|
134
|
+
|
|
135
|
+
**Shared (local and publish server startup):**
|
|
136
|
+
|
|
137
|
+
| Option | Default | Description |
|
|
138
|
+
|--------|---------|-------------|
|
|
139
|
+
| `--transport` | `stdio` | MCP transport: `stdio`, `http`, `streamable-http`, or `sse` |
|
|
140
|
+
| `--host` | `127.0.0.1` | Bind address when using an HTTP transport |
|
|
141
|
+
| `--port` | `8000` | Port when using an HTTP transport |
|
|
142
|
+
|
|
143
|
+
**Local mode only:**
|
|
144
|
+
|
|
145
|
+
| Option | Default | Description |
|
|
146
|
+
|--------|---------|-------------|
|
|
147
|
+
| `--data-dir` | `.` | Directory containing DuckDB `.db` files |
|
|
148
|
+
| `--allow-path` | `$HOME` | Extra directories allowed for `attach_file` (repeatable) |
|
|
149
|
+
| `--allow-external` / `--no-allow-external` | on | Allow attaching Postgres, SQLite, and MySQL databases |
|
|
150
|
+
|
|
151
|
+
**Publish mode only:**
|
|
152
|
+
|
|
153
|
+
| Option | Default | Description |
|
|
154
|
+
|--------|---------|-------------|
|
|
155
|
+
| `--db` | — | DuckDB file (required) |
|
|
156
|
+
| `--metadata` | — | `metadata.yaml` (required) |
|
|
157
|
+
| `--stats` | — | Precomputed `stats.json` (optional) |
|
|
158
|
+
| `--strict-metadata` | off | Fail startup on metadata/DB mismatches |
|
|
159
|
+
| `--query-timeout` | `10` | Per-query timeout in seconds |
|
|
160
|
+
| `--memory-limit` | `50%` | DuckDB `memory_limit` (percentage or absolute, e.g. `2GiB`) |
|
|
161
|
+
| `--threads` | `1` | DuckDB worker threads |
|
|
162
|
+
| `--max-temp-directory-size` | — | Cap disk spill for large queries (e.g. `10GiB`) |
|
|
163
|
+
|
|
164
|
+
For security, `--allow-path /` is rejected (it would allow reading any file on disk).
|
|
165
|
+
|
|
166
|
+
### stdio (default)
|
|
167
|
+
|
|
168
|
+
Best for editor integration. The host spawns the process and talks over stdin/stdout. Works for both `--mode local` and `--mode publish`.
|
|
169
|
+
|
|
170
|
+
**Cursor** — add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"mcpServers": {
|
|
175
|
+
"sqlnow": {
|
|
176
|
+
"command": "uv",
|
|
177
|
+
"args": [
|
|
178
|
+
"run",
|
|
179
|
+
"--directory",
|
|
180
|
+
"/absolute/path/to/sqlnow-mcp",
|
|
181
|
+
"sqlnow-mcp",
|
|
182
|
+
"--mode",
|
|
183
|
+
"local",
|
|
184
|
+
"--data-dir",
|
|
185
|
+
"/home/you/mydata"
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
If the package is installed or you use `uvx`, you can omit `--directory`:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"mcpServers": {
|
|
197
|
+
"sqlnow": {
|
|
198
|
+
"command": "uvx",
|
|
199
|
+
"args": [
|
|
200
|
+
"sqlnow-mcp",
|
|
201
|
+
"--mode",
|
|
202
|
+
"local",
|
|
203
|
+
"--data-dir",
|
|
204
|
+
"/home/you/mydata"
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Claude Desktop** — add to `claude_desktop_config.json`:
|
|
212
|
+
|
|
213
|
+
```json
|
|
214
|
+
{
|
|
215
|
+
"mcpServers": {
|
|
216
|
+
"sqlnow": {
|
|
217
|
+
"command": "uvx",
|
|
218
|
+
"args": ["sqlnow-mcp", "--mode", "local", "--data-dir", "/home/you/mydata"]
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Restart the host after changing MCP config.
|
|
225
|
+
|
|
226
|
+
### HTTP
|
|
227
|
+
|
|
228
|
+
Run the server as a local HTTP service, then point the host at a URL instead of spawning a command. Works for both `--mode local` and `--mode publish`.
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
sqlnow-mcp --mode local \
|
|
232
|
+
--data-dir ~/mydata \
|
|
233
|
+
--transport streamable-http \
|
|
234
|
+
--host 127.0.0.1 \
|
|
235
|
+
--port 8000
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Cursor / Claude Desktop:**
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{
|
|
242
|
+
"mcpServers": {
|
|
243
|
+
"sqlnow": {
|
|
244
|
+
"url": "http://127.0.0.1:8000/mcp"
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Use `http` or `sse` instead of `streamable-http` if your MCP client expects a different transport.
|
|
251
|
+
|
|
252
|
+
### Typical workflow
|
|
253
|
+
|
|
254
|
+
The server exposes tools for database discovery, schema inspection, profiling, and SQL. A sensible order for the LLM (or you, when calling tools manually) is:
|
|
255
|
+
|
|
256
|
+
1. `list_databases` — find `.db` files in the data directory
|
|
257
|
+
2. `create_database` — create a new empty database (optional)
|
|
258
|
+
3. `use_database` or `use_memory` — open a session (**required** before most other tools)
|
|
259
|
+
4. `attach_file` / `attach_database` — load CSV, Parquet, JSON, XLSX, or external DBs
|
|
260
|
+
5. `list_tables` — every table includes column names and types; call `describe_table` for one table
|
|
261
|
+
6. `sample_table`, `profile_table`, `value_counts` — build context before writing SQL (no ad-hoc `SELECT ... LIMIT 2` for schema)
|
|
262
|
+
7. `run_query_json` — small SELECT for model-side analysis (aggregations, counts, low limits); **full JSON in context — not for large result sets**
|
|
263
|
+
8. `run_query` — larger or user-visible SELECT results in the table viewer
|
|
264
|
+
|
|
265
|
+
**Schema vs. results:** After `list_tables`, column names and types are already available — do not use `run_query` with a small `LIMIT` just to inspect structure. Use `describe_table`, `profile_table`, or `run_query_json` with a low limit instead.
|
|
266
|
+
|
|
267
|
+
Tools that open the interactive table viewer (`run_query`, `sample_table`, `value_counts`) are wired to the MCP Apps UI resource `ui://sqlnow/table.html`. Large `run_query` results are paginated internally; the viewer calls `fetch_table_page` to load additional rows. `run_query_json` inlines all returned rows as JSON in the tool response — keep result sets small and aggregate in SQL when possible.
|
|
268
|
+
|
|
269
|
+
## Dev CLI
|
|
270
|
+
|
|
271
|
+
The same entry point also provides subcommands for testing without an MCP host:
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
# Create and use a database
|
|
275
|
+
sqlnow-mcp --data-dir ./data create-database demo
|
|
276
|
+
sqlnow-mcp --data-dir ./data -d demo attach-file ./samples/events.csv
|
|
277
|
+
sqlnow-mcp --data-dir ./data -d demo list-tables
|
|
278
|
+
sqlnow-mcp --data-dir ./data -d demo generate-metadata -o metadata.yaml
|
|
279
|
+
sqlnow-mcp --data-dir ./data -d demo generate-stats -o stats.json
|
|
280
|
+
sqlnow-mcp --data-dir ./data -d demo query "SELECT * FROM events LIMIT 10"
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Run `sqlnow-mcp --help` for the full command list.
|
|
284
|
+
|
|
285
|
+
## Building the table UI
|
|
286
|
+
|
|
287
|
+
The shipped artifact is a **single self-contained HTML file** at `sqlnow_mcp/ui/table.html`. The MCP server serves it as the `ui://sqlnow/table.html` resource (see `sqlnow_mcp/ui.py`). You normally use the committed copy as-is; rebuild only after changing the React source under `ui/`.
|
|
288
|
+
|
|
289
|
+
### Source layout
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
ui/
|
|
293
|
+
├── index.html # Vite entry HTML
|
|
294
|
+
├── vite.config.ts # React + single-file bundle
|
|
295
|
+
├── package.json
|
|
296
|
+
└── src/
|
|
297
|
+
├── main.tsx
|
|
298
|
+
├── TableApp.tsx # glide-data-grid + @modelcontextprotocol/ext-apps
|
|
299
|
+
└── types.ts
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Vite bundles React, glide-data-grid, and the MCP Apps client into one inline HTML file via `vite-plugin-singlefile`.
|
|
303
|
+
|
|
304
|
+
### Build steps
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
cd ui
|
|
308
|
+
npm install
|
|
309
|
+
npm run build
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The `build` script runs `vite build` and copies the output to the Python package:
|
|
313
|
+
|
|
314
|
+
```
|
|
315
|
+
ui/dist/index.html → sqlnow_mcp/ui/table.html
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Commit `sqlnow_mcp/ui/table.html` if you changed the UI and want others to get the update without running Node.
|
|
319
|
+
|
|
320
|
+
### Local UI preview
|
|
321
|
+
|
|
322
|
+
There is no `dev` script in `package.json`, but you can run Vite's dev server from `ui/`:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
cd ui
|
|
326
|
+
npm install
|
|
327
|
+
npx vite
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
This serves `ui/index.html` with hot reload. MCP Apps integration (host postMessage, `fetch_table_page`) only works inside a real MCP host; use the built `table.html` with the running MCP server for end-to-end testing.
|
|
331
|
+
|
|
332
|
+
## Development
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
uv sync --group dev
|
|
336
|
+
uv run pytest
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## License
|
|
340
|
+
|
|
341
|
+
See repository metadata and author information in `pyproject.toml`.
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# sqlnow-mcp
|
|
2
|
+
|
|
3
|
+
An MCP server for DuckDB with an interactive table viewer. Point it at a data directory, attach files or external databases, and explore data through an LLM host such as Cursor or Claude Desktop. Query results from `run_query`, `sample_table`, and `value_counts` render as a scrollable grid inside the host via [MCP Apps](https://modelcontextprotocol.io/). Use `run_query_json` for small SELECT results the model needs as JSON (aggregations, counts, spot checks) — not for large exports.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Python 3.11+
|
|
8
|
+
- [uv](https://docs.astral.sh/uv/) (recommended) or pip
|
|
9
|
+
- Node.js 18+ (only if you need to rebuild the table UI)
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
From a clone of this repo:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv sync
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or run without installing globally:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uvx sqlnow-mcp --mode local --data-dir ~/mydata
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## MCP server
|
|
26
|
+
|
|
27
|
+
Two modes are available:
|
|
28
|
+
|
|
29
|
+
- **local** — read/write; point at a data directory, switch databases, attach files and external connections.
|
|
30
|
+
- **publish** — read-only; single database with curated `metadata.yaml` (and optional `stats.json`) for sharing a branded dataset.
|
|
31
|
+
|
|
32
|
+
Transport (`stdio`, HTTP) works the same in both modes.
|
|
33
|
+
|
|
34
|
+
### Local mode
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
sqlnow-mcp --mode local --data-dir ~/mydata
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`--data-dir` is the working directory for `.db` files and attachable data. Session state (active database, attached sources) persists for the lifetime of the server process.
|
|
41
|
+
|
|
42
|
+
### Publish mode
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
sqlnow-mcp --mode publish \
|
|
46
|
+
--db ~/datasets/gemlive.db \
|
|
47
|
+
--metadata ~/datasets/metadata.yaml \
|
|
48
|
+
--stats ~/datasets/stats.json
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`--stats` is optional. The server name and instructions come from metadata (`short_name`, `welcome`, `example_questions`, etc.). Queries are SELECT-only with a default 10s timeout, 50% memory limit, and 1 DuckDB thread (all overridable).
|
|
52
|
+
|
|
53
|
+
**stdio (Claude Desktop):**
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"gem-data": {
|
|
59
|
+
"command": "uvx",
|
|
60
|
+
"args": [
|
|
61
|
+
"sqlnow-mcp", "--mode", "publish",
|
|
62
|
+
"--db", "/path/to/gemlive.db",
|
|
63
|
+
"--metadata", "/path/to/metadata.yaml"
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**HTTP:**
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
sqlnow-mcp --mode publish \
|
|
74
|
+
--db ~/datasets/gemlive.db \
|
|
75
|
+
--metadata ~/datasets/metadata.yaml \
|
|
76
|
+
--transport streamable-http \
|
|
77
|
+
--host 127.0.0.1 \
|
|
78
|
+
--port 8000
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"gem-data": {
|
|
85
|
+
"url": "http://127.0.0.1:8000/mcp"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Publish workflow: call `database_info()` first, then `list_tables` / `describe_table` (with metadata descriptions), then `run_query_json` or `run_query`. Publish mode exposes only native tables in the database file — attached databases (sidecar) are not supported.
|
|
92
|
+
|
|
93
|
+
Generate a starter metadata file from a local database:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
sqlnow-mcp --data-dir ./data -d demo generate-metadata -o metadata.yaml
|
|
97
|
+
sqlnow-mcp --data-dir ./data -d demo generate-stats -o stats.json
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
By default both commands include only native tables, not tables from attached databases.
|
|
101
|
+
|
|
102
|
+
### Options
|
|
103
|
+
|
|
104
|
+
**Shared (local and publish server startup):**
|
|
105
|
+
|
|
106
|
+
| Option | Default | Description |
|
|
107
|
+
|--------|---------|-------------|
|
|
108
|
+
| `--transport` | `stdio` | MCP transport: `stdio`, `http`, `streamable-http`, or `sse` |
|
|
109
|
+
| `--host` | `127.0.0.1` | Bind address when using an HTTP transport |
|
|
110
|
+
| `--port` | `8000` | Port when using an HTTP transport |
|
|
111
|
+
|
|
112
|
+
**Local mode only:**
|
|
113
|
+
|
|
114
|
+
| Option | Default | Description |
|
|
115
|
+
|--------|---------|-------------|
|
|
116
|
+
| `--data-dir` | `.` | Directory containing DuckDB `.db` files |
|
|
117
|
+
| `--allow-path` | `$HOME` | Extra directories allowed for `attach_file` (repeatable) |
|
|
118
|
+
| `--allow-external` / `--no-allow-external` | on | Allow attaching Postgres, SQLite, and MySQL databases |
|
|
119
|
+
|
|
120
|
+
**Publish mode only:**
|
|
121
|
+
|
|
122
|
+
| Option | Default | Description |
|
|
123
|
+
|--------|---------|-------------|
|
|
124
|
+
| `--db` | — | DuckDB file (required) |
|
|
125
|
+
| `--metadata` | — | `metadata.yaml` (required) |
|
|
126
|
+
| `--stats` | — | Precomputed `stats.json` (optional) |
|
|
127
|
+
| `--strict-metadata` | off | Fail startup on metadata/DB mismatches |
|
|
128
|
+
| `--query-timeout` | `10` | Per-query timeout in seconds |
|
|
129
|
+
| `--memory-limit` | `50%` | DuckDB `memory_limit` (percentage or absolute, e.g. `2GiB`) |
|
|
130
|
+
| `--threads` | `1` | DuckDB worker threads |
|
|
131
|
+
| `--max-temp-directory-size` | — | Cap disk spill for large queries (e.g. `10GiB`) |
|
|
132
|
+
|
|
133
|
+
For security, `--allow-path /` is rejected (it would allow reading any file on disk).
|
|
134
|
+
|
|
135
|
+
### stdio (default)
|
|
136
|
+
|
|
137
|
+
Best for editor integration. The host spawns the process and talks over stdin/stdout. Works for both `--mode local` and `--mode publish`.
|
|
138
|
+
|
|
139
|
+
**Cursor** — add to `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"mcpServers": {
|
|
144
|
+
"sqlnow": {
|
|
145
|
+
"command": "uv",
|
|
146
|
+
"args": [
|
|
147
|
+
"run",
|
|
148
|
+
"--directory",
|
|
149
|
+
"/absolute/path/to/sqlnow-mcp",
|
|
150
|
+
"sqlnow-mcp",
|
|
151
|
+
"--mode",
|
|
152
|
+
"local",
|
|
153
|
+
"--data-dir",
|
|
154
|
+
"/home/you/mydata"
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
If the package is installed or you use `uvx`, you can omit `--directory`:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"mcpServers": {
|
|
166
|
+
"sqlnow": {
|
|
167
|
+
"command": "uvx",
|
|
168
|
+
"args": [
|
|
169
|
+
"sqlnow-mcp",
|
|
170
|
+
"--mode",
|
|
171
|
+
"local",
|
|
172
|
+
"--data-dir",
|
|
173
|
+
"/home/you/mydata"
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Claude Desktop** — add to `claude_desktop_config.json`:
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"mcpServers": {
|
|
185
|
+
"sqlnow": {
|
|
186
|
+
"command": "uvx",
|
|
187
|
+
"args": ["sqlnow-mcp", "--mode", "local", "--data-dir", "/home/you/mydata"]
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Restart the host after changing MCP config.
|
|
194
|
+
|
|
195
|
+
### HTTP
|
|
196
|
+
|
|
197
|
+
Run the server as a local HTTP service, then point the host at a URL instead of spawning a command. Works for both `--mode local` and `--mode publish`.
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
sqlnow-mcp --mode local \
|
|
201
|
+
--data-dir ~/mydata \
|
|
202
|
+
--transport streamable-http \
|
|
203
|
+
--host 127.0.0.1 \
|
|
204
|
+
--port 8000
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Cursor / Claude Desktop:**
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"mcpServers": {
|
|
212
|
+
"sqlnow": {
|
|
213
|
+
"url": "http://127.0.0.1:8000/mcp"
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Use `http` or `sse` instead of `streamable-http` if your MCP client expects a different transport.
|
|
220
|
+
|
|
221
|
+
### Typical workflow
|
|
222
|
+
|
|
223
|
+
The server exposes tools for database discovery, schema inspection, profiling, and SQL. A sensible order for the LLM (or you, when calling tools manually) is:
|
|
224
|
+
|
|
225
|
+
1. `list_databases` — find `.db` files in the data directory
|
|
226
|
+
2. `create_database` — create a new empty database (optional)
|
|
227
|
+
3. `use_database` or `use_memory` — open a session (**required** before most other tools)
|
|
228
|
+
4. `attach_file` / `attach_database` — load CSV, Parquet, JSON, XLSX, or external DBs
|
|
229
|
+
5. `list_tables` — every table includes column names and types; call `describe_table` for one table
|
|
230
|
+
6. `sample_table`, `profile_table`, `value_counts` — build context before writing SQL (no ad-hoc `SELECT ... LIMIT 2` for schema)
|
|
231
|
+
7. `run_query_json` — small SELECT for model-side analysis (aggregations, counts, low limits); **full JSON in context — not for large result sets**
|
|
232
|
+
8. `run_query` — larger or user-visible SELECT results in the table viewer
|
|
233
|
+
|
|
234
|
+
**Schema vs. results:** After `list_tables`, column names and types are already available — do not use `run_query` with a small `LIMIT` just to inspect structure. Use `describe_table`, `profile_table`, or `run_query_json` with a low limit instead.
|
|
235
|
+
|
|
236
|
+
Tools that open the interactive table viewer (`run_query`, `sample_table`, `value_counts`) are wired to the MCP Apps UI resource `ui://sqlnow/table.html`. Large `run_query` results are paginated internally; the viewer calls `fetch_table_page` to load additional rows. `run_query_json` inlines all returned rows as JSON in the tool response — keep result sets small and aggregate in SQL when possible.
|
|
237
|
+
|
|
238
|
+
## Dev CLI
|
|
239
|
+
|
|
240
|
+
The same entry point also provides subcommands for testing without an MCP host:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# Create and use a database
|
|
244
|
+
sqlnow-mcp --data-dir ./data create-database demo
|
|
245
|
+
sqlnow-mcp --data-dir ./data -d demo attach-file ./samples/events.csv
|
|
246
|
+
sqlnow-mcp --data-dir ./data -d demo list-tables
|
|
247
|
+
sqlnow-mcp --data-dir ./data -d demo generate-metadata -o metadata.yaml
|
|
248
|
+
sqlnow-mcp --data-dir ./data -d demo generate-stats -o stats.json
|
|
249
|
+
sqlnow-mcp --data-dir ./data -d demo query "SELECT * FROM events LIMIT 10"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Run `sqlnow-mcp --help` for the full command list.
|
|
253
|
+
|
|
254
|
+
## Building the table UI
|
|
255
|
+
|
|
256
|
+
The shipped artifact is a **single self-contained HTML file** at `sqlnow_mcp/ui/table.html`. The MCP server serves it as the `ui://sqlnow/table.html` resource (see `sqlnow_mcp/ui.py`). You normally use the committed copy as-is; rebuild only after changing the React source under `ui/`.
|
|
257
|
+
|
|
258
|
+
### Source layout
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
ui/
|
|
262
|
+
├── index.html # Vite entry HTML
|
|
263
|
+
├── vite.config.ts # React + single-file bundle
|
|
264
|
+
├── package.json
|
|
265
|
+
└── src/
|
|
266
|
+
├── main.tsx
|
|
267
|
+
├── TableApp.tsx # glide-data-grid + @modelcontextprotocol/ext-apps
|
|
268
|
+
└── types.ts
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Vite bundles React, glide-data-grid, and the MCP Apps client into one inline HTML file via `vite-plugin-singlefile`.
|
|
272
|
+
|
|
273
|
+
### Build steps
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
cd ui
|
|
277
|
+
npm install
|
|
278
|
+
npm run build
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The `build` script runs `vite build` and copies the output to the Python package:
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
ui/dist/index.html → sqlnow_mcp/ui/table.html
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Commit `sqlnow_mcp/ui/table.html` if you changed the UI and want others to get the update without running Node.
|
|
288
|
+
|
|
289
|
+
### Local UI preview
|
|
290
|
+
|
|
291
|
+
There is no `dev` script in `package.json`, but you can run Vite's dev server from `ui/`:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
cd ui
|
|
295
|
+
npm install
|
|
296
|
+
npx vite
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
This serves `ui/index.html` with hot reload. MCP Apps integration (host postMessage, `fetch_table_page`) only works inside a real MCP host; use the built `table.html` with the running MCP server for end-to-end testing.
|
|
300
|
+
|
|
301
|
+
## Development
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
uv sync --group dev
|
|
305
|
+
uv run pytest
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## License
|
|
309
|
+
|
|
310
|
+
See repository metadata and author information in `pyproject.toml`.
|