mcp-server-motherduck 0.7.0__tar.gz → 0.8.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_server_motherduck-0.8.1/.bumpver.toml +19 -0
- mcp_server_motherduck-0.8.1/.github/workflows/bump-version.yml +87 -0
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/.github/workflows/publish-mcp.yml +2 -1
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/.gitignore +2 -1
- mcp_server_motherduck-0.7.0/README.md → mcp_server_motherduck-0.8.1/PKG-INFO +59 -7
- mcp_server_motherduck-0.7.0/PKG-INFO → mcp_server_motherduck-0.8.1/README.md +42 -24
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/pyproject.toml +4 -4
- mcp_server_motherduck-0.8.1/server.json +144 -0
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/src/mcp_server_motherduck/__init__.py +52 -5
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/src/mcp_server_motherduck/configs.py +1 -1
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/src/mcp_server_motherduck/database.py +72 -13
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/src/mcp_server_motherduck/server.py +6 -0
- mcp_server_motherduck-0.8.1/uv.lock +858 -0
- mcp_server_motherduck-0.7.0/server.json +0 -91
- mcp_server_motherduck-0.7.0/uv.lock +0 -429
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/LICENSE +0 -0
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/makefile +0 -0
- {mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/src/mcp_server_motherduck/prompt.py +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[bumpver]
|
|
2
|
+
current_version = "0.7.0"
|
|
3
|
+
version_pattern = "MAJOR.MINOR.PATCH"
|
|
4
|
+
commit_message = "Bump version {old_version} -> {new_version}"
|
|
5
|
+
commit = true
|
|
6
|
+
tag = true
|
|
7
|
+
push = false
|
|
8
|
+
|
|
9
|
+
[bumpver.file_patterns]
|
|
10
|
+
"pyproject.toml" = [
|
|
11
|
+
'version = "{version}"',
|
|
12
|
+
]
|
|
13
|
+
"src/mcp_server_motherduck/configs.py" = [
|
|
14
|
+
'SERVER_VERSION = "{version}"',
|
|
15
|
+
]
|
|
16
|
+
"server.json" = [
|
|
17
|
+
'"version": "{version}"',
|
|
18
|
+
]
|
|
19
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Release New Version
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
version:
|
|
7
|
+
description: 'Version number (e.g., 0.8.0)'
|
|
8
|
+
required: true
|
|
9
|
+
type: string
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
bump-version:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions:
|
|
15
|
+
contents: write
|
|
16
|
+
outputs:
|
|
17
|
+
new_version: ${{ steps.set-version.outputs.version }}
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout code
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
with:
|
|
23
|
+
fetch-depth: 0
|
|
24
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
25
|
+
|
|
26
|
+
- name: Setup Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: '3.11'
|
|
30
|
+
|
|
31
|
+
- name: Install uv
|
|
32
|
+
uses: astral-sh/setup-uv@v5
|
|
33
|
+
with:
|
|
34
|
+
enable-cache: true
|
|
35
|
+
|
|
36
|
+
- name: Install bumpver
|
|
37
|
+
run: uv tool install bumpver
|
|
38
|
+
|
|
39
|
+
- name: Configure git
|
|
40
|
+
run: |
|
|
41
|
+
git config --global user.name "github-actions[bot]"
|
|
42
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
43
|
+
|
|
44
|
+
- name: Validate version input
|
|
45
|
+
run: |
|
|
46
|
+
version="${{ inputs.version }}"
|
|
47
|
+
if ! [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
48
|
+
echo "Version must be in MAJOR.MINOR.PATCH format (e.g., 0.8.0)."
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
git fetch --tags --force
|
|
52
|
+
if git rev-parse "v$version" >/dev/null 2>&1; then
|
|
53
|
+
echo "Tag v$version already exists."
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
- name: Update version to ${{ inputs.version }}
|
|
58
|
+
id: set-version
|
|
59
|
+
run: |
|
|
60
|
+
uv tool run bumpver update --set-version ${{ inputs.version }}
|
|
61
|
+
echo "NEW_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
|
|
62
|
+
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
|
|
63
|
+
|
|
64
|
+
- name: Push changes
|
|
65
|
+
run: |
|
|
66
|
+
git push origin "HEAD:${{ github.event.repository.default_branch }}"
|
|
67
|
+
git push origin --tags
|
|
68
|
+
|
|
69
|
+
# Publish job runs via workflow_call
|
|
70
|
+
call-publish:
|
|
71
|
+
needs: bump-version
|
|
72
|
+
uses: ./.github/workflows/publish-mcp.yml
|
|
73
|
+
secrets: inherit
|
|
74
|
+
|
|
75
|
+
create-release:
|
|
76
|
+
needs: [bump-version, call-publish]
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
permissions:
|
|
79
|
+
contents: write
|
|
80
|
+
steps:
|
|
81
|
+
- name: Create GitHub Release
|
|
82
|
+
env:
|
|
83
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
84
|
+
run: |
|
|
85
|
+
gh release create "v${{ needs.bump-version.outputs.new_version }}" \
|
|
86
|
+
--title "v${{ needs.bump-version.outputs.new_version }}" \
|
|
87
|
+
--generate-notes
|
{mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/.github/workflows/publish-mcp.yml
RENAMED
|
@@ -2,6 +2,7 @@ name: Publish to PyPI and MCP Registry
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_dispatch: # Allows manual triggering from GitHub Actions UI
|
|
5
|
+
workflow_call: # Allows triggering from other workflows
|
|
5
6
|
|
|
6
7
|
jobs:
|
|
7
8
|
publish:
|
|
@@ -71,4 +72,4 @@ jobs:
|
|
|
71
72
|
run: ./mcp-publisher login github-oidc
|
|
72
73
|
|
|
73
74
|
- name: Publish to MCP Registry
|
|
74
|
-
run: ./mcp-publisher publish
|
|
75
|
+
run: ./mcp-publisher publish
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# Custom
|
|
4
4
|
.python-version
|
|
5
|
+
.mcpregistry_*
|
|
5
6
|
|
|
6
7
|
# Byte-compiled / optimized / DLL files
|
|
7
8
|
__pycache__/
|
|
@@ -170,7 +171,7 @@ cython_debug/
|
|
|
170
171
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
171
172
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
172
173
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
173
|
-
|
|
174
|
+
.idea/
|
|
174
175
|
|
|
175
176
|
# Ruff stuff:
|
|
176
177
|
.ruff_cache/
|
|
@@ -1,6 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-server-motherduck
|
|
3
|
+
Version: 0.8.1
|
|
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.4.3
|
|
11
|
+
Requires-Dist: mcp>=1.23.0
|
|
12
|
+
Requires-Dist: pytz>=2025.2
|
|
13
|
+
Requires-Dist: starlette>=0.49.1
|
|
14
|
+
Requires-Dist: tabulate>=0.9.0
|
|
15
|
+
Requires-Dist: uvicorn>=0.34.0
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
1
18
|
# MotherDuck's DuckDB MCP Server
|
|
2
19
|
|
|
3
|
-
|
|
20
|
+
## What is the main difference between MotherDuck's remote MCP and this local MCP?
|
|
21
|
+
|
|
22
|
+
If you are choosing between MotherDuck's hosted/remote MCP and this local MCP:
|
|
23
|
+
|
|
24
|
+
- **Remote MCP (hosted by MotherDuck)**: read-only, zero-setup, and the recommended default for most users who just need query access. See the [MotherDuck MCP docs](https://motherduck.com/docs/sql-reference/mcp/).
|
|
25
|
+
- **Local MCP (this repo)**: self-hosted and supports write access when you need to create, modify, or manage data in a controlled environment.
|
|
26
|
+
|
|
27
|
+
A local MCP server implementation that interacts with DuckDB and MotherDuck databases, providing SQL analytics capabilities to AI Assistants and IDEs.
|
|
28
|
+
|
|
29
|
+
> This repository contains the **self-hosted/local** MCP server implementation.
|
|
4
30
|
|
|
5
31
|
[<img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Install in Cursor">](https://cursor.com/en/install-mcp?name=DuckDB&config=eyJjb21tYW5kIjoidXZ4IG1jcC1zZXJ2ZXItbW90aGVyZHVjayAtLWRiLXBhdGggOm1lbW9yeToiLCJlbnYiOnsibW90aGVyZHVja190b2tlbiI6IiJ9fQ%3D%3D)
|
|
6
32
|
|
|
@@ -34,6 +60,11 @@ The server offers one tool:
|
|
|
34
60
|
|
|
35
61
|
All interactions with both DuckDB and MotherDuck are done through writing SQL queries.
|
|
36
62
|
|
|
63
|
+
**Result Limiting**: Query results are automatically limited to prevent using up too much context:
|
|
64
|
+
- Maximum 1024 rows by default (configurable with `--max-rows`)
|
|
65
|
+
- Maximum 50,000 characters by default (configurable with `--max-chars`)
|
|
66
|
+
- Truncated responses include a note about truncation
|
|
67
|
+
|
|
37
68
|
## Command Line Parameters
|
|
38
69
|
|
|
39
70
|
The MCP server supports the following parameters:
|
|
@@ -42,27 +73,37 @@ The MCP server supports the following parameters:
|
|
|
42
73
|
|-----------|------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
43
74
|
| `--transport` | Choice | `stdio` | Transport type. Options: `stdio`, `sse`, `stream` |
|
|
44
75
|
| `--port` | Integer | `8000` | Port to listen on for sse and stream transport mode |
|
|
76
|
+
| `--host` | String | `127.0.0.1` | Host to bind the MCP server for sse and stream transport mode |
|
|
45
77
|
| `--db-path` | String | `md:` | Path to local DuckDB database file, MotherDuck database, or S3 URL (e.g., `s3://bucket/path/to/db.duckdb`) |
|
|
46
78
|
| `--motherduck-token` | String | `None` | Access token to use for MotherDuck database connections (uses `motherduck_token` env var by default) |
|
|
47
79
|
| `--read-only` | Flag | `False` | Flag for connecting to DuckDB or MotherDuck in read-only mode. For DuckDB it uses short-lived connections to enable concurrent access |
|
|
48
80
|
| `--home-dir` | String | `None` | Home directory for DuckDB (uses `HOME` env var by default) |
|
|
49
81
|
| `--saas-mode` | Flag | `False` | Flag for connecting to MotherDuck in [SaaS mode](https://motherduck.com/docs/key-tasks/authenticating-and-connecting-to-motherduck/authenticating-to-motherduck/#authentication-using-saas-mode). (disables filesystem and write permissions for local DuckDB) |
|
|
50
82
|
| `--json-response` | Flag | `False` | Enable JSON responses for HTTP stream. Only supported for `stream` transport |
|
|
83
|
+
| `--max-rows` | Integer | `1024` | Maximum number of rows to return from queries. |
|
|
84
|
+
| `--max-chars` | Integer | `50000` | Maximum number of characters in query results. |
|
|
85
|
+
| `--query-timeout` | Integer | `-1` | Query execution timeout in seconds. Set to -1 to disable timeout (default). |
|
|
51
86
|
|
|
52
87
|
### Quick Usage Examples
|
|
53
88
|
|
|
54
89
|
```bash
|
|
55
|
-
# Connect to local DuckDB file in read-only mode
|
|
56
|
-
uvx mcp-server-motherduck --
|
|
90
|
+
# Connect to local DuckDB file in read-only mode
|
|
91
|
+
uvx mcp-server-motherduck --db-path /path/to/local.db --read-only
|
|
57
92
|
|
|
58
|
-
# Connect to MotherDuck with token
|
|
59
|
-
uvx mcp-server-motherduck --
|
|
93
|
+
# Connect to MotherDuck with token
|
|
94
|
+
uvx mcp-server-motherduck --db-path md: --motherduck-token YOUR_TOKEN
|
|
60
95
|
|
|
61
|
-
# Connect to local DuckDB file in read-only mode
|
|
62
|
-
uvx mcp-server-motherduck --
|
|
96
|
+
# Connect to local DuckDB file in read-only mode
|
|
97
|
+
uvx mcp-server-motherduck --db-path /path/to/local.db --read-only
|
|
63
98
|
|
|
64
99
|
# Connect to MotherDuck in SaaS mode for enhanced security with stream transport mode
|
|
65
100
|
uvx mcp-server-motherduck --transport stream --db-path md: --motherduck-token YOUR_TOKEN --saas-mode
|
|
101
|
+
|
|
102
|
+
# Customize result truncation limits
|
|
103
|
+
uvx mcp-server-motherduck --db-path md: --motherduck-token YOUR_TOKEN --max-rows 2048 --max-chars 100000
|
|
104
|
+
|
|
105
|
+
# Enable query timeout (5 minutes)
|
|
106
|
+
uvx mcp-server-motherduck --db-path md: --motherduck-token YOUR_TOKEN --query-timeout 300
|
|
66
107
|
```
|
|
67
108
|
|
|
68
109
|
## Getting Started
|
|
@@ -364,6 +405,7 @@ You can connect to DuckDB databases stored on Amazon S3 by providing an S3 URL a
|
|
|
364
405
|
|
|
365
406
|
**Note**: For S3 connections:
|
|
366
407
|
- AWS credentials must be provided via environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and optionally `AWS_DEFAULT_REGION`)
|
|
408
|
+
- For temporary credentials (AWS SSO), set the `AWS_SESSION_TOKEN` environment variable (and optionally `AWS_DEFAULT_REGION`) to automatically use DuckDB's `credential_chain` provider.
|
|
367
409
|
- The S3 database is attached to an in-memory DuckDB instance
|
|
368
410
|
- The httpfs extension is automatically installed and configured for S3 access
|
|
369
411
|
- Both read and write operations are supported
|
|
@@ -425,6 +467,16 @@ To run the server from a local development environment, use the following config
|
|
|
425
467
|
}
|
|
426
468
|
```
|
|
427
469
|
|
|
470
|
+
## Release process
|
|
471
|
+
|
|
472
|
+
This repo uses a GitHub Actions workflow to bump, tag, publish, and create a release.
|
|
473
|
+
|
|
474
|
+
1. Use the Github action `Release New Version`
|
|
475
|
+
2. Enter the new version in `MAJOR.MINOR.PATCH` format (for example: `0.8.1`)
|
|
476
|
+
3. Run the workflow
|
|
477
|
+
|
|
478
|
+
The workflow will update all versioned files, commit and tag `vX.Y.Z`, publish to PyPI and the MCP registry, then create the GitHub release.
|
|
479
|
+
|
|
428
480
|
## Troubleshooting
|
|
429
481
|
|
|
430
482
|
- If you encounter connection issues, verify your MotherDuck token is correct
|
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: mcp-server-motherduck
|
|
3
|
-
Version: 0.7.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.1
|
|
11
|
-
Requires-Dist: mcp>=1.9.4
|
|
12
|
-
Requires-Dist: pytz>=2025.2
|
|
13
|
-
Requires-Dist: starlette>=0.46.1
|
|
14
|
-
Requires-Dist: tabulate>=0.9.0
|
|
15
|
-
Requires-Dist: uvicorn>=0.34.0
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
|
|
18
1
|
# MotherDuck's DuckDB MCP Server
|
|
19
2
|
|
|
20
|
-
|
|
3
|
+
## What is the main difference between MotherDuck's remote MCP and this local MCP?
|
|
4
|
+
|
|
5
|
+
If you are choosing between MotherDuck's hosted/remote MCP and this local MCP:
|
|
6
|
+
|
|
7
|
+
- **Remote MCP (hosted by MotherDuck)**: read-only, zero-setup, and the recommended default for most users who just need query access. See the [MotherDuck MCP docs](https://motherduck.com/docs/sql-reference/mcp/).
|
|
8
|
+
- **Local MCP (this repo)**: self-hosted and supports write access when you need to create, modify, or manage data in a controlled environment.
|
|
9
|
+
|
|
10
|
+
A local MCP server implementation that interacts with DuckDB and MotherDuck databases, providing SQL analytics capabilities to AI Assistants and IDEs.
|
|
11
|
+
|
|
12
|
+
> This repository contains the **self-hosted/local** MCP server implementation.
|
|
21
13
|
|
|
22
14
|
[<img src="https://cursor.com/deeplink/mcp-install-dark.svg" alt="Install in Cursor">](https://cursor.com/en/install-mcp?name=DuckDB&config=eyJjb21tYW5kIjoidXZ4IG1jcC1zZXJ2ZXItbW90aGVyZHVjayAtLWRiLXBhdGggOm1lbW9yeToiLCJlbnYiOnsibW90aGVyZHVja190b2tlbiI6IiJ9fQ%3D%3D)
|
|
23
15
|
|
|
@@ -51,6 +43,11 @@ The server offers one tool:
|
|
|
51
43
|
|
|
52
44
|
All interactions with both DuckDB and MotherDuck are done through writing SQL queries.
|
|
53
45
|
|
|
46
|
+
**Result Limiting**: Query results are automatically limited to prevent using up too much context:
|
|
47
|
+
- Maximum 1024 rows by default (configurable with `--max-rows`)
|
|
48
|
+
- Maximum 50,000 characters by default (configurable with `--max-chars`)
|
|
49
|
+
- Truncated responses include a note about truncation
|
|
50
|
+
|
|
54
51
|
## Command Line Parameters
|
|
55
52
|
|
|
56
53
|
The MCP server supports the following parameters:
|
|
@@ -59,27 +56,37 @@ The MCP server supports the following parameters:
|
|
|
59
56
|
|-----------|------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
60
57
|
| `--transport` | Choice | `stdio` | Transport type. Options: `stdio`, `sse`, `stream` |
|
|
61
58
|
| `--port` | Integer | `8000` | Port to listen on for sse and stream transport mode |
|
|
59
|
+
| `--host` | String | `127.0.0.1` | Host to bind the MCP server for sse and stream transport mode |
|
|
62
60
|
| `--db-path` | String | `md:` | Path to local DuckDB database file, MotherDuck database, or S3 URL (e.g., `s3://bucket/path/to/db.duckdb`) |
|
|
63
61
|
| `--motherduck-token` | String | `None` | Access token to use for MotherDuck database connections (uses `motherduck_token` env var by default) |
|
|
64
62
|
| `--read-only` | Flag | `False` | Flag for connecting to DuckDB or MotherDuck in read-only mode. For DuckDB it uses short-lived connections to enable concurrent access |
|
|
65
63
|
| `--home-dir` | String | `None` | Home directory for DuckDB (uses `HOME` env var by default) |
|
|
66
64
|
| `--saas-mode` | Flag | `False` | Flag for connecting to MotherDuck in [SaaS mode](https://motherduck.com/docs/key-tasks/authenticating-and-connecting-to-motherduck/authenticating-to-motherduck/#authentication-using-saas-mode). (disables filesystem and write permissions for local DuckDB) |
|
|
67
65
|
| `--json-response` | Flag | `False` | Enable JSON responses for HTTP stream. Only supported for `stream` transport |
|
|
66
|
+
| `--max-rows` | Integer | `1024` | Maximum number of rows to return from queries. |
|
|
67
|
+
| `--max-chars` | Integer | `50000` | Maximum number of characters in query results. |
|
|
68
|
+
| `--query-timeout` | Integer | `-1` | Query execution timeout in seconds. Set to -1 to disable timeout (default). |
|
|
68
69
|
|
|
69
70
|
### Quick Usage Examples
|
|
70
71
|
|
|
71
72
|
```bash
|
|
72
|
-
# Connect to local DuckDB file in read-only mode
|
|
73
|
-
uvx mcp-server-motherduck --
|
|
73
|
+
# Connect to local DuckDB file in read-only mode
|
|
74
|
+
uvx mcp-server-motherduck --db-path /path/to/local.db --read-only
|
|
74
75
|
|
|
75
|
-
# Connect to MotherDuck with token
|
|
76
|
-
uvx mcp-server-motherduck --
|
|
76
|
+
# Connect to MotherDuck with token
|
|
77
|
+
uvx mcp-server-motherduck --db-path md: --motherduck-token YOUR_TOKEN
|
|
77
78
|
|
|
78
|
-
# Connect to local DuckDB file in read-only mode
|
|
79
|
-
uvx mcp-server-motherduck --
|
|
79
|
+
# Connect to local DuckDB file in read-only mode
|
|
80
|
+
uvx mcp-server-motherduck --db-path /path/to/local.db --read-only
|
|
80
81
|
|
|
81
82
|
# Connect to MotherDuck in SaaS mode for enhanced security with stream transport mode
|
|
82
83
|
uvx mcp-server-motherduck --transport stream --db-path md: --motherduck-token YOUR_TOKEN --saas-mode
|
|
84
|
+
|
|
85
|
+
# Customize result truncation limits
|
|
86
|
+
uvx mcp-server-motherduck --db-path md: --motherduck-token YOUR_TOKEN --max-rows 2048 --max-chars 100000
|
|
87
|
+
|
|
88
|
+
# Enable query timeout (5 minutes)
|
|
89
|
+
uvx mcp-server-motherduck --db-path md: --motherduck-token YOUR_TOKEN --query-timeout 300
|
|
83
90
|
```
|
|
84
91
|
|
|
85
92
|
## Getting Started
|
|
@@ -381,6 +388,7 @@ You can connect to DuckDB databases stored on Amazon S3 by providing an S3 URL a
|
|
|
381
388
|
|
|
382
389
|
**Note**: For S3 connections:
|
|
383
390
|
- AWS credentials must be provided via environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and optionally `AWS_DEFAULT_REGION`)
|
|
391
|
+
- For temporary credentials (AWS SSO), set the `AWS_SESSION_TOKEN` environment variable (and optionally `AWS_DEFAULT_REGION`) to automatically use DuckDB's `credential_chain` provider.
|
|
384
392
|
- The S3 database is attached to an in-memory DuckDB instance
|
|
385
393
|
- The httpfs extension is automatically installed and configured for S3 access
|
|
386
394
|
- Both read and write operations are supported
|
|
@@ -442,6 +450,16 @@ To run the server from a local development environment, use the following config
|
|
|
442
450
|
}
|
|
443
451
|
```
|
|
444
452
|
|
|
453
|
+
## Release process
|
|
454
|
+
|
|
455
|
+
This repo uses a GitHub Actions workflow to bump, tag, publish, and create a release.
|
|
456
|
+
|
|
457
|
+
1. Use the Github action `Release New Version`
|
|
458
|
+
2. Enter the new version in `MAJOR.MINOR.PATCH` format (for example: `0.8.1`)
|
|
459
|
+
3. Run the workflow
|
|
460
|
+
|
|
461
|
+
The workflow will update all versioned files, commit and tag `vX.Y.Z`, publish to PyPI and the MCP registry, then create the GitHub release.
|
|
462
|
+
|
|
445
463
|
## Troubleshooting
|
|
446
464
|
|
|
447
465
|
- If you encounter connection issues, verify your MotherDuck token is correct
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mcp-server-motherduck"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.8.1"
|
|
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
|
-
"duckdb==1.3
|
|
8
|
+
"duckdb==1.4.3",
|
|
9
9
|
"tabulate>=0.9.0",
|
|
10
10
|
"click>=8.1.8",
|
|
11
|
-
"starlette>=0.
|
|
11
|
+
"starlette>=0.49.1",
|
|
12
12
|
"uvicorn>=0.34.0",
|
|
13
13
|
"anyio>=4.8.0",
|
|
14
|
-
"mcp>=1.
|
|
14
|
+
"mcp>=1.23.0",
|
|
15
15
|
"pytz>=2025.2"
|
|
16
16
|
]
|
|
17
17
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json",
|
|
3
|
+
"name": "io.github.motherduckdb/mcp-server-motherduck",
|
|
4
|
+
"description": "Fast analytics and data processing with DuckDB and MotherDuck",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/motherduckdb/mcp-server-motherduck",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "0.8.1",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "pypi",
|
|
13
|
+
"registryBaseUrl": "https://pypi.org",
|
|
14
|
+
"identifier": "mcp-server-motherduck",
|
|
15
|
+
"version": "0.8.1",
|
|
16
|
+
"transport": {
|
|
17
|
+
"type": "stdio"
|
|
18
|
+
},
|
|
19
|
+
"packageArguments": [
|
|
20
|
+
{
|
|
21
|
+
"type": "named",
|
|
22
|
+
"name": "--transport",
|
|
23
|
+
"description": "Transport type for MCP server",
|
|
24
|
+
"default": "stdio",
|
|
25
|
+
"choices": ["stdio", "sse", "stream"],
|
|
26
|
+
"isRequired": false
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "named",
|
|
30
|
+
"name": "--port",
|
|
31
|
+
"description": "Port to listen on for sse and stream transport mode",
|
|
32
|
+
"default": "8000",
|
|
33
|
+
"format": "number",
|
|
34
|
+
"isRequired": false
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"type": "named",
|
|
38
|
+
"name": "--host",
|
|
39
|
+
"description": "Host to bind the MCP server for sse and stream transport mode",
|
|
40
|
+
"default": "127.0.0.1",
|
|
41
|
+
"isRequired": false
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"type": "named",
|
|
45
|
+
"name": "--db-path",
|
|
46
|
+
"description": "Path to local DuckDB database file or MotherDuck database",
|
|
47
|
+
"default": "md:",
|
|
48
|
+
"isRequired": false
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"type": "named",
|
|
52
|
+
"name": "--motherduck-token",
|
|
53
|
+
"description": "Access token to use for MotherDuck database connections",
|
|
54
|
+
"isRequired": false,
|
|
55
|
+
"isSecret": true
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"type": "named",
|
|
59
|
+
"name": "--read-only",
|
|
60
|
+
"description": "Flag for connecting to DuckDB or MotherDuck in read-only mode",
|
|
61
|
+
"isRequired": false
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"type": "named",
|
|
65
|
+
"name": "--home-dir",
|
|
66
|
+
"description": "Home directory for DuckDB",
|
|
67
|
+
"isRequired": false
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"type": "named",
|
|
71
|
+
"name": "--saas-mode",
|
|
72
|
+
"description": "Flag for connecting to MotherDuck in SaaS mode (disables filesystem and write permissions for local DuckDB)",
|
|
73
|
+
"isRequired": false
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"type": "named",
|
|
77
|
+
"name": "--json-response",
|
|
78
|
+
"description": "Enable JSON responses for HTTP stream (only supported for stream transport)",
|
|
79
|
+
"isRequired": false
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"type": "named",
|
|
83
|
+
"name": "--max-rows",
|
|
84
|
+
"description": "Maximum number of rows to return from queries",
|
|
85
|
+
"default": "1024",
|
|
86
|
+
"format": "number",
|
|
87
|
+
"isRequired": false
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"type": "named",
|
|
91
|
+
"name": "--max-chars",
|
|
92
|
+
"description": "Maximum number of characters in query results",
|
|
93
|
+
"default": "50000",
|
|
94
|
+
"format": "number",
|
|
95
|
+
"isRequired": false
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"type": "named",
|
|
99
|
+
"name": "--query-timeout",
|
|
100
|
+
"description": "Query execution timeout in seconds. Set to -1 to disable timeout",
|
|
101
|
+
"default": "-1",
|
|
102
|
+
"format": "number",
|
|
103
|
+
"isRequired": false
|
|
104
|
+
}
|
|
105
|
+
],
|
|
106
|
+
"environmentVariables": [
|
|
107
|
+
{
|
|
108
|
+
"name": "motherduck_token",
|
|
109
|
+
"description": "Access token to use for MotherDuck database connections",
|
|
110
|
+
"isRequired": false,
|
|
111
|
+
"isSecret": true
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"name": "HOME",
|
|
115
|
+
"description": "Home directory for DuckDB (used as default if --home-dir not specified)",
|
|
116
|
+
"isRequired": false
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"name": "AWS_ACCESS_KEY_ID",
|
|
120
|
+
"description": "AWS access key for S3 database connections",
|
|
121
|
+
"isRequired": false,
|
|
122
|
+
"isSecret": true
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "AWS_SECRET_ACCESS_KEY",
|
|
126
|
+
"description": "AWS secret access key for S3 database connections",
|
|
127
|
+
"isRequired": false,
|
|
128
|
+
"isSecret": true
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"name": "AWS_SESSION_TOKEN",
|
|
132
|
+
"description": "AWS session token for temporary credentials (IAM roles, AWS SSO, EC2 instance profiles)",
|
|
133
|
+
"isRequired": false,
|
|
134
|
+
"isSecret": true
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"name": "AWS_DEFAULT_REGION",
|
|
138
|
+
"description": "AWS region for S3 database connections",
|
|
139
|
+
"isRequired": false
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
{mcp_server_motherduck-0.7.0 → mcp_server_motherduck-0.8.1}/src/mcp_server_motherduck/__init__.py
RENAMED
|
@@ -14,6 +14,7 @@ logging.basicConfig(
|
|
|
14
14
|
|
|
15
15
|
@click.command()
|
|
16
16
|
@click.option("--port", default=8000, help="Port to listen on for SSE")
|
|
17
|
+
@click.option("--host", default=SERVER_LOCALHOST, help="Host to bind the MCP server")
|
|
17
18
|
@click.option(
|
|
18
19
|
"--transport",
|
|
19
20
|
type=click.Choice(["stdio", "sse", "stream"]),
|
|
@@ -51,8 +52,27 @@ logging.basicConfig(
|
|
|
51
52
|
default=False,
|
|
52
53
|
help="(Default: `False`) Enable JSON responses instead of SSE streams. Only supported for `stream` transport.",
|
|
53
54
|
)
|
|
55
|
+
@click.option(
|
|
56
|
+
"--max-rows",
|
|
57
|
+
type=int,
|
|
58
|
+
default=1024,
|
|
59
|
+
help="(Default: `1024`) Maximum number of rows to return from queries. Use LIMIT in your SQL for specific row counts.",
|
|
60
|
+
)
|
|
61
|
+
@click.option(
|
|
62
|
+
"--max-chars",
|
|
63
|
+
type=int,
|
|
64
|
+
default=50000,
|
|
65
|
+
help="(Default: `50000`) Maximum number of characters in query results. Prevents issues with wide rows or large text columns.",
|
|
66
|
+
)
|
|
67
|
+
@click.option(
|
|
68
|
+
"--query-timeout",
|
|
69
|
+
type=int,
|
|
70
|
+
default=-1,
|
|
71
|
+
help="(Default: `-1`) Query execution timeout in seconds. Set to -1 to disable timeout.",
|
|
72
|
+
)
|
|
54
73
|
def main(
|
|
55
74
|
port,
|
|
75
|
+
host,
|
|
56
76
|
transport,
|
|
57
77
|
db_path,
|
|
58
78
|
motherduck_token,
|
|
@@ -60,11 +80,19 @@ def main(
|
|
|
60
80
|
saas_mode,
|
|
61
81
|
read_only,
|
|
62
82
|
json_response,
|
|
83
|
+
max_rows,
|
|
84
|
+
max_chars,
|
|
85
|
+
query_timeout,
|
|
63
86
|
):
|
|
64
87
|
"""Main entry point for the package."""
|
|
65
88
|
|
|
66
89
|
logger.info("🦆 MotherDuck MCP Server v" + SERVER_VERSION)
|
|
67
90
|
logger.info("Ready to execute SQL queries via DuckDB/MotherDuck")
|
|
91
|
+
logger.info(f"Query result limits: {max_rows} rows, {max_chars:,} characters")
|
|
92
|
+
if query_timeout == -1:
|
|
93
|
+
logger.info("Query timeout: disabled")
|
|
94
|
+
else:
|
|
95
|
+
logger.info(f"Query timeout: {query_timeout}s")
|
|
68
96
|
|
|
69
97
|
app, init_opts = build_application(
|
|
70
98
|
db_path=db_path,
|
|
@@ -72,6 +100,9 @@ def main(
|
|
|
72
100
|
home_dir=home_dir,
|
|
73
101
|
saas_mode=saas_mode,
|
|
74
102
|
read_only=read_only,
|
|
103
|
+
max_rows=max_rows,
|
|
104
|
+
max_chars=max_chars,
|
|
105
|
+
query_timeout=query_timeout,
|
|
75
106
|
)
|
|
76
107
|
|
|
77
108
|
if transport == "sse":
|
|
@@ -92,7 +123,7 @@ def main(
|
|
|
92
123
|
return Response()
|
|
93
124
|
|
|
94
125
|
logger.info(
|
|
95
|
-
f"🦆 Connect to MotherDuck MCP Server at \033[1m\033[36mhttp://{
|
|
126
|
+
f"🦆 Connect to MotherDuck MCP Server at \033[1m\033[36mhttp://{host}:{port}/sse\033[0m"
|
|
96
127
|
)
|
|
97
128
|
|
|
98
129
|
starlette_app = Starlette(
|
|
@@ -107,7 +138,7 @@ def main(
|
|
|
107
138
|
|
|
108
139
|
uvicorn.run(
|
|
109
140
|
starlette_app,
|
|
110
|
-
host=
|
|
141
|
+
host=host,
|
|
111
142
|
port=port,
|
|
112
143
|
log_config=UVICORN_LOGGING_CONFIG,
|
|
113
144
|
)
|
|
@@ -148,7 +179,7 @@ def main(
|
|
|
148
179
|
)
|
|
149
180
|
|
|
150
181
|
logger.info(
|
|
151
|
-
f"🦆 Connect to MotherDuck MCP Server at \033[1m\033[36mhttp://{
|
|
182
|
+
f"🦆 Connect to MotherDuck MCP Server at \033[1m\033[36mhttp://{host}:{port}/mcp\033[0m"
|
|
152
183
|
)
|
|
153
184
|
|
|
154
185
|
# Create an ASGI application using the transport
|
|
@@ -164,7 +195,7 @@ def main(
|
|
|
164
195
|
|
|
165
196
|
uvicorn.run(
|
|
166
197
|
starlette_app,
|
|
167
|
-
host=
|
|
198
|
+
host=host,
|
|
168
199
|
port=port,
|
|
169
200
|
log_config=UVICORN_LOGGING_CONFIG,
|
|
170
201
|
)
|
|
@@ -179,7 +210,23 @@ def main(
|
|
|
179
210
|
async with stdio_server() as (read_stream, write_stream):
|
|
180
211
|
await app.run(read_stream, write_stream, init_opts)
|
|
181
212
|
|
|
182
|
-
|
|
213
|
+
try:
|
|
214
|
+
anyio.run(arun)
|
|
215
|
+
except (BrokenPipeError, ConnectionResetError, anyio.BrokenResourceError):
|
|
216
|
+
logger.info("Client disconnected")
|
|
217
|
+
except KeyboardInterrupt:
|
|
218
|
+
logger.info("Server interrupted by user")
|
|
219
|
+
except BaseException as e:
|
|
220
|
+
# Handle exception groups from anyio (Python 3.11+)
|
|
221
|
+
if type(e).__name__ == 'ExceptionGroup':
|
|
222
|
+
if any(isinstance(exc, (BrokenPipeError, ConnectionResetError, anyio.BrokenResourceError))
|
|
223
|
+
for exc in getattr(e, 'exceptions', [])):
|
|
224
|
+
logger.info("Client disconnected")
|
|
225
|
+
else:
|
|
226
|
+
raise
|
|
227
|
+
else:
|
|
228
|
+
raise
|
|
229
|
+
|
|
183
230
|
# This will only be reached when the server is shutting down
|
|
184
231
|
logger.info(
|
|
185
232
|
"🦆 MotherDuck MCP Server in \033[32mstdio\033[0m mode shutting down"
|