mcp-server-motherduck 0.5__tar.gz → 0.6.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 mcp-server-motherduck might be problematic. Click here for more details.
- mcp_server_motherduck-0.6.0/.idea/workspace.xml +111 -0
- mcp_server_motherduck-0.5/README.md → mcp_server_motherduck-0.6.0/PKG-INFO +63 -33
- mcp_server_motherduck-0.5/PKG-INFO → mcp_server_motherduck-0.6.0/README.md +47 -46
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/pyproject.toml +7 -4
- mcp_server_motherduck-0.6.0/src/mcp_server_motherduck/__init__.py +193 -0
- mcp_server_motherduck-0.6.0/src/mcp_server_motherduck/configs.py +32 -0
- mcp_server_motherduck-0.6.0/src/mcp_server_motherduck/database.py +139 -0
- mcp_server_motherduck-0.6.0/src/mcp_server_motherduck/server.py +149 -0
- mcp_server_motherduck-0.6.0/uv.lock +417 -0
- mcp_server_motherduck-0.5/.idea/workspace.xml +0 -66
- mcp_server_motherduck-0.5/src/mcp_server_motherduck/__init__.py +0 -66
- mcp_server_motherduck-0.5/src/mcp_server_motherduck/server.py +0 -299
- mcp_server_motherduck-0.5/uv.lock +0 -529
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/.github/workflows/python-publish.yml +0 -0
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/.gitignore +0 -0
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/.idea/.gitignore +0 -0
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/.idea/mcp-server-motherduck.iml +0 -0
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/.idea/misc.xml +0 -0
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/.idea/modules.xml +0 -0
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/.idea/vcs.xml +0 -0
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/LICENSE +0 -0
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/makefile +0 -0
- {mcp_server_motherduck-0.5 → mcp_server_motherduck-0.6.0}/src/mcp_server_motherduck/prompt.py +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="AutoImportSettings">
|
|
4
|
+
<option name="autoReloadType" value="SELECTIVE" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="CMakeProjectFlavorService">
|
|
7
|
+
<option name="flavorId" value="CMakePlainProjectFlavor" />
|
|
8
|
+
</component>
|
|
9
|
+
<component name="CMakeSettings">
|
|
10
|
+
<configurations>
|
|
11
|
+
<configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" />
|
|
12
|
+
</configurations>
|
|
13
|
+
</component>
|
|
14
|
+
<component name="ChangeListManager">
|
|
15
|
+
<list default="true" id="8bdee1d4-886c-4093-b4cf-95b120034c9e" name="Changes" comment="">
|
|
16
|
+
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
|
17
|
+
<change beforePath="$PROJECT_DIR$/src/mcp_server_motherduck/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/mcp_server_motherduck/__init__.py" afterDir="false" />
|
|
18
|
+
<change beforePath="$PROJECT_DIR$/src/mcp_server_motherduck/database.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/mcp_server_motherduck/database.py" afterDir="false" />
|
|
19
|
+
<change beforePath="$PROJECT_DIR$/src/mcp_server_motherduck/server.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/mcp_server_motherduck/server.py" afterDir="false" />
|
|
20
|
+
<change beforePath="$PROJECT_DIR$/uv.lock" beforeDir="false" afterPath="$PROJECT_DIR$/uv.lock" afterDir="false" />
|
|
21
|
+
</list>
|
|
22
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
23
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
24
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
25
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
26
|
+
</component>
|
|
27
|
+
<component name="ClangdSettings">
|
|
28
|
+
<option name="formatViaClangd" value="false" />
|
|
29
|
+
</component>
|
|
30
|
+
<component name="FlaskConsoleOptions" custom-start-script="import sys sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) from flask.cli import ScriptInfo locals().update(ScriptInfo(create_app=None).load_app().make_shell_context()) print("Python %s on %s\nApp: %s [%s]\nInstance: %s" % (sys.version, sys.platform, app.import_name, app.env, app.instance_path))">
|
|
31
|
+
<envs>
|
|
32
|
+
<env key="FLASK_APP" value="app" />
|
|
33
|
+
</envs>
|
|
34
|
+
<option name="myCustomStartScript" value="import sys sys.path.extend([WORKING_DIR_AND_PYTHON_PATHS]) from flask.cli import ScriptInfo locals().update(ScriptInfo(create_app=None).load_app().make_shell_context()) print("Python %s on %s\nApp: %s [%s]\nInstance: %s" % (sys.version, sys.platform, app.import_name, app.env, app.instance_path))" />
|
|
35
|
+
<option name="myEnvs">
|
|
36
|
+
<map>
|
|
37
|
+
<entry key="FLASK_APP" value="app" />
|
|
38
|
+
</map>
|
|
39
|
+
</option>
|
|
40
|
+
</component>
|
|
41
|
+
<component name="Git.Settings">
|
|
42
|
+
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
43
|
+
</component>
|
|
44
|
+
<component name="MarkdownSettingsMigration">
|
|
45
|
+
<option name="stateVersion" value="1" />
|
|
46
|
+
</component>
|
|
47
|
+
<component name="ProjectColorInfo">{
|
|
48
|
+
"associatedIndex": 3
|
|
49
|
+
}</component>
|
|
50
|
+
<component name="ProjectId" id="2vcb2orYlzlw5ZHOvoXgpTtCdVi" />
|
|
51
|
+
<component name="ProjectViewState">
|
|
52
|
+
<option name="hideEmptyMiddlePackages" value="true" />
|
|
53
|
+
<option name="showLibraryContents" value="true" />
|
|
54
|
+
</component>
|
|
55
|
+
<component name="PropertiesComponent"><![CDATA[{
|
|
56
|
+
"keyToString": {
|
|
57
|
+
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
|
58
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
59
|
+
"RunOnceActivity.cidr.known.project.marker": "true",
|
|
60
|
+
"RunOnceActivity.git.unshallow": "true",
|
|
61
|
+
"RunOnceActivity.readMode.enableVisualFormatting": "true",
|
|
62
|
+
"WebServerToolWindowFactoryState": "false",
|
|
63
|
+
"cf.first.check.clang-format": "false",
|
|
64
|
+
"cidr.known.project.marker": "true",
|
|
65
|
+
"git-widget-placeholder": "adi/add-inapp-sse",
|
|
66
|
+
"last_opened_file_path": "/Users/doehmen/Documents/motherduck/mcp-server-motherduck",
|
|
67
|
+
"node.js.detected.package.eslint": "true",
|
|
68
|
+
"node.js.detected.package.tslint": "true",
|
|
69
|
+
"node.js.selected.package.eslint": "(autodetect)",
|
|
70
|
+
"node.js.selected.package.tslint": "(autodetect)",
|
|
71
|
+
"nodejs_package_manager_path": "npm",
|
|
72
|
+
"project.structure.last.edited": "Project",
|
|
73
|
+
"project.structure.proportion": "0.0",
|
|
74
|
+
"project.structure.side.proportion": "0.0",
|
|
75
|
+
"settings.editor.selected.configurable": "preferences.updates",
|
|
76
|
+
"vue.rearranger.settings.migration": "true"
|
|
77
|
+
}
|
|
78
|
+
}]]></component>
|
|
79
|
+
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
|
80
|
+
<component name="TaskManager">
|
|
81
|
+
<task active="true" id="Default" summary="Default task">
|
|
82
|
+
<changelist id="8bdee1d4-886c-4093-b4cf-95b120034c9e" name="Changes" comment="" />
|
|
83
|
+
<created>1744447105618</created>
|
|
84
|
+
<option name="number" value="Default" />
|
|
85
|
+
<option name="presentableId" value="Default" />
|
|
86
|
+
<updated>1744447105618</updated>
|
|
87
|
+
<workItem from="1744447107447" duration="7470000" />
|
|
88
|
+
<workItem from="1744787944049" duration="599000" />
|
|
89
|
+
<workItem from="1744896732489" duration="6070000" />
|
|
90
|
+
<workItem from="1747725447463" duration="5949000" />
|
|
91
|
+
<workItem from="1747924921526" duration="17579000" />
|
|
92
|
+
<workItem from="1749541956173" duration="67000" />
|
|
93
|
+
<workItem from="1749546948380" duration="272000" />
|
|
94
|
+
<workItem from="1749547445107" duration="2288000" />
|
|
95
|
+
<workItem from="1750068892931" duration="1202000" />
|
|
96
|
+
<workItem from="1750076934923" duration="598000" />
|
|
97
|
+
<workItem from="1750084124415" duration="194000" />
|
|
98
|
+
<workItem from="1750084439422" duration="575000" />
|
|
99
|
+
<workItem from="1750086684829" duration="92000" />
|
|
100
|
+
<workItem from="1750099676834" duration="5651000" />
|
|
101
|
+
</task>
|
|
102
|
+
<servers />
|
|
103
|
+
</component>
|
|
104
|
+
<component name="TypeScriptGeneratedFilesManager">
|
|
105
|
+
<option name="version" value="3" />
|
|
106
|
+
</component>
|
|
107
|
+
<component name="XSLT-Support.FileAssociations.UIState">
|
|
108
|
+
<expand />
|
|
109
|
+
<select />
|
|
110
|
+
</component>
|
|
111
|
+
</project>
|
|
@@ -1,7 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-server-motherduck
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: A MCP server for MotherDuck and local DuckDB
|
|
5
|
+
Author-email: tdoehmen <till@motherduck.com>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: anyio>=4.8.0
|
|
9
|
+
Requires-Dist: click>=8.1.8
|
|
10
|
+
Requires-Dist: duckdb==1.3.0
|
|
11
|
+
Requires-Dist: mcp>=1.9.4
|
|
12
|
+
Requires-Dist: starlette>=0.46.1
|
|
13
|
+
Requires-Dist: tabulate>=0.9.0
|
|
14
|
+
Requires-Dist: uvicorn>=0.34.0
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
1
17
|
# MotherDuck's DuckDB MCP Server
|
|
2
18
|
|
|
3
19
|
An MCP server implementation that interacts with DuckDB and MotherDuck databases, providing SQL analytics capabilities to AI Assistants and IDEs.
|
|
4
20
|
|
|
21
|
+
[](https://cursor.com/install-mcp?name=DuckDB&config=eyJjb21tYW5kIjoidXZ4IG1jcC1zZXJ2ZXItbW90aGVyZHVjayAtLWRiLXBhdGggbWQ6IiwiZW52Ijp7Im1vdGhlcmR1Y2tfdG9rZW4iOiIifX0%3D)
|
|
22
|
+
|
|
5
23
|
## Resources
|
|
6
24
|
- [Close the Loop: Faster Data Pipelines with MCP, DuckDB & AI (Blogpost)](https://motherduck.com/blog/faster-data-pipelines-with-mcp-duckdb-ai/)
|
|
7
25
|
- [Faster Data Pipelines development with MCP and DuckDB (YouTube)](https://www.youtube.com/watch?v=yG1mv8ZRxcU)
|
|
@@ -32,6 +50,37 @@ The server offers one tool:
|
|
|
32
50
|
|
|
33
51
|
All interactions with both DuckDB and MotherDuck are done through writing SQL queries.
|
|
34
52
|
|
|
53
|
+
## Command Line Parameters
|
|
54
|
+
|
|
55
|
+
The MCP server supports the following parameters:
|
|
56
|
+
|
|
57
|
+
| Parameter | Type | Default | Description |
|
|
58
|
+
|-----------|------|---------|-------------|
|
|
59
|
+
| `--transport` | Choice | `stdio` | Transport type. Options: `stdio`, `sse`, `stream` |
|
|
60
|
+
| `--port` | Integer | `8000` | Port to listen on for sse and stream transport mode |
|
|
61
|
+
| `--db-path` | String | `md:` | Path to local DuckDB database file or MotherDuck database |
|
|
62
|
+
| `--motherduck-token` | String | `None` | Access token to use for MotherDuck database connections (uses `motherduck_token` env var by default) |
|
|
63
|
+
| `--read-only` | Flag | `False` | Flag for connecting to DuckDB in read-only mode. Only supported for local DuckDB databases. Uses short-lived connections for concurrent access |
|
|
64
|
+
| `--home-dir` | String | `None` | Home directory for DuckDB (uses `HOME` env var by default) |
|
|
65
|
+
| `--saas-mode` | Flag | `False` | Flag for connecting to MotherDuck in SaaS mode |
|
|
66
|
+
| `--json-response` | Flag | `False` | Enable JSON responses for HTTP stream. Only supported for `stream` transport |
|
|
67
|
+
|
|
68
|
+
### Quick Usage Examples
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Connect to local DuckDB file in read-only mode with stream transport mode
|
|
72
|
+
uvx mcp-server-motherduck --transport stream --db-path /path/to/local.db --read-only
|
|
73
|
+
|
|
74
|
+
# Connect to MotherDuck with token with stream transport mode
|
|
75
|
+
uvx mcp-server-motherduck --transport stream --db-path md: --motherduck-token YOUR_TOKEN
|
|
76
|
+
|
|
77
|
+
# Connect to local DuckDB file in read-only mode with stream transport mode
|
|
78
|
+
uvx mcp-server-motherduck --transport stream --db-path /path/to/local.db --read-only
|
|
79
|
+
|
|
80
|
+
# Connect to MotherDuck in SaaS mode for enhanced security with stream transport mode
|
|
81
|
+
uvx mcp-server-motherduck --transport stream --db-path md: --motherduck-token YOUR_TOKEN --saas-mode
|
|
82
|
+
```
|
|
83
|
+
|
|
35
84
|
## Getting Started
|
|
36
85
|
|
|
37
86
|
### General Prerequisites
|
|
@@ -81,11 +130,11 @@ See [Connect to local DuckDB](#connect-to-local-duckdb).
|
|
|
81
130
|
|
|
82
131
|
### Usage with VS Code
|
|
83
132
|
|
|
84
|
-
[](https://insiders.vscode.dev/redirect/mcp/install?name=mcp-server-motherduck&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22mcp-server-motherduck%22%2C%22--db-path%22%2C%22md%3A%22%2C%22--motherduck-token%22%2C%22%24%7Binput%3Amotherduck_token%7D%22%5D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22motherduck_token%22%2C%22description%22%3A%22MotherDuck+Token%22%2C%22password%22%3Atrue%7D%5D) [](https://insiders.vscode.dev/redirect/mcp/install?name=mcp-server-motherduck&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22mcp-server-motherduck%22%2C%22--db-path%22%2C%22md%3A%22%2C%22--motherduck-token%22%2C%22%24%7Binput%3Amotherduck_token%7D%22%5D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22motherduck_token%22%2C%22description%22%3A%22MotherDuck+Token%22%2C%22password%22%3Atrue%7D%5D&quality=insiders)
|
|
85
134
|
|
|
86
|
-
|
|
135
|
+
For the quickest installation, click one of the "Install with UV" buttons at the top.
|
|
87
136
|
|
|
88
|
-
|
|
137
|
+
#### Manual Installation
|
|
89
138
|
|
|
90
139
|
Add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing `Ctrl + Shift + P` and typing `Preferences: Open User Settings (JSON)`.
|
|
91
140
|
|
|
@@ -172,7 +221,6 @@ Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace
|
|
|
172
221
|
**Important Notes**:
|
|
173
222
|
|
|
174
223
|
- Replace `YOUR_MOTHERDUCK_TOKEN_HERE` with your actual MotherDuck token
|
|
175
|
-
- Replace `YOUR_HOME_FOLDER_PATH` with the path to your home directory (needed by DuckDB for file operations). For example, on macOS, it would be `/Users/your_username`
|
|
176
224
|
- The `HOME` environment variable is required for DuckDB to function properly.
|
|
177
225
|
|
|
178
226
|
## Securing your MCP Server when querying MotherDuck
|
|
@@ -277,47 +325,29 @@ Once configured, you can e.g. ask Claude to run queries like:
|
|
|
277
325
|
- "Join data from my local DuckDB database with a table in MotherDuck"
|
|
278
326
|
- "Analyze data stored in Amazon S3"
|
|
279
327
|
|
|
280
|
-
##
|
|
281
|
-
|
|
282
|
-
The server is designed to be run by tools like Claude Desktop and Cursor, but you can start it manually for testing purposes. When testing the server manually, you can specify which database to connect to using the `--db-path` parameter:
|
|
283
|
-
|
|
284
|
-
1. **Default MotherDuck database**:
|
|
285
|
-
|
|
286
|
-
- To connect to the default MotherDuck database, you will need to pass the auth token using the `--motherduck-token` parameter.
|
|
287
|
-
|
|
288
|
-
```bash
|
|
289
|
-
uvx mcp-server-motherduck --db-path md: --motherduck-token <your_motherduck_token>
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
2. **Specific MotherDuck database**:
|
|
293
|
-
|
|
294
|
-
```bash
|
|
295
|
-
uvx mcp-server-motherduck --db-path md:your_database_name --motherduck-token <your_motherduck_token>
|
|
296
|
-
```
|
|
328
|
+
## Running in SSE mode
|
|
297
329
|
|
|
298
|
-
|
|
330
|
+
The server can run in SSE mode in two ways:
|
|
299
331
|
|
|
300
|
-
|
|
301
|
-
uvx mcp-server-motherduck --db-path /path/to/your/local.db
|
|
302
|
-
```
|
|
332
|
+
### Direct SSE mode
|
|
303
333
|
|
|
304
|
-
|
|
334
|
+
Run the server directly in SSE mode using the `--transport sse` flag:
|
|
305
335
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
336
|
+
```bash
|
|
337
|
+
uvx mcp-server-motherduck --transport sse --port 8000 --db-path md: --motherduck-token <your_motherduck_token>
|
|
338
|
+
```
|
|
309
339
|
|
|
310
|
-
|
|
340
|
+
This will start the server listening on the specified port (default 8000) and you can point your clients directly to this endpoint.
|
|
311
341
|
|
|
312
|
-
|
|
342
|
+
### Using supergateway
|
|
313
343
|
|
|
314
|
-
|
|
344
|
+
Alternatively, you can run SSE mode using `supergateway`:
|
|
315
345
|
|
|
316
346
|
```bash
|
|
317
347
|
npx -y supergateway --stdio "uvx mcp-server-motherduck --db-path md: --motherduck-token <your_motherduck_token>"
|
|
318
348
|
```
|
|
319
349
|
|
|
320
|
-
|
|
350
|
+
Both methods allow you to point your clients such as Claude Desktop, Cursor to the SSE endpoint.
|
|
321
351
|
|
|
322
352
|
## Development configuration
|
|
323
353
|
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: mcp-server-motherduck
|
|
3
|
-
Version: 0.5
|
|
4
|
-
Summary: A MCP server for MotherDuck and local DuckDB
|
|
5
|
-
Author-email: tdoehmen <till@motherduck.com>
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
Requires-Python: >=3.10
|
|
8
|
-
Requires-Dist: duckdb==1.2.2
|
|
9
|
-
Requires-Dist: mcp>=1.3.0
|
|
10
|
-
Requires-Dist: pandas>=2.0.0
|
|
11
|
-
Requires-Dist: tabulate>=0.9.0
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
|
|
14
1
|
# MotherDuck's DuckDB MCP Server
|
|
15
2
|
|
|
16
3
|
An MCP server implementation that interacts with DuckDB and MotherDuck databases, providing SQL analytics capabilities to AI Assistants and IDEs.
|
|
17
4
|
|
|
5
|
+
[](https://cursor.com/install-mcp?name=DuckDB&config=eyJjb21tYW5kIjoidXZ4IG1jcC1zZXJ2ZXItbW90aGVyZHVjayAtLWRiLXBhdGggbWQ6IiwiZW52Ijp7Im1vdGhlcmR1Y2tfdG9rZW4iOiIifX0%3D)
|
|
6
|
+
|
|
18
7
|
## Resources
|
|
19
8
|
- [Close the Loop: Faster Data Pipelines with MCP, DuckDB & AI (Blogpost)](https://motherduck.com/blog/faster-data-pipelines-with-mcp-duckdb-ai/)
|
|
20
9
|
- [Faster Data Pipelines development with MCP and DuckDB (YouTube)](https://www.youtube.com/watch?v=yG1mv8ZRxcU)
|
|
@@ -45,6 +34,37 @@ The server offers one tool:
|
|
|
45
34
|
|
|
46
35
|
All interactions with both DuckDB and MotherDuck are done through writing SQL queries.
|
|
47
36
|
|
|
37
|
+
## Command Line Parameters
|
|
38
|
+
|
|
39
|
+
The MCP server supports the following parameters:
|
|
40
|
+
|
|
41
|
+
| Parameter | Type | Default | Description |
|
|
42
|
+
|-----------|------|---------|-------------|
|
|
43
|
+
| `--transport` | Choice | `stdio` | Transport type. Options: `stdio`, `sse`, `stream` |
|
|
44
|
+
| `--port` | Integer | `8000` | Port to listen on for sse and stream transport mode |
|
|
45
|
+
| `--db-path` | String | `md:` | Path to local DuckDB database file or MotherDuck database |
|
|
46
|
+
| `--motherduck-token` | String | `None` | Access token to use for MotherDuck database connections (uses `motherduck_token` env var by default) |
|
|
47
|
+
| `--read-only` | Flag | `False` | Flag for connecting to DuckDB in read-only mode. Only supported for local DuckDB databases. Uses short-lived connections for concurrent access |
|
|
48
|
+
| `--home-dir` | String | `None` | Home directory for DuckDB (uses `HOME` env var by default) |
|
|
49
|
+
| `--saas-mode` | Flag | `False` | Flag for connecting to MotherDuck in SaaS mode |
|
|
50
|
+
| `--json-response` | Flag | `False` | Enable JSON responses for HTTP stream. Only supported for `stream` transport |
|
|
51
|
+
|
|
52
|
+
### Quick Usage Examples
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Connect to local DuckDB file in read-only mode with stream transport mode
|
|
56
|
+
uvx mcp-server-motherduck --transport stream --db-path /path/to/local.db --read-only
|
|
57
|
+
|
|
58
|
+
# Connect to MotherDuck with token with stream transport mode
|
|
59
|
+
uvx mcp-server-motherduck --transport stream --db-path md: --motherduck-token YOUR_TOKEN
|
|
60
|
+
|
|
61
|
+
# Connect to local DuckDB file in read-only mode with stream transport mode
|
|
62
|
+
uvx mcp-server-motherduck --transport stream --db-path /path/to/local.db --read-only
|
|
63
|
+
|
|
64
|
+
# Connect to MotherDuck in SaaS mode for enhanced security with stream transport mode
|
|
65
|
+
uvx mcp-server-motherduck --transport stream --db-path md: --motherduck-token YOUR_TOKEN --saas-mode
|
|
66
|
+
```
|
|
67
|
+
|
|
48
68
|
## Getting Started
|
|
49
69
|
|
|
50
70
|
### General Prerequisites
|
|
@@ -94,11 +114,11 @@ See [Connect to local DuckDB](#connect-to-local-duckdb).
|
|
|
94
114
|
|
|
95
115
|
### Usage with VS Code
|
|
96
116
|
|
|
97
|
-
[](https://insiders.vscode.dev/redirect/mcp/install?name=mcp-server-motherduck&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22mcp-server-motherduck%22%2C%22--db-path%22%2C%22md%3A%22%2C%22--motherduck-token%22%2C%22%24%7Binput%3Amotherduck_token%7D%22%5D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22motherduck_token%22%2C%22description%22%3A%22MotherDuck+Token%22%2C%22password%22%3Atrue%7D%5D) [](https://insiders.vscode.dev/redirect/mcp/install?name=mcp-server-motherduck&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22mcp-server-motherduck%22%2C%22--db-path%22%2C%22md%3A%22%2C%22--motherduck-token%22%2C%22%24%7Binput%3Amotherduck_token%7D%22%5D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22motherduck_token%22%2C%22description%22%3A%22MotherDuck+Token%22%2C%22password%22%3Atrue%7D%5D&quality=insiders)
|
|
98
118
|
|
|
99
|
-
|
|
119
|
+
For the quickest installation, click one of the "Install with UV" buttons at the top.
|
|
100
120
|
|
|
101
|
-
|
|
121
|
+
#### Manual Installation
|
|
102
122
|
|
|
103
123
|
Add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing `Ctrl + Shift + P` and typing `Preferences: Open User Settings (JSON)`.
|
|
104
124
|
|
|
@@ -185,7 +205,6 @@ Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace
|
|
|
185
205
|
**Important Notes**:
|
|
186
206
|
|
|
187
207
|
- Replace `YOUR_MOTHERDUCK_TOKEN_HERE` with your actual MotherDuck token
|
|
188
|
-
- Replace `YOUR_HOME_FOLDER_PATH` with the path to your home directory (needed by DuckDB for file operations). For example, on macOS, it would be `/Users/your_username`
|
|
189
208
|
- The `HOME` environment variable is required for DuckDB to function properly.
|
|
190
209
|
|
|
191
210
|
## Securing your MCP Server when querying MotherDuck
|
|
@@ -290,47 +309,29 @@ Once configured, you can e.g. ask Claude to run queries like:
|
|
|
290
309
|
- "Join data from my local DuckDB database with a table in MotherDuck"
|
|
291
310
|
- "Analyze data stored in Amazon S3"
|
|
292
311
|
|
|
293
|
-
##
|
|
294
|
-
|
|
295
|
-
The server is designed to be run by tools like Claude Desktop and Cursor, but you can start it manually for testing purposes. When testing the server manually, you can specify which database to connect to using the `--db-path` parameter:
|
|
296
|
-
|
|
297
|
-
1. **Default MotherDuck database**:
|
|
298
|
-
|
|
299
|
-
- To connect to the default MotherDuck database, you will need to pass the auth token using the `--motherduck-token` parameter.
|
|
300
|
-
|
|
301
|
-
```bash
|
|
302
|
-
uvx mcp-server-motherduck --db-path md: --motherduck-token <your_motherduck_token>
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
2. **Specific MotherDuck database**:
|
|
306
|
-
|
|
307
|
-
```bash
|
|
308
|
-
uvx mcp-server-motherduck --db-path md:your_database_name --motherduck-token <your_motherduck_token>
|
|
309
|
-
```
|
|
312
|
+
## Running in SSE mode
|
|
310
313
|
|
|
311
|
-
|
|
314
|
+
The server can run in SSE mode in two ways:
|
|
312
315
|
|
|
313
|
-
|
|
314
|
-
uvx mcp-server-motherduck --db-path /path/to/your/local.db
|
|
315
|
-
```
|
|
316
|
+
### Direct SSE mode
|
|
316
317
|
|
|
317
|
-
|
|
318
|
+
Run the server directly in SSE mode using the `--transport sse` flag:
|
|
318
319
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
```bash
|
|
321
|
+
uvx mcp-server-motherduck --transport sse --port 8000 --db-path md: --motherduck-token <your_motherduck_token>
|
|
322
|
+
```
|
|
322
323
|
|
|
323
|
-
|
|
324
|
+
This will start the server listening on the specified port (default 8000) and you can point your clients directly to this endpoint.
|
|
324
325
|
|
|
325
|
-
|
|
326
|
+
### Using supergateway
|
|
326
327
|
|
|
327
|
-
|
|
328
|
+
Alternatively, you can run SSE mode using `supergateway`:
|
|
328
329
|
|
|
329
330
|
```bash
|
|
330
331
|
npx -y supergateway --stdio "uvx mcp-server-motherduck --db-path md: --motherduck-token <your_motherduck_token>"
|
|
331
332
|
```
|
|
332
333
|
|
|
333
|
-
|
|
334
|
+
Both methods allow you to point your clients such as Claude Desktop, Cursor to the SSE endpoint.
|
|
334
335
|
|
|
335
336
|
## Development configuration
|
|
336
337
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mcp-server-motherduck"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "A MCP server for MotherDuck and local DuckDB"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"
|
|
9
|
-
"duckdb==1.2.2",
|
|
10
|
-
"pandas>=2.0.0",
|
|
8
|
+
"duckdb==1.3.0",
|
|
11
9
|
"tabulate>=0.9.0",
|
|
10
|
+
"click>=8.1.8",
|
|
11
|
+
"starlette>=0.46.1",
|
|
12
|
+
"uvicorn>=0.34.0",
|
|
13
|
+
"anyio>=4.8.0",
|
|
14
|
+
"mcp>=1.9.4",
|
|
12
15
|
]
|
|
13
16
|
|
|
14
17
|
[[project.authors]]
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import anyio
|
|
2
|
+
import logging
|
|
3
|
+
import click
|
|
4
|
+
from .server import build_application
|
|
5
|
+
from .configs import SERVER_VERSION, SERVER_LOCALHOST, UVICORN_LOGGING_CONFIG
|
|
6
|
+
|
|
7
|
+
__version__ = SERVER_VERSION
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger("mcp_server_motherduck")
|
|
10
|
+
logging.basicConfig(
|
|
11
|
+
level=logging.INFO, format="[motherduck] %(levelname)s - %(message)s"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command()
|
|
16
|
+
@click.option("--port", default=8000, help="Port to listen on for SSE")
|
|
17
|
+
@click.option(
|
|
18
|
+
"--transport",
|
|
19
|
+
type=click.Choice(["stdio", "sse", "stream"]),
|
|
20
|
+
default="stdio",
|
|
21
|
+
help="(Default: `stdio`) Transport type",
|
|
22
|
+
)
|
|
23
|
+
@click.option(
|
|
24
|
+
"--db-path",
|
|
25
|
+
default="md:",
|
|
26
|
+
help="(Default: `md:`) Path to local DuckDB database file or MotherDuck database",
|
|
27
|
+
)
|
|
28
|
+
@click.option(
|
|
29
|
+
"--motherduck-token",
|
|
30
|
+
default=None,
|
|
31
|
+
help="(Default: env var `motherduck_token`) Access token to use for MotherDuck database connections",
|
|
32
|
+
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--home-dir",
|
|
35
|
+
default=None,
|
|
36
|
+
help="(Default: env var `HOME`) Home directory for DuckDB",
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"--saas-mode",
|
|
40
|
+
is_flag=True,
|
|
41
|
+
help="Flag for connecting to MotherDuck in SaaS mode",
|
|
42
|
+
)
|
|
43
|
+
@click.option(
|
|
44
|
+
"--read-only",
|
|
45
|
+
is_flag=True,
|
|
46
|
+
help="Flag for connecting to DuckDB in read-only mode. Only supported for local DuckDB databases. Also makes use of short lived connections so multiple MCP clients or other systems can remain active (though each operation must be done sequentially).",
|
|
47
|
+
)
|
|
48
|
+
@click.option(
|
|
49
|
+
"--json-response",
|
|
50
|
+
is_flag=True,
|
|
51
|
+
default=False,
|
|
52
|
+
help="(Default: `False`) Enable JSON responses instead of SSE streams. Only supported for `stream` transport.",
|
|
53
|
+
)
|
|
54
|
+
def main(
|
|
55
|
+
port,
|
|
56
|
+
transport,
|
|
57
|
+
db_path,
|
|
58
|
+
motherduck_token,
|
|
59
|
+
home_dir,
|
|
60
|
+
saas_mode,
|
|
61
|
+
read_only,
|
|
62
|
+
json_response,
|
|
63
|
+
):
|
|
64
|
+
"""Main entry point for the package."""
|
|
65
|
+
|
|
66
|
+
logger.info("🦆 MotherDuck MCP Server v" + SERVER_VERSION)
|
|
67
|
+
logger.info("Ready to execute SQL queries via DuckDB/MotherDuck")
|
|
68
|
+
|
|
69
|
+
app, init_opts = build_application(
|
|
70
|
+
db_path=db_path,
|
|
71
|
+
motherduck_token=motherduck_token,
|
|
72
|
+
home_dir=home_dir,
|
|
73
|
+
saas_mode=saas_mode,
|
|
74
|
+
read_only=read_only,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if transport == "sse":
|
|
78
|
+
from mcp.server.sse import SseServerTransport
|
|
79
|
+
from starlette.applications import Starlette
|
|
80
|
+
from starlette.responses import Response
|
|
81
|
+
from starlette.routing import Mount, Route
|
|
82
|
+
|
|
83
|
+
logger.info("MCP server initialized in \033[32msse\033[0m mode")
|
|
84
|
+
|
|
85
|
+
sse = SseServerTransport("/messages/")
|
|
86
|
+
|
|
87
|
+
async def handle_sse(request):
|
|
88
|
+
async with sse.connect_sse(
|
|
89
|
+
request.scope, request.receive, request._send
|
|
90
|
+
) as (read_stream, write_stream):
|
|
91
|
+
await app.run(read_stream, write_stream, init_opts)
|
|
92
|
+
return Response()
|
|
93
|
+
|
|
94
|
+
logger.info(
|
|
95
|
+
f"🦆 Connect to MotherDuck MCP Server at \033[1m\033[36mhttp://{SERVER_LOCALHOST}:{port}/sse\033[0m"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
starlette_app = Starlette(
|
|
99
|
+
debug=True,
|
|
100
|
+
routes=[
|
|
101
|
+
Route("/sse", endpoint=handle_sse, methods=["GET"]),
|
|
102
|
+
Mount("/messages/", app=sse.handle_post_message),
|
|
103
|
+
],
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
import uvicorn
|
|
107
|
+
|
|
108
|
+
uvicorn.run(
|
|
109
|
+
starlette_app,
|
|
110
|
+
host=SERVER_LOCALHOST,
|
|
111
|
+
port=port,
|
|
112
|
+
log_config=UVICORN_LOGGING_CONFIG,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
elif transport == "stream":
|
|
116
|
+
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
|
|
117
|
+
from collections.abc import AsyncIterator
|
|
118
|
+
from starlette.applications import Starlette
|
|
119
|
+
from starlette.routing import Mount
|
|
120
|
+
from starlette.types import Receive, Scope, Send
|
|
121
|
+
import contextlib
|
|
122
|
+
|
|
123
|
+
logger.info("MCP server initialized in \033[32mhttp-streamable\033[0m mode")
|
|
124
|
+
|
|
125
|
+
# Create the session manager with true stateless mode
|
|
126
|
+
session_manager = StreamableHTTPSessionManager(
|
|
127
|
+
app=app,
|
|
128
|
+
event_store=None,
|
|
129
|
+
json_response=json_response,
|
|
130
|
+
stateless=True,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
async def handle_streamable_http(
|
|
134
|
+
scope: Scope, receive: Receive, send: Send
|
|
135
|
+
) -> None:
|
|
136
|
+
await session_manager.handle_request(scope, receive, send)
|
|
137
|
+
|
|
138
|
+
@contextlib.asynccontextmanager
|
|
139
|
+
async def lifespan(app: Starlette) -> AsyncIterator[None]:
|
|
140
|
+
"""Context manager for session manager."""
|
|
141
|
+
async with session_manager.run():
|
|
142
|
+
logger.info("MCP server started with StreamableHTTP session manager")
|
|
143
|
+
try:
|
|
144
|
+
yield
|
|
145
|
+
finally:
|
|
146
|
+
logger.info(
|
|
147
|
+
"🦆 MotherDuck MCP Server in \033[32mhttp-streamable\033[0m mode shutting down"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
logger.info(
|
|
151
|
+
f"🦆 Connect to MotherDuck MCP Server at \033[1m\033[36mhttp://{SERVER_LOCALHOST}:{port}/mcp\033[0m"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Create an ASGI application using the transport
|
|
155
|
+
starlette_app = Starlette(
|
|
156
|
+
debug=True,
|
|
157
|
+
routes=[
|
|
158
|
+
Mount("/mcp", app=handle_streamable_http),
|
|
159
|
+
],
|
|
160
|
+
lifespan=lifespan,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
import uvicorn
|
|
164
|
+
|
|
165
|
+
uvicorn.run(
|
|
166
|
+
starlette_app,
|
|
167
|
+
host=SERVER_LOCALHOST,
|
|
168
|
+
port=port,
|
|
169
|
+
log_config=UVICORN_LOGGING_CONFIG,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
else:
|
|
173
|
+
from mcp.server.stdio import stdio_server
|
|
174
|
+
|
|
175
|
+
logger.info("MCP server initialized in \033[32mstdio\033[0m mode")
|
|
176
|
+
logger.info("Waiting for client connection")
|
|
177
|
+
|
|
178
|
+
async def arun():
|
|
179
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
180
|
+
await app.run(read_stream, write_stream, init_opts)
|
|
181
|
+
|
|
182
|
+
anyio.run(arun)
|
|
183
|
+
# This will only be reached when the server is shutting down
|
|
184
|
+
logger.info(
|
|
185
|
+
"🦆 MotherDuck MCP Server in \033[32mstdio\033[0m mode shutting down"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
# Optionally expose other important items at package level
|
|
190
|
+
__all__ = ["main"]
|
|
191
|
+
|
|
192
|
+
if __name__ == "__main__":
|
|
193
|
+
main()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
SERVER_VERSION = "0.6.0"
|
|
4
|
+
|
|
5
|
+
SERVER_LOCALHOST = "127.0.0.1"
|
|
6
|
+
|
|
7
|
+
UVICORN_LOGGING_CONFIG: dict[str, Any] = {
|
|
8
|
+
"version": 1,
|
|
9
|
+
"disable_existing_loggers": False,
|
|
10
|
+
"formatters": {
|
|
11
|
+
"default": {
|
|
12
|
+
"()": "uvicorn.logging.DefaultFormatter",
|
|
13
|
+
"fmt": "[uvicorn] %(levelname)s - %(message)s",
|
|
14
|
+
"use_colors": None,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
"handlers": {
|
|
18
|
+
"default": {
|
|
19
|
+
"formatter": "default",
|
|
20
|
+
"class": "logging.StreamHandler",
|
|
21
|
+
"stream": "ext://sys.stderr",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
"loggers": {
|
|
25
|
+
"uvicorn": {
|
|
26
|
+
"handlers": ["default"],
|
|
27
|
+
"level": "INFO",
|
|
28
|
+
"propagate": False,
|
|
29
|
+
},
|
|
30
|
+
"uvicorn.error": {"level": "INFO"},
|
|
31
|
+
},
|
|
32
|
+
}
|