mcp-server-motherduck 0.3.4__py3-none-any.whl → 0.4.0__py3-none-any.whl
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/__init__.py +21 -3
- mcp_server_motherduck/server.py +44 -33
- {mcp_server_motherduck-0.3.4.dist-info → mcp_server_motherduck-0.4.0.dist-info}/METADATA +15 -29
- mcp_server_motherduck-0.4.0.dist-info/RECORD +8 -0
- mcp_server_motherduck-0.3.4.dist-info/RECORD +0 -8
- {mcp_server_motherduck-0.3.4.dist-info → mcp_server_motherduck-0.4.0.dist-info}/WHEEL +0 -0
- {mcp_server_motherduck-0.3.4.dist-info → mcp_server_motherduck-0.4.0.dist-info}/entry_points.txt +0 -0
- {mcp_server_motherduck-0.3.4.dist-info → mcp_server_motherduck-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -13,12 +13,23 @@ def main():
|
|
|
13
13
|
parser = argparse.ArgumentParser(description="MotherDuck MCP Server")
|
|
14
14
|
parser.add_argument(
|
|
15
15
|
"--db-path",
|
|
16
|
-
|
|
16
|
+
default="md:",
|
|
17
|
+
help="(Default: `md:`) Path to local DuckDB database file or MotherDuck database",
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"--motherduck-token",
|
|
21
|
+
default=None,
|
|
22
|
+
help="(Default: env var `motherduck_token`) Access token to use for MotherDuck database connections",
|
|
23
|
+
)
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
"--home-dir",
|
|
26
|
+
default=None,
|
|
27
|
+
help="(Default: env var `HOME`) Home directory for DuckDB",
|
|
17
28
|
)
|
|
18
29
|
# This is experimental and will change in the future
|
|
19
30
|
parser.add_argument(
|
|
20
31
|
"--result-format",
|
|
21
|
-
help="Format of the
|
|
32
|
+
help="(Default: `markdown`) Format of the query result",
|
|
22
33
|
default="markdown",
|
|
23
34
|
choices=["markdown", "duckbox", "text"],
|
|
24
35
|
)
|
|
@@ -28,7 +39,14 @@ def main():
|
|
|
28
39
|
logger.info("Ready to execute SQL queries via DuckDB/MotherDuck")
|
|
29
40
|
logger.info("Waiting for client connection...\n")
|
|
30
41
|
|
|
31
|
-
asyncio.run(
|
|
42
|
+
asyncio.run(
|
|
43
|
+
server.main(
|
|
44
|
+
db_path=args.db_path,
|
|
45
|
+
motherduck_token=args.motherduck_token,
|
|
46
|
+
result_format=args.result_format,
|
|
47
|
+
home_dir=args.home_dir,
|
|
48
|
+
)
|
|
49
|
+
)
|
|
32
50
|
|
|
33
51
|
|
|
34
52
|
# Optionally expose other important items at package level
|
mcp_server_motherduck/server.py
CHANGED
|
@@ -12,7 +12,7 @@ from mcp.server.models import InitializationOptions
|
|
|
12
12
|
from .prompt import PROMPT_TEMPLATE
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
SERVER_VERSION = "0.
|
|
15
|
+
SERVER_VERSION = "0.4.0"
|
|
16
16
|
|
|
17
17
|
logger = logging.getLogger("mcp_server_motherduck")
|
|
18
18
|
|
|
@@ -21,18 +21,26 @@ class DatabaseClient:
|
|
|
21
21
|
def __init__(
|
|
22
22
|
self,
|
|
23
23
|
db_path: str | None = None,
|
|
24
|
+
motherduck_token: str | None = None,
|
|
24
25
|
result_format: Literal["markdown", "duckbox", "text"] = "markdown",
|
|
26
|
+
home_dir: str | None = None,
|
|
25
27
|
):
|
|
26
|
-
self.db_path, self.db_type = self._resolve_db_path_type(
|
|
28
|
+
self.db_path, self.db_type = self._resolve_db_path_type(
|
|
29
|
+
db_path, motherduck_token
|
|
30
|
+
)
|
|
27
31
|
logger.info(f"Database client initialized in `{self.db_type}` mode")
|
|
28
32
|
|
|
33
|
+
# Set the home directory for DuckDB
|
|
34
|
+
if home_dir:
|
|
35
|
+
os.environ["HOME"] = home_dir
|
|
36
|
+
|
|
29
37
|
self.conn = self._initialize_connection()
|
|
30
38
|
self.result_format = result_format
|
|
31
39
|
|
|
32
40
|
def _initialize_connection(self) -> duckdb.DuckDBPyConnection:
|
|
33
41
|
"""Initialize connection to the MotherDuck or DuckDB database"""
|
|
34
42
|
|
|
35
|
-
logger.info(f"🔌 Connecting to {self.db_type} database
|
|
43
|
+
logger.info(f"🔌 Connecting to {self.db_type} database")
|
|
36
44
|
|
|
37
45
|
conn = duckdb.connect(
|
|
38
46
|
self.db_path,
|
|
@@ -44,32 +52,36 @@ class DatabaseClient:
|
|
|
44
52
|
return conn
|
|
45
53
|
|
|
46
54
|
def _resolve_db_path_type(
|
|
47
|
-
self, db_path: str | None = None
|
|
55
|
+
self, db_path: str, motherduck_token: str | None = None
|
|
48
56
|
) -> tuple[str, Literal["duckdb", "motherduck"]]:
|
|
49
57
|
"""Resolve and validate the database path"""
|
|
50
|
-
# Use MotherDuck if token is available and no path specified
|
|
51
|
-
if db_path is None and os.getenv("motherduck_token"):
|
|
52
|
-
logger.info("Using MotherDuck token to connect to database `md:`")
|
|
53
|
-
return "md:", "motherduck"
|
|
54
|
-
|
|
55
58
|
# Handle MotherDuck paths
|
|
56
|
-
if db_path
|
|
57
|
-
if
|
|
59
|
+
if db_path.startswith("md:"):
|
|
60
|
+
if motherduck_token:
|
|
61
|
+
logger.info("Using MotherDuck token to connect to database `md:`")
|
|
62
|
+
return f"{db_path}?motherduck_token={motherduck_token}", "motherduck"
|
|
63
|
+
elif os.getenv("motherduck_token"):
|
|
64
|
+
logger.info(
|
|
65
|
+
"Using MotherDuck token from env to connect to database `md:`"
|
|
66
|
+
)
|
|
67
|
+
return (
|
|
68
|
+
f"{db_path}?motherduck_token={os.getenv('motherduck_token')}",
|
|
69
|
+
"motherduck",
|
|
70
|
+
)
|
|
71
|
+
else:
|
|
58
72
|
raise ValueError(
|
|
59
|
-
"Please set the `motherduck_token` environment variable when using `md:` as db_path."
|
|
73
|
+
"Please set the `motherduck_token` as an environment variable or pass it as an argument with `--motherduck-token` when using `md:` as db_path."
|
|
60
74
|
)
|
|
61
|
-
return db_path, "motherduck"
|
|
62
75
|
|
|
63
|
-
|
|
64
|
-
if db_path:
|
|
65
|
-
if not os.path.exists(db_path):
|
|
66
|
-
raise FileNotFoundError(
|
|
67
|
-
f"The database path `{db_path}` does not exist."
|
|
68
|
-
)
|
|
76
|
+
if db_path == ":memory:":
|
|
69
77
|
return db_path, "duckdb"
|
|
70
78
|
|
|
71
|
-
#
|
|
72
|
-
|
|
79
|
+
# Handle local database paths as the last check
|
|
80
|
+
if not os.path.exists(db_path):
|
|
81
|
+
raise FileNotFoundError(
|
|
82
|
+
f"The local database path `{db_path}` does not exist."
|
|
83
|
+
)
|
|
84
|
+
return db_path, "duckdb"
|
|
73
85
|
|
|
74
86
|
def query(self, query: str) -> str:
|
|
75
87
|
try:
|
|
@@ -94,21 +106,21 @@ class DatabaseClient:
|
|
|
94
106
|
except Exception as e:
|
|
95
107
|
raise ValueError(f"❌ Error executing query: {e}")
|
|
96
108
|
|
|
97
|
-
def mcp_config(self) -> dict[str, str]:
|
|
98
|
-
"""Used for debugging purposes to show the current MCP config"""
|
|
99
|
-
return {
|
|
100
|
-
"current_working_directory": os.getcwd(),
|
|
101
|
-
"database_type": self.db_type,
|
|
102
|
-
"database_path": self.db_path,
|
|
103
|
-
}
|
|
104
|
-
|
|
105
109
|
|
|
106
110
|
async def main(
|
|
107
|
-
db_path: str,
|
|
111
|
+
db_path: str,
|
|
112
|
+
motherduck_token: str | None = None,
|
|
113
|
+
result_format: Literal["markdown", "duckbox", "text"] = "markdown",
|
|
114
|
+
home_dir: str | None = None,
|
|
108
115
|
):
|
|
109
|
-
logger.info(
|
|
116
|
+
logger.info("Starting MotherDuck MCP Server")
|
|
110
117
|
server = Server("mcp-server-motherduck")
|
|
111
|
-
db_client = DatabaseClient(
|
|
118
|
+
db_client = DatabaseClient(
|
|
119
|
+
db_path=db_path,
|
|
120
|
+
result_format=result_format,
|
|
121
|
+
motherduck_token=motherduck_token,
|
|
122
|
+
home_dir=home_dir,
|
|
123
|
+
)
|
|
112
124
|
|
|
113
125
|
logger.info("Registering handlers")
|
|
114
126
|
|
|
@@ -235,4 +247,3 @@ async def main(
|
|
|
235
247
|
|
|
236
248
|
# This will only be reached when the server is shutting down
|
|
237
249
|
logger.info("\n🦆 MotherDuck MCP Server shutting down...")
|
|
238
|
-
logger.info(f"Database connection to {db_client.db_path} closed.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-server-motherduck
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A MCP server for MotherDuck and local DuckDB
|
|
5
5
|
Author-email: tdoehmen <till@motherduck.com>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -8,7 +8,6 @@ Requires-Python: >=3.10
|
|
|
8
8
|
Requires-Dist: duckdb>=1.2.1
|
|
9
9
|
Requires-Dist: mcp>=1.3.0
|
|
10
10
|
Requires-Dist: pandas>=2.0.0
|
|
11
|
-
Requires-Dist: pydantic-settings>=2.8.1
|
|
12
11
|
Requires-Dist: tabulate>=0.9.0
|
|
13
12
|
Description-Content-Type: text/markdown
|
|
14
13
|
|
|
@@ -72,12 +71,12 @@ If you plan to use MotherDuck MCP with Claude Desktop, you will also need Claude
|
|
|
72
71
|
"mcp-server-motherduck": {
|
|
73
72
|
"command": "uvx",
|
|
74
73
|
"args": [
|
|
75
|
-
"mcp-server-motherduck"
|
|
74
|
+
"mcp-server-motherduck",
|
|
75
|
+
"--db-path",
|
|
76
|
+
"md:",
|
|
77
|
+
"--motherduck-token",
|
|
78
|
+
"<YOUR_MOTHERDUCK_TOKEN_HERE>",
|
|
76
79
|
],
|
|
77
|
-
"env": {
|
|
78
|
-
"motherduck_token": "YOUR_MOTHERDUCK_TOKEN_HERE",
|
|
79
|
-
"HOME": "YOUR_HOME_FOLDER_PATH"
|
|
80
|
-
}
|
|
81
80
|
}
|
|
82
81
|
}
|
|
83
82
|
```
|
|
@@ -99,27 +98,20 @@ Once configured, you can ask Claude to run queries like:
|
|
|
99
98
|
|
|
100
99
|
## Testing
|
|
101
100
|
|
|
102
|
-
The server is designed to be run by tools like Claude Desktop, 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:
|
|
101
|
+
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:
|
|
103
102
|
|
|
104
103
|
1. **Default MotherDuck database**:
|
|
105
104
|
|
|
106
|
-
- To connect to the default MotherDuck database, you will need to
|
|
105
|
+
- To connect to the default MotherDuck database, you will need to pass the auth token using the `--motherduck-token` parameter.
|
|
107
106
|
|
|
108
107
|
```bash
|
|
109
|
-
|
|
110
|
-
uvx mcp-server-motherduck --db-path md:
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
- Alternatively, you can pass the token directly:
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
motherduck_token=<your_motherduck_token> uvx mcp-server-motherduck --db-path md:
|
|
108
|
+
uvx mcp-server-motherduck --db-path md: --motherduck-token <your_motherduck_token>
|
|
117
109
|
```
|
|
118
110
|
|
|
119
111
|
2. **Specific MotherDuck database**:
|
|
120
112
|
|
|
121
113
|
```bash
|
|
122
|
-
uvx mcp-server-motherduck --db-path md:your_database_name
|
|
114
|
+
uvx mcp-server-motherduck --db-path md:your_database_name --motherduck-token <your_motherduck_token>
|
|
123
115
|
```
|
|
124
116
|
|
|
125
117
|
3. **Local DuckDB database**:
|
|
@@ -128,10 +120,10 @@ The server is designed to be run by tools like Claude Desktop, but you can start
|
|
|
128
120
|
uvx mcp-server-motherduck --db-path /path/to/your/local.db
|
|
129
121
|
```
|
|
130
122
|
|
|
131
|
-
4. **In-memory database
|
|
123
|
+
4. **In-memory database**:
|
|
132
124
|
|
|
133
125
|
```bash
|
|
134
|
-
uvx mcp-server-motherduck
|
|
126
|
+
uvx mcp-server-motherduck --db-path :memory:
|
|
135
127
|
```
|
|
136
128
|
|
|
137
129
|
If you don't specify a database path but have set the `motherduck_token` environment variable, the server will automatically connect to the default MotherDuck database (`md:`).
|
|
@@ -141,23 +133,17 @@ If you don't specify a database path but have set the `motherduck_token` environ
|
|
|
141
133
|
The server could also be run ing SSE mode using `supergateway` by running the following command:
|
|
142
134
|
|
|
143
135
|
```bash
|
|
144
|
-
|
|
136
|
+
npx -y supergateway --stdio "uvx mcp-server-motherduck --db-path md: --motherduck-token <your_motherduck_token>"
|
|
145
137
|
```
|
|
146
138
|
|
|
147
139
|
And you can point your clients such as Claude Desktop, Cursor to this endpoint.
|
|
148
140
|
|
|
149
|
-
### Environment Variables
|
|
150
|
-
|
|
151
|
-
The server uses the following environment variables:
|
|
152
|
-
|
|
153
|
-
- `motherduck_token`: Your MotherDuck authentication token
|
|
154
|
-
- `HOME`: Directory used by DuckDB for file operations
|
|
155
|
-
|
|
156
141
|
## Troubleshooting
|
|
157
142
|
|
|
158
143
|
- If you encounter connection issues, verify your MotherDuck token is correct
|
|
159
|
-
- For local file access problems, ensure the `
|
|
144
|
+
- For local file access problems, ensure the `--home-dir` parameter is set correctly
|
|
160
145
|
- Check that the `uvx` command is available in your PATH
|
|
146
|
+
- In version previous for v0.4.0 we used environment variables, now we use parameters
|
|
161
147
|
|
|
162
148
|
## License
|
|
163
149
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
mcp_server_motherduck/__init__.py,sha256=_HXXeC7-S1nr4oaNEQKt_TKJrdrS_1iWRt557_pX6kI,1642
|
|
2
|
+
mcp_server_motherduck/prompt.py,sha256=P7BrmhVXwDkPeSHQ3f25WMP6lpBpN2BxDzYPOQ3fxX8,56699
|
|
3
|
+
mcp_server_motherduck/server.py,sha256=cS1bLPEnrf3RSHklcAoktGUTxctvhH5DMp2SZ3eA1AM,9085
|
|
4
|
+
mcp_server_motherduck-0.4.0.dist-info/METADATA,sha256=766vAQwLLRwi6fNVoZKI6JpJKhGfEf1oKZWUPfjAwy0,5325
|
|
5
|
+
mcp_server_motherduck-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
mcp_server_motherduck-0.4.0.dist-info/entry_points.txt,sha256=dRTgcvWJn40bz0PVuKPylK6w92cFN32lwunZOgo5j4s,69
|
|
7
|
+
mcp_server_motherduck-0.4.0.dist-info/licenses/LICENSE,sha256=Tj68w9jCiceFKTvZ3jET-008NjhozcQMXpm-fyL9WUI,1067
|
|
8
|
+
mcp_server_motherduck-0.4.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
mcp_server_motherduck/__init__.py,sha256=fvkO3-wgyUVAdIzy-As4pl3_LYHFMgO9STVTHAyTyKA,1078
|
|
2
|
-
mcp_server_motherduck/prompt.py,sha256=P7BrmhVXwDkPeSHQ3f25WMP6lpBpN2BxDzYPOQ3fxX8,56699
|
|
3
|
-
mcp_server_motherduck/server.py,sha256=N7ySKj43BtMRGE_9HGhWr8_3wGSsjxKD9xIJJv7W4Eg,8841
|
|
4
|
-
mcp_server_motherduck-0.3.4.dist-info/METADATA,sha256=jhdryCCXMVJdKKi0o7E6v9NiJBakx_d9HRECRn21mks,5623
|
|
5
|
-
mcp_server_motherduck-0.3.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
mcp_server_motherduck-0.3.4.dist-info/entry_points.txt,sha256=dRTgcvWJn40bz0PVuKPylK6w92cFN32lwunZOgo5j4s,69
|
|
7
|
-
mcp_server_motherduck-0.3.4.dist-info/licenses/LICENSE,sha256=Tj68w9jCiceFKTvZ3jET-008NjhozcQMXpm-fyL9WUI,1067
|
|
8
|
-
mcp_server_motherduck-0.3.4.dist-info/RECORD,,
|
|
File without changes
|
{mcp_server_motherduck-0.3.4.dist-info → mcp_server_motherduck-0.4.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{mcp_server_motherduck-0.3.4.dist-info → mcp_server_motherduck-0.4.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|