mcp-server-motherduck 0.1.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.
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from mcp.server.models import InitializationOptions
|
|
4
|
+
import mcp.types as types
|
|
5
|
+
from mcp.server import NotificationOptions, Server
|
|
6
|
+
from pydantic import AnyUrl
|
|
7
|
+
import mcp.server.stdio
|
|
8
|
+
import duckdb
|
|
9
|
+
|
|
10
|
+
# Store notes as a simple key-value dict to demonstrate state management
|
|
11
|
+
notes: dict[str, str] = {}
|
|
12
|
+
|
|
13
|
+
server = Server("mcp-server-motherduck")
|
|
14
|
+
|
|
15
|
+
conn = duckdb.connect('md:')
|
|
16
|
+
|
|
17
|
+
@server.list_resources()
|
|
18
|
+
async def handle_list_resources() -> list[types.Resource]:
|
|
19
|
+
"""
|
|
20
|
+
List available note resources.
|
|
21
|
+
Each note is exposed as a resource with a custom note:// URI scheme.
|
|
22
|
+
"""
|
|
23
|
+
return []
|
|
24
|
+
|
|
25
|
+
@server.read_resource()
|
|
26
|
+
async def handle_read_resource(uri: AnyUrl) -> str:
|
|
27
|
+
"""
|
|
28
|
+
Read a specific note's content by its URI.
|
|
29
|
+
The note name is extracted from the URI host component.
|
|
30
|
+
"""
|
|
31
|
+
raise ValueError(f"Unsupported URI scheme: {uri.scheme}")
|
|
32
|
+
|
|
33
|
+
@server.list_prompts()
|
|
34
|
+
async def handle_list_prompts() -> list[types.Prompt]:
|
|
35
|
+
"""
|
|
36
|
+
List available prompts.
|
|
37
|
+
Each prompt can have optional arguments to customize its behavior.
|
|
38
|
+
"""
|
|
39
|
+
return []
|
|
40
|
+
|
|
41
|
+
@server.get_prompt()
|
|
42
|
+
async def handle_get_prompt(
|
|
43
|
+
name: str, arguments: dict[str, str] | None
|
|
44
|
+
) -> types.GetPromptResult:
|
|
45
|
+
"""
|
|
46
|
+
Generate a prompt by combining arguments with server state.
|
|
47
|
+
The prompt includes all current notes and can be customized via arguments.
|
|
48
|
+
"""
|
|
49
|
+
return types.GetPromptResult(
|
|
50
|
+
description="None",
|
|
51
|
+
messages=[
|
|
52
|
+
types.PromptMessage(
|
|
53
|
+
role="user",
|
|
54
|
+
content=types.TextContent(
|
|
55
|
+
type="text",
|
|
56
|
+
text="none",
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
@server.list_tools()
|
|
63
|
+
async def handle_list_tools() -> list[types.Tool]:
|
|
64
|
+
"""
|
|
65
|
+
List available tools.
|
|
66
|
+
Each tool specifies its arguments using JSON Schema validation.
|
|
67
|
+
"""
|
|
68
|
+
return [
|
|
69
|
+
types.Tool(
|
|
70
|
+
name="read-query-motherduck",
|
|
71
|
+
description="Execute a SELECT query on the MotherDuck (DuckDB) database",
|
|
72
|
+
inputSchema={
|
|
73
|
+
"type": "object",
|
|
74
|
+
"properties": {
|
|
75
|
+
"query": {"type": "string", "description": "SELECT SQL query to execute"},
|
|
76
|
+
},
|
|
77
|
+
"required": ["query"],
|
|
78
|
+
},
|
|
79
|
+
)
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
@server.call_tool()
|
|
83
|
+
async def handle_call_tool(
|
|
84
|
+
name: str, arguments: dict | None
|
|
85
|
+
) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
|
|
86
|
+
"""
|
|
87
|
+
Handle tool execution requests.
|
|
88
|
+
Tools can modify server state and notify clients of changes.
|
|
89
|
+
"""
|
|
90
|
+
if name == "read-query-motherduck":
|
|
91
|
+
if not arguments["query"].strip().upper().startswith("SELECT"):
|
|
92
|
+
raise ValueError("Only SELECT queries are allowed for read-query")
|
|
93
|
+
results = conn.execute(arguments["query"]).fetchall()
|
|
94
|
+
return [types.TextContent(type="text", text=str(results))]
|
|
95
|
+
|
|
96
|
+
async def main():
|
|
97
|
+
# Run the server using stdin/stdout streams
|
|
98
|
+
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
|
|
99
|
+
await server.run(
|
|
100
|
+
read_stream,
|
|
101
|
+
write_stream,
|
|
102
|
+
InitializationOptions(
|
|
103
|
+
server_name="motherduck",
|
|
104
|
+
server_version="0.1.0",
|
|
105
|
+
capabilities=server.get_capabilities(
|
|
106
|
+
notification_options=NotificationOptions(),
|
|
107
|
+
experimental_capabilities={},
|
|
108
|
+
),
|
|
109
|
+
),
|
|
110
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: mcp-server-motherduck
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A MCP server for MotherDuck
|
|
5
|
+
Author-email: tdoehmen <till@motherduck.com>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: duckdb>=1.1.3
|
|
8
|
+
Requires-Dist: mcp>=1.0.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# mcp-server-motherduck MCP server
|
|
12
|
+
|
|
13
|
+
A MCP server for MotherDuck
|
|
14
|
+
|
|
15
|
+
## Components
|
|
16
|
+
|
|
17
|
+
### Resources
|
|
18
|
+
|
|
19
|
+
### Prompts
|
|
20
|
+
|
|
21
|
+
### Tools
|
|
22
|
+
|
|
23
|
+
The server implements one tool:
|
|
24
|
+
- read-query-motherduck: Executes a single SQL SELECT statement on MotherDuck and retrieves the result
|
|
25
|
+
- Takes "query" as required string arguments
|
|
26
|
+
|
|
27
|
+
## Configuration
|
|
28
|
+
|
|
29
|
+
Set [MotherDuck token](https://motherduck.com/docs/key-tasks/authenticating-and-connecting-to-motherduck/authenticating-to-motherduck/#storing-the-access-token-as-an-environment-variable) as environment variable:
|
|
30
|
+
`export motherduck_token='<token>'`
|
|
31
|
+
|
|
32
|
+
## Quickstart
|
|
33
|
+
|
|
34
|
+
### Install
|
|
35
|
+
|
|
36
|
+
#### Claude Desktop
|
|
37
|
+
|
|
38
|
+
On MacOS: `~/Library/Application\ Support/Claude/claude_desktop_config.json`
|
|
39
|
+
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
|
|
40
|
+
|
|
41
|
+
<details>
|
|
42
|
+
<summary>Development/Unpublished Servers Configuration</summary>
|
|
43
|
+
```
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"mcp-server-motherduck": {
|
|
46
|
+
"command": "uv",
|
|
47
|
+
"args": [
|
|
48
|
+
"--directory",
|
|
49
|
+
"/Users/<username>/mcp-server/mcp-server-motherduck",
|
|
50
|
+
"run",
|
|
51
|
+
"mcp-server-motherduck"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
</details>
|
|
57
|
+
|
|
58
|
+
<details>
|
|
59
|
+
<summary>Published Servers Configuration</summary>
|
|
60
|
+
```
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"mcp-server-motherduck": {
|
|
63
|
+
"command": "uvx",
|
|
64
|
+
"args": [
|
|
65
|
+
"mcp-server-motherduck"
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
</details>
|
|
71
|
+
|
|
72
|
+
## Development
|
|
73
|
+
|
|
74
|
+
### Building and Publishing
|
|
75
|
+
|
|
76
|
+
To prepare the package for distribution:
|
|
77
|
+
|
|
78
|
+
1. Sync dependencies and update lockfile:
|
|
79
|
+
```bash
|
|
80
|
+
uv sync
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
2. Build package distributions:
|
|
84
|
+
```bash
|
|
85
|
+
uv build
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
This will create source and wheel distributions in the `dist/` directory.
|
|
89
|
+
|
|
90
|
+
3. Publish to PyPI:
|
|
91
|
+
```bash
|
|
92
|
+
uv publish
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Note: You'll need to set PyPI credentials via environment variables or command flags:
|
|
96
|
+
- Token: `--token` or `UV_PUBLISH_TOKEN`
|
|
97
|
+
- Or username/password: `--username`/`UV_PUBLISH_USERNAME` and `--password`/`UV_PUBLISH_PASSWORD`
|
|
98
|
+
|
|
99
|
+
### Debugging
|
|
100
|
+
|
|
101
|
+
Since MCP servers run over stdio, debugging can be challenging. For the best debugging
|
|
102
|
+
experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector).
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with this command:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npx @modelcontextprotocol/inspector uv --directory /Users/doehmen/Documents/Research/docs/ai-playground/mcp-server/mcp-server-motherduck run mcp-server-motherduck
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
mcp_server_motherduck/__init__.py,sha256=KNZ1bD9ZGfyZwlv91Ueeega_1lsRDLs2fYQDgNbBdtc,212
|
|
2
|
+
mcp_server_motherduck/server.py,sha256=iMKB8Y_RpPUKgjNMu1INJ2cTe0YOJIKWBIlkjoU1oXc,3445
|
|
3
|
+
mcp_server_motherduck-0.1.0.dist-info/METADATA,sha256=fNVbRCZNdhbSfZdabcpSS-YFkZRyyGdq1lDyuqgo_ho,2767
|
|
4
|
+
mcp_server_motherduck-0.1.0.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
5
|
+
mcp_server_motherduck-0.1.0.dist-info/entry_points.txt,sha256=dRTgcvWJn40bz0PVuKPylK6w92cFN32lwunZOgo5j4s,69
|
|
6
|
+
mcp_server_motherduck-0.1.0.dist-info/RECORD,,
|