Skip to content

Commit a7d985c

Browse files
authored
feat(mcp): introduce MCP server (#1094)
1 parent 4ac283e commit a7d985c

File tree

11 files changed

+1158
-0
lines changed

11 files changed

+1158
-0
lines changed

mcp-server/README.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Wren MCP Server
2+
3+
The **Wren MCP Server** is a **Model Context Protocol (MCP) server** that provides tools for interacting with **Wren Engine** to facilitate AI agent integration.
4+
5+
## Requirements
6+
7+
Before setting up the Wren MCP Server, ensure you have the following dependency installed:
8+
9+
- **[uv](https://docs.astral.sh/uv/getting-started/installation/#installing-uv)** - A fast and efficient Python package manager.
10+
11+
## Environment Variables
12+
13+
The server requires the following environment variables to be set:
14+
15+
| Variable | Description |
16+
|----------|------------|
17+
| `WREN_URL` | The URL of the **Wren Ibis server**. |
18+
| `CONNECTION_INFO_FILE` | The path to the **required connection info file**. |
19+
| `MDL_PATH` | The path to the **MDL file**. |
20+
21+
### Connection Info
22+
23+
The following JSON is a connection info of a Postgres. You can find the requried fields for each data source in the [source code](https://github.com/Canner/wren-engine/blob/4ac283ee0754b12a8c3b0a6f13b32c935fcb7b0d/ibis-server/app/model/__init__.py#L75).
24+
```json
25+
{
26+
"host": "localhost",
27+
"port": "5432",
28+
"user": "test",
29+
"password": "test",
30+
"database": "test"
31+
}
32+
```
33+
34+
### The `dataSource` field is requried.
35+
36+
In the MDL, the `dataSource` field is required to indicate which data source should be connected.
37+
38+
### `.env` File Support
39+
40+
Wren MCP Server supports an `.env` file for easier environment configuration. You can define all the required environment variables in this file.
41+
42+
---
43+
44+
## Installation & Usage
45+
46+
### 1. Set the Python Envrionment
47+
48+
Use the `uv` command to create a virtual envrionment and activate it:
49+
```
50+
> uv venv
51+
Using CPython 3.11.11
52+
Creating virtual environment at: .venv
53+
Activate with: source .venv/bin/activate
54+
> source .venv/bin/activate
55+
> uv run app/wren.py
56+
Loaded MDL etc/mdl.json
57+
Loaded connection info etc/pg_conneciton.json
58+
```
59+
You would see that the MDL and connection info are loaded. Then, you can use `Ctrl + C` terminate the process.
60+
61+
### 2. Start Wren Engine and Ibis Server
62+
63+
- If you **already have a running Wren Engine**, ensure that `WREN_URL` is correctly set to point to your server.
64+
- If you **don't have a running engine**, you can start one using Docker:
65+
66+
```sh
67+
cd docker
68+
docker compose up
69+
```
70+
71+
### 3. Set Environment Variables
72+
73+
Make sure all required environment variables are properly configured, either in your system or within a `.env` file.
74+
75+
### 4. Configure the MCP Server
76+
77+
Create a configuration file with the following structure:
78+
79+
```json
80+
{
81+
"mcpServers": {
82+
"wren": {
83+
"command": "uv",
84+
"args": [
85+
"--directory",
86+
"/ABSOLUTE/PATH/TO/PARENT/FOLDER/wren-engine/mcp-server",
87+
"run",
88+
"app/wren.py"
89+
],
90+
"autoApprove": [],
91+
"disabled": false
92+
}
93+
}
94+
}
95+
```
96+
97+
#### Notes:
98+
- You **may need to provide the full path** to the `uv` executable in the `"command"` field. You can find it using:
99+
- **MacOS/Linux**: `which uv`
100+
- **Windows**: `where uv`
101+
- Ensure that the **absolute path** to the MCP server directory is used in the configuration.
102+
- For more details, refer to the [MCP Server Guide](https://modelcontextprotocol.io/quickstart/server#test-with-commands).
103+
104+
### 5. Choose an AI Agent That Supports MCP Server
105+
106+
The following AI agents are compatible with Wren MCP Server and deploy the MCP config:
107+
108+
- **[Claude Desktop](https://modelcontextprotocol.io/quickstart/user)**
109+
- **[Cline](https://docs.cline.bot/mcp-servers/mcp-quickstart)**
110+
111+
### 6. Check the Wren Engine is Connected
112+
113+
You can ask the AI agent to perform a health check for Wren Engine.
114+
115+
### 7. Start the Conversation
116+
117+
Now, you can start asking questions through your AI agent and interact with Wren Engine.
118+
119+
---
120+
121+
## Additional Resources
122+
123+
- **Wren Engine Documentation**: [Wren AI](https://getwren.ai/)
124+
- **MCP Protocol Guide**: [Model Context Protocol](https://modelcontextprotocol.io/)

mcp-server/app/__init__.py

Whitespace-only changes.

mcp-server/app/dto.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from pydantic import BaseModel
2+
from pydantic.fields import Field
3+
4+
5+
class Column(BaseModel):
6+
name: str
7+
type: str
8+
expression: str = None
9+
isCalculated: bool = False
10+
relationship: str = None
11+
description: str = None
12+
13+
14+
class TableReference(BaseModel):
15+
catalog: str = None
16+
mdl_schema: str = Field(alias="schema", default=None)
17+
table: str
18+
19+
20+
class Model(BaseModel):
21+
name: str
22+
tableReference: TableReference
23+
columns: list[Column]
24+
primaryKey: str = None
25+
description: str = None
26+
27+
28+
class Relationship(BaseModel):
29+
name: str
30+
models: list[str]
31+
join_type: str
32+
join_condition: str
33+
34+
35+
class View(BaseModel):
36+
name: str
37+
statement: str
38+
description: str = None
39+
40+
41+
class Manifest(BaseModel):
42+
catalog: str = "wren"
43+
mdl_schema: str = Field(alias="schema", default="public")
44+
models: list[Model]
45+
relationships: list[Relationship]
46+
views: list[View]
47+
description: str = None
48+
49+
50+
class TableColumns(BaseModel):
51+
table_name: str
52+
column_names: list[str] = None

mcp-server/app/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import base64
2+
import orjson
3+
4+
5+
def dict_to_base64_string(data: dict[str, any]) -> str:
6+
return base64.b64encode(orjson.dumps(data)).decode("utf-8")
7+
8+
9+
def json_to_base64_string(data: str) -> str:
10+
return base64.b64encode(data.encode("utf-8")).decode("utf-8")

0 commit comments

Comments
 (0)