jmd-mcp-sql 0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andreas Ostermeyer
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,345 @@
1
+ Metadata-Version: 2.4
2
+ Name: jmd-mcp-sql
3
+ Version: 0.4
4
+ Summary: JMD-based MCP server for SQLite — natural language database interface
5
+ Author-email: Andreas Ostermeyer <andreas@ostermeyer.de>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Andreas Ostermeyer
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/ostermeyer/jmd-spec
29
+ Project-URL: Repository, https://github.com/ostermeyer/jmd-mcp-sql
30
+ Keywords: jmd,mcp,sqlite,llm,natural-language,database
31
+ Requires-Python: >=3.10
32
+ Description-Content-Type: text/markdown
33
+ License-File: LICENSE
34
+ Requires-Dist: mcp>=1.0
35
+ Requires-Dist: jmd-format>=0.4.1
36
+ Dynamic: license-file
37
+
38
+ # jmd-mcp-sql
39
+
40
+ MCP server that exposes a SQLite database through three JMD tools — a natural language database interface for LLM-driven workflows.
41
+
42
+ ## Tools
43
+
44
+ | Tool | `#` Data | `#?` Query | `#!` Schema | `#-` Delete |
45
+ | --- | --- | --- | --- | --- |
46
+ | `read` | SELECT by fields | SELECT with filters + aggregation | PRAGMA (describe table) | — |
47
+ | `write` | INSERT OR REPLACE | — | CREATE / ALTER TABLE | — |
48
+ | `delete` | — | — | DROP TABLE | DELETE WHERE |
49
+
50
+ All inputs and outputs are JMD documents. The LLM speaks JMD — no SQL required.
51
+
52
+ ## Installation
53
+
54
+ Install the latest version directly from GitHub:
55
+
56
+ ```bash
57
+ pip install git+https://github.com/ostermeyer/jmd-mcp-sql.git
58
+ ```
59
+
60
+ Or pin a specific release:
61
+
62
+ ```bash
63
+ pip install git+https://github.com/ostermeyer/jmd-mcp-sql.git@v0.1
64
+ ```
65
+
66
+ Pre-built packages are attached to each
67
+ [GitHub Release](https://github.com/ostermeyer/jmd-mcp-sql/releases).
68
+
69
+ ## Configuration
70
+
71
+ ### With Claude Code
72
+
73
+ Add to your MCP configuration (`~/.claude/settings.json`):
74
+
75
+ ```json
76
+ {
77
+ "mcpServers": {
78
+ "sql": {
79
+ "command": "jmd-mcp-sql",
80
+ "args": ["/path/to/your.db"]
81
+ }
82
+ }
83
+ }
84
+ ```
85
+
86
+ Or use the bundled Northwind demo database (no argument needed):
87
+
88
+ ```json
89
+ {
90
+ "mcpServers": {
91
+ "sql": {
92
+ "command": "jmd-mcp-sql"
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ The demo database ships as `northwind.sql` (plain text, version-controlled). On the
99
+ first run without an explicit path, the server creates `northwind.db` from that file
100
+ automatically. The `.db` file is not tracked by git.
101
+
102
+ ## JMD Document Syntax
103
+
104
+ Every document starts with a heading line that sets the document type and table name,
105
+ followed by `key: value` pairs (one per line):
106
+
107
+ ```text
108
+ # Product → data document (exact lookup / insert-or-replace)
109
+ #? Product → query document (filter / list / aggregate)
110
+ #! Product → schema document (describe / create / drop table)
111
+ #- Product → delete document (delete matching records)
112
+
113
+ key: value → string, integer, or float — inferred automatically
114
+ key: true/false → boolean
115
+ ```
116
+
117
+ ## Discovering the Database
118
+
119
+ To see which tables exist, read each table's schema:
120
+
121
+ ```text
122
+ read("#! Customers")
123
+ ```
124
+
125
+ This returns a `#!` document with column names, JMD types, and modifiers
126
+ (`readonly` = primary key, `optional` = nullable).
127
+
128
+ ## Typical Workflows
129
+
130
+ **List all rows** (small tables only):
131
+
132
+ ```text
133
+ read("#? Orders")
134
+ ```
135
+
136
+ **Filter rows — equality:**
137
+
138
+ ```text
139
+ read("#? Orders\nstatus: shipped")
140
+ ```
141
+
142
+ **Filter rows — comparison:**
143
+
144
+ ```text
145
+ read("#? Orders\nFreight: > 50")
146
+ ```
147
+
148
+ **Filter rows — alternation (OR):**
149
+
150
+ ```text
151
+ read("#? Orders\nShipCountry: Germany|France|UK")
152
+ ```
153
+
154
+ **Filter rows — contains (case-insensitive substring):**
155
+
156
+ ```text
157
+ read("#? Customers\nCompanyName: ~Corp")
158
+ ```
159
+
160
+ **Filter rows — regex pattern:**
161
+
162
+ ```text
163
+ read("#? Products\nProductName: ^Chai.*")
164
+ ```
165
+
166
+ **Filter rows — negation** (composes with any operator):
167
+
168
+ ```text
169
+ read("#? Orders\nShipCountry: !Germany")
170
+ read("#? Products\nProductName: !^LEGACY.*")
171
+ ```
172
+
173
+ **Look up one record:**
174
+
175
+ ```text
176
+ read("# Customers\nid: 42")
177
+ ```
178
+
179
+ **Insert or replace a record:**
180
+
181
+ ```text
182
+ write("# Orders\nid: 1\nstatus: pending\ntotal: 99.90")
183
+ ```
184
+
185
+ **Create a table:**
186
+
187
+ ```text
188
+ write("#! Products\nid: integer readonly\nname: string\nprice: float optional")
189
+ ```
190
+
191
+ **Delete a record:**
192
+
193
+ ```text
194
+ delete("#- Orders\nid: 1")
195
+ ```
196
+
197
+ **Drop a table:**
198
+
199
+ ```text
200
+ delete("#! OldTable")
201
+ ```
202
+
203
+ ## Pagination
204
+
205
+ Always use pagination when querying tables that may contain many rows.
206
+
207
+ Use frontmatter fields before the `#?` heading to control pagination:
208
+
209
+ ```text
210
+ read("page-size: 50\npage: 1\n\n#? Orders")
211
+ ```
212
+
213
+ The response carries pagination metadata as **frontmatter** — before the root heading:
214
+
215
+ ```text
216
+ total: 830
217
+ page: 1
218
+ pages: 17
219
+ page-size: 50
220
+
221
+ # Orders
222
+ ## data[]
223
+ - OrderID: 10248
224
+ ...
225
+ ```
226
+
227
+ **Count only** (no rows returned):
228
+
229
+ ```text
230
+ read("count: true\n\n#? Orders")
231
+ ```
232
+
233
+ Returns:
234
+
235
+ ```text
236
+ count: 830
237
+
238
+ # Orders
239
+ ```
240
+
241
+ Use `total` and `pages` to determine whether to fetch more pages.
242
+ For tables with fewer than ~20 rows pagination is optional.
243
+
244
+ ## Field Projection
245
+
246
+ Use `select:` frontmatter to return only specific columns. This keeps
247
+ responses small and context windows focused.
248
+
249
+ ```text
250
+ read("select: OrderID, EmployeeID\npage-size: 50\n\n#? Orders")
251
+ ```
252
+
253
+ Works with both `#` (data) and `#?` (query) documents. When combined with
254
+ aggregation, `select:` filters the result columns after the GROUP BY.
255
+
256
+ ## Joins
257
+
258
+ Use `join:` frontmatter to query across multiple tables in one call.
259
+ The value is `<TableName> on <JoinColumn>` (INNER JOIN, equi-join on a
260
+ column that exists in both tables).
261
+
262
+ ```text
263
+ read("join: Order Details on OrderID\nsum: UnitPrice * Quantity * (1 - Discount) as revenue\ngroup: EmployeeID\nsort: revenue desc\n\n#? Orders")
264
+ ```
265
+
266
+ **Multiple joins** — comma-separated in a single `join:` value:
267
+
268
+ ```text
269
+ join: Order Details on OrderID, Employees on EmployeeID
270
+ ```
271
+
272
+ **Expression syntax** — use `<expression> as <alias>` in aggregate functions
273
+ to compute derived values across joined columns:
274
+
275
+ ```text
276
+ sum: UnitPrice * Quantity * (1 - Discount) as revenue
277
+ ```
278
+
279
+ The alias becomes the result column name. Without `as`, the default alias
280
+ `<func>_<field>` applies (e.g. `sum_Freight`).
281
+
282
+ Allowed in expressions: column names, numeric literals, arithmetic operators
283
+ (`+`, `-`, `*`, `/`), and standard SQL functions (`SUM`, `AVG`, `ROUND`, …).
284
+ Subqueries and SQL keywords are not permitted.
285
+
286
+ **Projection rules for join queries:**
287
+
288
+ - Unambiguous columns (appear in exactly one table) resolve automatically.
289
+ - Join key columns always resolve to the main table.
290
+ - Columns present in multiple tables (other than join keys) require explicit
291
+ qualification — specify them via `select:` or filter on the unambiguous side.
292
+
293
+ ## Aggregation
294
+
295
+ Aggregation is expressed as **frontmatter** before the `#?` heading.
296
+ QBE filter fields narrow rows *before* aggregation (SQL WHERE).
297
+ The `having:` key filters *after* aggregation (SQL HAVING).
298
+
299
+ | Key | SQL | Result column name |
300
+ | --- | --- | --- |
301
+ | `group: f1, f2` | GROUP BY | grouping keys pass through unchanged |
302
+ | `sum: field` | SUM(field) | `sum_field` |
303
+ | `avg: field` | AVG(field) | `avg_field` |
304
+ | `min: field` | MIN(field) | `min_field` |
305
+ | `max: field` | MAX(field) | `max_field` |
306
+ | `count` | COUNT(*) | `count` |
307
+
308
+ Multiple fields per function: `sum: Freight, Total` → `sum_Freight` and `sum_Total`.
309
+
310
+ | Frontmatter | Meaning |
311
+ | --- | --- |
312
+ | `sort: sum_revenue desc, EmployeeID asc` | ORDER BY (multiple columns, mixed) |
313
+ | `having: count > 5` | HAVING COUNT(*) > 5 |
314
+ | `having: sum_Freight > 1000, count > 2` | HAVING … AND … (comma = AND) |
315
+
316
+ `having:` supports: `>`, `>=`, `<`, `<=`, `=`.
317
+ `sort:` references any result column — grouping keys or aggregate aliases.
318
+ `page-size:` and `page:` apply to the aggregated result set.
319
+
320
+ **Example — top 3 employees by revenue:**
321
+
322
+ ```text
323
+ read("group: EmployeeID\nsum: revenue\nsort: sum_revenue desc\npage-size: 3\n\n#? OrderDetails")
324
+ ```
325
+
326
+ ## Error Handling
327
+
328
+ All tools return a `# Error` document on failure:
329
+
330
+ ```text
331
+ # Error
332
+ status: 400
333
+ code: not_found
334
+ message: No records found in Orders
335
+ ```
336
+
337
+ Check the `code` field to decide how to proceed.
338
+
339
+ ## Specification
340
+
341
+ The JMD format is documented at [jmd-spec](https://github.com/ostermeyer/jmd-spec).
342
+
343
+ ## License
344
+
345
+ MIT License. See [LICENSE](LICENSE).
@@ -0,0 +1,308 @@
1
+ # jmd-mcp-sql
2
+
3
+ MCP server that exposes a SQLite database through three JMD tools — a natural language database interface for LLM-driven workflows.
4
+
5
+ ## Tools
6
+
7
+ | Tool | `#` Data | `#?` Query | `#!` Schema | `#-` Delete |
8
+ | --- | --- | --- | --- | --- |
9
+ | `read` | SELECT by fields | SELECT with filters + aggregation | PRAGMA (describe table) | — |
10
+ | `write` | INSERT OR REPLACE | — | CREATE / ALTER TABLE | — |
11
+ | `delete` | — | — | DROP TABLE | DELETE WHERE |
12
+
13
+ All inputs and outputs are JMD documents. The LLM speaks JMD — no SQL required.
14
+
15
+ ## Installation
16
+
17
+ Install the latest version directly from GitHub:
18
+
19
+ ```bash
20
+ pip install git+https://github.com/ostermeyer/jmd-mcp-sql.git
21
+ ```
22
+
23
+ Or pin a specific release:
24
+
25
+ ```bash
26
+ pip install git+https://github.com/ostermeyer/jmd-mcp-sql.git@v0.1
27
+ ```
28
+
29
+ Pre-built packages are attached to each
30
+ [GitHub Release](https://github.com/ostermeyer/jmd-mcp-sql/releases).
31
+
32
+ ## Configuration
33
+
34
+ ### With Claude Code
35
+
36
+ Add to your MCP configuration (`~/.claude/settings.json`):
37
+
38
+ ```json
39
+ {
40
+ "mcpServers": {
41
+ "sql": {
42
+ "command": "jmd-mcp-sql",
43
+ "args": ["/path/to/your.db"]
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ Or use the bundled Northwind demo database (no argument needed):
50
+
51
+ ```json
52
+ {
53
+ "mcpServers": {
54
+ "sql": {
55
+ "command": "jmd-mcp-sql"
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ The demo database ships as `northwind.sql` (plain text, version-controlled). On the
62
+ first run without an explicit path, the server creates `northwind.db` from that file
63
+ automatically. The `.db` file is not tracked by git.
64
+
65
+ ## JMD Document Syntax
66
+
67
+ Every document starts with a heading line that sets the document type and table name,
68
+ followed by `key: value` pairs (one per line):
69
+
70
+ ```text
71
+ # Product → data document (exact lookup / insert-or-replace)
72
+ #? Product → query document (filter / list / aggregate)
73
+ #! Product → schema document (describe / create / drop table)
74
+ #- Product → delete document (delete matching records)
75
+
76
+ key: value → string, integer, or float — inferred automatically
77
+ key: true/false → boolean
78
+ ```
79
+
80
+ ## Discovering the Database
81
+
82
+ To see which tables exist, read each table's schema:
83
+
84
+ ```text
85
+ read("#! Customers")
86
+ ```
87
+
88
+ This returns a `#!` document with column names, JMD types, and modifiers
89
+ (`readonly` = primary key, `optional` = nullable).
90
+
91
+ ## Typical Workflows
92
+
93
+ **List all rows** (small tables only):
94
+
95
+ ```text
96
+ read("#? Orders")
97
+ ```
98
+
99
+ **Filter rows — equality:**
100
+
101
+ ```text
102
+ read("#? Orders\nstatus: shipped")
103
+ ```
104
+
105
+ **Filter rows — comparison:**
106
+
107
+ ```text
108
+ read("#? Orders\nFreight: > 50")
109
+ ```
110
+
111
+ **Filter rows — alternation (OR):**
112
+
113
+ ```text
114
+ read("#? Orders\nShipCountry: Germany|France|UK")
115
+ ```
116
+
117
+ **Filter rows — contains (case-insensitive substring):**
118
+
119
+ ```text
120
+ read("#? Customers\nCompanyName: ~Corp")
121
+ ```
122
+
123
+ **Filter rows — regex pattern:**
124
+
125
+ ```text
126
+ read("#? Products\nProductName: ^Chai.*")
127
+ ```
128
+
129
+ **Filter rows — negation** (composes with any operator):
130
+
131
+ ```text
132
+ read("#? Orders\nShipCountry: !Germany")
133
+ read("#? Products\nProductName: !^LEGACY.*")
134
+ ```
135
+
136
+ **Look up one record:**
137
+
138
+ ```text
139
+ read("# Customers\nid: 42")
140
+ ```
141
+
142
+ **Insert or replace a record:**
143
+
144
+ ```text
145
+ write("# Orders\nid: 1\nstatus: pending\ntotal: 99.90")
146
+ ```
147
+
148
+ **Create a table:**
149
+
150
+ ```text
151
+ write("#! Products\nid: integer readonly\nname: string\nprice: float optional")
152
+ ```
153
+
154
+ **Delete a record:**
155
+
156
+ ```text
157
+ delete("#- Orders\nid: 1")
158
+ ```
159
+
160
+ **Drop a table:**
161
+
162
+ ```text
163
+ delete("#! OldTable")
164
+ ```
165
+
166
+ ## Pagination
167
+
168
+ Always use pagination when querying tables that may contain many rows.
169
+
170
+ Use frontmatter fields before the `#?` heading to control pagination:
171
+
172
+ ```text
173
+ read("page-size: 50\npage: 1\n\n#? Orders")
174
+ ```
175
+
176
+ The response carries pagination metadata as **frontmatter** — before the root heading:
177
+
178
+ ```text
179
+ total: 830
180
+ page: 1
181
+ pages: 17
182
+ page-size: 50
183
+
184
+ # Orders
185
+ ## data[]
186
+ - OrderID: 10248
187
+ ...
188
+ ```
189
+
190
+ **Count only** (no rows returned):
191
+
192
+ ```text
193
+ read("count: true\n\n#? Orders")
194
+ ```
195
+
196
+ Returns:
197
+
198
+ ```text
199
+ count: 830
200
+
201
+ # Orders
202
+ ```
203
+
204
+ Use `total` and `pages` to determine whether to fetch more pages.
205
+ For tables with fewer than ~20 rows pagination is optional.
206
+
207
+ ## Field Projection
208
+
209
+ Use `select:` frontmatter to return only specific columns. This keeps
210
+ responses small and context windows focused.
211
+
212
+ ```text
213
+ read("select: OrderID, EmployeeID\npage-size: 50\n\n#? Orders")
214
+ ```
215
+
216
+ Works with both `#` (data) and `#?` (query) documents. When combined with
217
+ aggregation, `select:` filters the result columns after the GROUP BY.
218
+
219
+ ## Joins
220
+
221
+ Use `join:` frontmatter to query across multiple tables in one call.
222
+ The value is `<TableName> on <JoinColumn>` (INNER JOIN, equi-join on a
223
+ column that exists in both tables).
224
+
225
+ ```text
226
+ read("join: Order Details on OrderID\nsum: UnitPrice * Quantity * (1 - Discount) as revenue\ngroup: EmployeeID\nsort: revenue desc\n\n#? Orders")
227
+ ```
228
+
229
+ **Multiple joins** — comma-separated in a single `join:` value:
230
+
231
+ ```text
232
+ join: Order Details on OrderID, Employees on EmployeeID
233
+ ```
234
+
235
+ **Expression syntax** — use `<expression> as <alias>` in aggregate functions
236
+ to compute derived values across joined columns:
237
+
238
+ ```text
239
+ sum: UnitPrice * Quantity * (1 - Discount) as revenue
240
+ ```
241
+
242
+ The alias becomes the result column name. Without `as`, the default alias
243
+ `<func>_<field>` applies (e.g. `sum_Freight`).
244
+
245
+ Allowed in expressions: column names, numeric literals, arithmetic operators
246
+ (`+`, `-`, `*`, `/`), and standard SQL functions (`SUM`, `AVG`, `ROUND`, …).
247
+ Subqueries and SQL keywords are not permitted.
248
+
249
+ **Projection rules for join queries:**
250
+
251
+ - Unambiguous columns (appear in exactly one table) resolve automatically.
252
+ - Join key columns always resolve to the main table.
253
+ - Columns present in multiple tables (other than join keys) require explicit
254
+ qualification — specify them via `select:` or filter on the unambiguous side.
255
+
256
+ ## Aggregation
257
+
258
+ Aggregation is expressed as **frontmatter** before the `#?` heading.
259
+ QBE filter fields narrow rows *before* aggregation (SQL WHERE).
260
+ The `having:` key filters *after* aggregation (SQL HAVING).
261
+
262
+ | Key | SQL | Result column name |
263
+ | --- | --- | --- |
264
+ | `group: f1, f2` | GROUP BY | grouping keys pass through unchanged |
265
+ | `sum: field` | SUM(field) | `sum_field` |
266
+ | `avg: field` | AVG(field) | `avg_field` |
267
+ | `min: field` | MIN(field) | `min_field` |
268
+ | `max: field` | MAX(field) | `max_field` |
269
+ | `count` | COUNT(*) | `count` |
270
+
271
+ Multiple fields per function: `sum: Freight, Total` → `sum_Freight` and `sum_Total`.
272
+
273
+ | Frontmatter | Meaning |
274
+ | --- | --- |
275
+ | `sort: sum_revenue desc, EmployeeID asc` | ORDER BY (multiple columns, mixed) |
276
+ | `having: count > 5` | HAVING COUNT(*) > 5 |
277
+ | `having: sum_Freight > 1000, count > 2` | HAVING … AND … (comma = AND) |
278
+
279
+ `having:` supports: `>`, `>=`, `<`, `<=`, `=`.
280
+ `sort:` references any result column — grouping keys or aggregate aliases.
281
+ `page-size:` and `page:` apply to the aggregated result set.
282
+
283
+ **Example — top 3 employees by revenue:**
284
+
285
+ ```text
286
+ read("group: EmployeeID\nsum: revenue\nsort: sum_revenue desc\npage-size: 3\n\n#? OrderDetails")
287
+ ```
288
+
289
+ ## Error Handling
290
+
291
+ All tools return a `# Error` document on failure:
292
+
293
+ ```text
294
+ # Error
295
+ status: 400
296
+ code: not_found
297
+ message: No records found in Orders
298
+ ```
299
+
300
+ Check the `code` field to decide how to proceed.
301
+
302
+ ## Specification
303
+
304
+ The JMD format is documented at [jmd-spec](https://github.com/ostermeyer/jmd-spec).
305
+
306
+ ## License
307
+
308
+ MIT License. See [LICENSE](LICENSE).
@@ -0,0 +1,4 @@
1
+ """JMD MCP server for SQLite."""
2
+ from .server import main
3
+
4
+ __all__ = ["main"]