Skip to content

Commit 80e1fe8

Browse files
Merge pull request #13 from dremio/config-support
Config suppot
2 parents 9362736 + c60dfaf commit 80e1fe8

3 files changed

Lines changed: 229 additions & 24 deletions

File tree

README.md

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22

33
Table of Contents
44

5-
- [Introduction](#introduction)
6-
- [Installation](#installation)
7-
- [Initial setup](#initial-setup)
8-
- [MCP server config file](#mcp-server-config-file)
9-
- [Format](#format)
10-
- [Modes](#modes)
11-
- [The LLM (Claude) config file](#the-llm-claude-config-file)
12-
- [Further Documentation](#further-documentation)
13-
- [Additional Information](#additional-information)
5+
- [Introduction](#introduction)
6+
- [Installation](#installation)
7+
- [Initial setup](#initial-setup)
8+
- [Quick start](#quick-start)
9+
- [Demo](#demo)
10+
- [Configuartion details](#configuartion-details)
11+
- [MCP server config file](#mcp-server-config-file)
12+
- [Format](#format)
13+
- [Modes](#modes)
14+
- [The LLM (Claude) config file](#the-llm-claude-config-file)
15+
- [Further Documentation](#further-documentation)
16+
- [Additional Information](#additional-information)
1417

1518
# Introduction
1619

@@ -44,8 +47,11 @@ architecture-beta
4447

4548
# Installation
4649

47-
- The MCP server is python based and the only prerequiste is to install the package manager [uv](https://docs.astral.sh/uv/guides/install-python/)
48-
- Do a sanity check by doing
50+
The MCP server runs locally on the machine that runs the LLM frontend (eg Claude). The installation steps are simple
51+
52+
1. Clone or download this repository.
53+
2. Install the [uv](https://docs.astral.sh/uv/guides/install-python/) package manager. The MCP server requires python (3.11 or later)
54+
3. Do a sanity check by doing
4955

5056
```shell
5157
# cd <toplevel git dir> or add `--directory <toplevel git dir>`
@@ -75,11 +81,72 @@ There are two configurations necessary before the MCP server can be invoked.
7581
1. **The server config file**: This will cover the details of connecting and communicating with Dremio
7682
2. **The LLM config file**: This covers configuring the LLM desktop app (Claude for now) to make it aware of the MCP server
7783

78-
## MCP server config file
84+
## Quick start
85+
86+
The quickest way to do this setup is -
87+
88+
1. Create the dremio config file using
89+
90+
```shell
91+
$ uv run dremio-mcp-server config create dremio \
92+
--uri <dremio uri> \
93+
--pat <dremio pat> \
94+
# optional: add your project ID if setting up for dremio cloud
95+
# --project-id <dremio project id>
96+
```
97+
98+
2. Download and install Claude desktop. And then create the Claude config file using
99+
100+
```shell
101+
$ uv run dremio-mcp-server config create claude
102+
```
103+
104+
3. Validate the config files using
105+
106+
```shell
107+
$ uv run dremio-mcp-server config list --type claude`
108+
109+
Default config file: '/Users/..../Library/Application Support/Claude/claude_desktop_config.json' (exists = True)
110+
{
111+
'globalShortcut': '',
112+
'mcpServers': {
113+
'Dremio': {
114+
'command': '/opt/homebrew/Cellar/uv/0.6.14/bin/uv',
115+
'args': [
116+
'run',
117+
'--directory',
118+
'...../dremio-mcp',
119+
'dremio-mcp-server',
120+
'run'
121+
]
122+
}
123+
}
124+
}
125+
126+
$ uv run dremio-mcp-server config list --type claude`
127+
Default config file: /Users/..../.config/dremioai/config.yaml (exists = True)
128+
dremio:
129+
enable_experimental: false
130+
pat: ....
131+
uri: ....
132+
tools:
133+
server_mode: FOR_DATA_PATTERNS
134+
```
135+
136+
**You are done!**. You can start Claude and start using the MCP server
137+
138+
### Demo
139+
![Demo](assests/demo.gif)
140+
141+
The rest of the documentation below provides details of the config files
142+
143+
## Configuartion details
144+
145+
### MCP server config file
79146

80147
This file is located by default at `$HOME/.config/dremioai/config.yaml` but can be overriden using the `--config-file` option at runtime for `dremio-mcp-server`
81148

82-
### Format
149+
#### Format
83150

84151
```yaml
85152
# The dremio section contains 3 main things - the URI to connect, PAT to use
@@ -100,7 +167,7 @@ tools:
100167
#token: ...
101168
```
102169

103-
### Modes
170+
#### Modes
104171

105172
There are 3 modes
106173

@@ -110,7 +177,7 @@ There are 3 modes
110177

111178
Multiple modes can be specified with separated by `,`
112179

113-
## The LLM (Claude) config file
180+
### The LLM (Claude) config file
114181

115182
To setup the Claude config file (refer to [this as an example](https://modelcontextprotocol.io/quickstart/user#2-add-the-filesystem-mcp-server)) edit the Claude desktop config file
116183

assests/demo.gif

25.7 MB
Loading

src/dremioai/servers/mcp.py

Lines changed: 147 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@
2929
from rich import console, table, print as pp
3030
from click import Choice
3131
from dremioai.config import settings
32+
from enum import StrEnum, auto
33+
from json import load, dump as jdump
34+
from shutil import which
3235
import asyncio
33-
from yaml import dump
36+
from yaml import dump, add_representer
3437

3538

3639
def init(
@@ -146,23 +149,158 @@ def main(
146149
)
147150

148151

152+
class ConfigTypes(StrEnum):
153+
dremioai = auto()
154+
claude = auto()
155+
156+
149157
@tc.command("list", help="Show default configuration, if it exists")
150158
def show_default_config(
151159
show_filename: Annotated[
152160
bool, Option(help="Show the filename for default config file")
153161
] = False,
162+
type: Annotated[
163+
Optional[ConfigTypes],
164+
Option(help="The type of configuration to show", show_default=True),
165+
] = ConfigTypes.dremioai,
154166
):
155-
dc = settings.default_config()
156-
pp(f"Default config file: {dc!s} (exists = {dc.exists()!s})")
157-
if not show_filename:
158-
settings.configure(dc)
159-
pp(
160-
dump(
161-
settings.instance().model_dump(
162-
exclude_none=True, mode="json", exclude_unset=True
167+
168+
match type:
169+
case ConfigTypes.dremioai:
170+
dc = settings.default_config()
171+
pp(f"Default config file: {dc!s} (exists = {dc.exists()!s})")
172+
if not show_filename:
173+
settings.configure(dc)
174+
pp(
175+
dump(
176+
settings.instance().model_dump(
177+
exclude_none=True, mode="json", exclude_unset=True
178+
)
179+
)
163180
)
181+
case ConfigTypes.claude:
182+
cc = (
183+
Path.home()
184+
/ "Library"
185+
/ "Application Support"
186+
/ "Claude"
187+
/ "claude_desktop_config.json"
164188
)
189+
pp(f"Default config file: '{cc!s}' (exists = {cc.exists()!s})")
190+
if not show_filename:
191+
pp(load(cc.open()))
192+
193+
194+
cc = Typer(
195+
context_settings=dict(help_option_names=["-h", "--help"]),
196+
name="create",
197+
help="Create DremioAI or LLM configuration files",
198+
)
199+
tc.add_typer(cc)
200+
201+
202+
@cc.command("claude", help="Create a default configuration file for Claude")
203+
def create_default_config(
204+
dry_run: Annotated[
205+
bool, Option(help="Dry run, do not overwrite the config file. Just print it")
206+
] = False,
207+
):
208+
if (uv := which("uv")) is not None:
209+
uv = Path(uv).resolve()
210+
dir = str(Path(os.getcwd()).resolve())
211+
dmcp = {
212+
"Dremio": {
213+
"command": str(uv),
214+
"args": ["run", "--directory", dir, "dremio-mcp-server", "run"],
215+
}
216+
}
217+
cc = (
218+
Path.home()
219+
/ "Library"
220+
/ "Application Support"
221+
/ "Claude"
222+
/ "claude_desktop_config.json"
165223
)
224+
c = load(cc.open()) if cc.exists() else {"mcpServers": {}}
225+
c["mcpServers"].update(dmcp)
226+
if dry_run:
227+
pp(c)
228+
else:
229+
if not cc.exists():
230+
cc.parent.mkdir(parents=True, exist_ok=True)
231+
with cc.open("w") as f:
232+
jdump(c, f)
233+
pp(f"Created default config file: {cc!s}")
234+
else:
235+
raise FileNotFoundError("uv command not found. Please install uv")
236+
237+
238+
@cc.command("dremioai", help="Create a default configuration file")
239+
def create_default_config(
240+
uri: Annotated[
241+
str,
242+
Option(
243+
help=f"The Dremio URL or shorthand for Dremio Cloud regions ({ ','.join(settings.DremioCloudUri)})"
244+
),
245+
],
246+
pat: Annotated[
247+
str,
248+
Option(
249+
help="The Dremio PAT. If it starts with @ then treat the rest is treated as a filename"
250+
),
251+
],
252+
project_id: Annotated[
253+
Optional[str],
254+
Option(help="The Dremio project id, only if connecting to Dremio Cloud"),
255+
] = None,
256+
mode: Annotated[
257+
Optional[List[str]],
258+
Option("-m", "--mode", help="MCP server mode", click_type=Choice(_mode())),
259+
] = [tools.ToolType.FOR_DATA_PATTERNS.name],
260+
enable_experimental: Annotated[
261+
bool, Option(help="Enable experimental features")
262+
] = False,
263+
dry_run: Annotated[
264+
bool, Option(help="Dry run, do not overwrite the config file. Just print it")
265+
] = False,
266+
):
267+
mode = "|".join([tools.ToolType[m.upper()].name for m in mode])
268+
dremio = settings.Dremio.model_validate(
269+
{
270+
"uri": uri,
271+
"pat": pat,
272+
"project_id": project_id,
273+
"enable_experimental": enable_experimental,
274+
}
275+
)
276+
ts = settings.Tools.model_validate({"server_mode": tools.ToolType.FOR_SELF})
277+
settings.configure(settings.default_config(), force=True)
278+
settings.instance().dremio = dremio
279+
settings.instance().tools = ts
280+
d = settings.instance().model_dump(
281+
exclude_none=True, mode="json", exclude_unset=True
282+
)
283+
284+
add_representer(
285+
str,
286+
lambda dumper, data: dumper.represent_scalar(
287+
"tag:yaml.org,2002:str", data, style=('"' if "@" in data else None)
288+
),
289+
)
290+
if pat.startswith("@") and d["dremio"]["pat"] != pat:
291+
d["dremio"]["pat"] = pat
292+
d["tools"]["server_mode"] = mode
293+
294+
if dry_run:
295+
pp(dump(d))
296+
return
297+
298+
dc = settings.default_config()
299+
if not dc.exists():
300+
dc.parent.mkdir(parents=True, exist_ok=True)
301+
with dc.open("w") as f:
302+
dump(d, f)
303+
pp(f"Created default config file: {dc!s}")
166304

167305

168306
# --------------------------------------------------------------------------------

0 commit comments

Comments
 (0)