datris-mcp-server 1.2.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,124 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: datris-mcp-server
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: MCP server for the Datris AI Agent-Native Data Platform
|
|
5
|
+
Project-URL: Homepage, https://datris.ai
|
|
6
|
+
Project-URL: Repository, https://github.com/datris/datris-platform-oss
|
|
7
|
+
Project-URL: Documentation, https://github.com/datris/datris-platform-oss/blob/main/docs/mcp.md
|
|
8
|
+
Project-URL: Issues, https://github.com/datris/datris-platform-oss/issues
|
|
9
|
+
Author-email: Datris <info@datris.ai>
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
Keywords: ai-agent,data-platform,datris,mcp,model-context-protocol
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Database
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: mcp[cli]
|
|
24
|
+
Requires-Dist: python-dotenv
|
|
25
|
+
Requires-Dist: requests
|
|
26
|
+
Requires-Dist: starlette
|
|
27
|
+
Requires-Dist: uvicorn
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# Datris MCP Server
|
|
31
|
+
|
|
32
|
+
MCP (Model Context Protocol) server for the [Datris AI Agent-Native Data Platform](https://datris.ai). Enables AI agents (Claude Desktop, Claude Code, Cursor, and custom frameworks) to natively interact with the platform — discover data, create pipelines, upload files, monitor jobs, search vector databases, query structured data, and answer questions with AI.
|
|
33
|
+
|
|
34
|
+
<!-- mcp-name: io.github.datris/datris -->
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install datris-mcp-server
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
### stdio mode (Claude Desktop / Claude Code)
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
PIPELINE_URL=http://localhost:8080 datris-mcp-server
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Or run directly:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
PIPELINE_URL=http://localhost:8080 python server.py
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### SSE mode (Docker / remote agents)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
PIPELINE_URL=http://localhost:8080 datris-mcp-server --sse --port 3000
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Docker
|
|
63
|
+
|
|
64
|
+
The MCP server starts automatically with `docker compose up` in SSE mode on port 3000.
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
|
|
68
|
+
### Claude Desktop
|
|
69
|
+
|
|
70
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"datris": {
|
|
76
|
+
"command": "datris-mcp-server",
|
|
77
|
+
"env": {
|
|
78
|
+
"PIPELINE_URL": "http://localhost:8080"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Claude Code
|
|
86
|
+
|
|
87
|
+
Add to `.mcp.json` in your project root:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"datris": {
|
|
93
|
+
"command": "datris-mcp-server",
|
|
94
|
+
"env": {
|
|
95
|
+
"PIPELINE_URL": "http://localhost:8080"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Environment Variables
|
|
103
|
+
|
|
104
|
+
| Variable | Default | Description |
|
|
105
|
+
|----------|---------|-------------|
|
|
106
|
+
| `PIPELINE_URL` | `http://localhost:8080` | Datris pipeline server URL |
|
|
107
|
+
| `PIPELINE_API_KEY` | (empty) | API key if pipeline has key validation enabled |
|
|
108
|
+
|
|
109
|
+
## Tools
|
|
110
|
+
|
|
111
|
+
30+ tools across these categories:
|
|
112
|
+
|
|
113
|
+
- **Pipeline Management** — create, list, get, delete pipelines; upload files; monitor jobs
|
|
114
|
+
- **Vector Search** — semantic search across Qdrant, Weaviate, Milvus, Chroma, pgvector
|
|
115
|
+
- **Database Query** — read-only SQL queries (PostgreSQL) and MongoDB queries
|
|
116
|
+
- **Metadata Discovery** — explore databases, schemas, tables, columns, collections
|
|
117
|
+
- **AI** — RAG-powered question answering
|
|
118
|
+
- **System** — health checks, version info
|
|
119
|
+
|
|
120
|
+
See the full documentation at [docs/mcp.md](https://github.com/datris/datris-platform-oss/blob/main/docs/mcp.md).
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
Apache 2.0
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
server.py,sha256=HlC6y79o67MhyFDn6LWRozn7JhHaEpHenmZj3x9IwKU,44245
|
|
2
|
+
datris_mcp_server-1.2.0.dist-info/METADATA,sha256=3fWcS34zczHBleEEajduUDLzrpNkd5iXrzlElPJOcS4,3537
|
|
3
|
+
datris_mcp_server-1.2.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
4
|
+
datris_mcp_server-1.2.0.dist-info/entry_points.txt,sha256=sKsVHwslKpo51XRmkc94nvCyRVeHmQ9cbI0YE-6hN8Y,50
|
|
5
|
+
datris_mcp_server-1.2.0.dist-info/RECORD,,
|
server.py
ADDED
|
@@ -0,0 +1,1211 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Datris MCP Server
|
|
4
|
+
|
|
5
|
+
A thin REST API client that exposes the Datris platform as MCP tools so AI agents
|
|
6
|
+
(Claude, Cursor, etc.) can natively interact with the platform — discover data,
|
|
7
|
+
create pipelines, upload files, monitor jobs, search vector databases, query
|
|
8
|
+
structured data, and answer questions with AI.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
pip install -r requirements.txt
|
|
12
|
+
|
|
13
|
+
# stdio mode (for Claude Desktop / Claude Code)
|
|
14
|
+
python server.py
|
|
15
|
+
|
|
16
|
+
# SSE mode (for Docker / remote agents)
|
|
17
|
+
python server.py --sse --port 3000
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
import requests
|
|
26
|
+
from dotenv import load_dotenv
|
|
27
|
+
from mcp.server import Server
|
|
28
|
+
from mcp.types import Resource, Tool, TextContent
|
|
29
|
+
|
|
30
|
+
load_dotenv()
|
|
31
|
+
|
|
32
|
+
PIPELINE_URL = os.getenv("PIPELINE_URL", "http://localhost:8080")
|
|
33
|
+
PIPELINE_API_KEY = os.getenv("PIPELINE_API_KEY", "")
|
|
34
|
+
|
|
35
|
+
server = Server("datris", instructions="""\
|
|
36
|
+
Datris is the first AI Agent-Native Data Platform. It ingests, validates, transforms, and routes data to databases, message queues, and vector stores — all driven by pipeline configurations that AI agents can create and manage programmatically.
|
|
37
|
+
|
|
38
|
+
A pipeline defines a complete data processing flow:
|
|
39
|
+
- Source: file format (CSV, JSON, XML), schema with field names and types
|
|
40
|
+
- Data Quality: validation rules including regex patterns, AI-powered rules, and JSON Schema validation
|
|
41
|
+
- Transformation: deduplication, trimming, AI transforms, and JavaScript row functions
|
|
42
|
+
- Preprocessor: optional REST endpoint called before processing
|
|
43
|
+
- Destination: PostgreSQL, MongoDB, Kafka, ActiveMQ, REST endpoint, or vector databases (Qdrant, Weaviate, Milvus, Chroma, pgvector) with embedding configuration
|
|
44
|
+
|
|
45
|
+
Recommended workflow for agents:
|
|
46
|
+
1. Discover existing data: use metadata tools (list_postgres_databases, list_postgres_tables, etc.) to explore what's available
|
|
47
|
+
2. Create a pipeline: use generate_schema to auto-create from a sample file, or create_pipeline manually
|
|
48
|
+
3. Profile data: use profile_data to get AI-suggested data quality rules before ingestion
|
|
49
|
+
4. Ingest data: use upload_file to process files through the pipeline
|
|
50
|
+
5. Monitor: use get_job_status to track processing (status: RUNNING, COMPLETED, FAILED, CANCELLED)
|
|
51
|
+
6. Query & search: use query tools for structured data, search tools for semantic/vector search
|
|
52
|
+
7. RAG: combine search results with ai_answer for AI-powered question answering
|
|
53
|
+
""")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _headers():
|
|
57
|
+
"""Build request headers."""
|
|
58
|
+
h = {"Content-Type": "application/json"}
|
|
59
|
+
if PIPELINE_API_KEY:
|
|
60
|
+
h["x-api-key"] = PIPELINE_API_KEY
|
|
61
|
+
return h
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _call(method, path, **kwargs):
|
|
65
|
+
"""Make an HTTP request to the pipeline API."""
|
|
66
|
+
url = f"{PIPELINE_URL}{path}"
|
|
67
|
+
try:
|
|
68
|
+
resp = getattr(requests, method)(url, headers=_headers(), timeout=300, **kwargs)
|
|
69
|
+
return resp.text
|
|
70
|
+
except requests.RequestException as e:
|
|
71
|
+
return json.dumps({"error": str(e)})
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _upload(path, file_path, data=None):
|
|
75
|
+
"""Upload a file via multipart POST to the pipeline API."""
|
|
76
|
+
with open(file_path, "rb") as f:
|
|
77
|
+
files = {"file": (os.path.basename(file_path), f)}
|
|
78
|
+
h = {}
|
|
79
|
+
if PIPELINE_API_KEY:
|
|
80
|
+
h["x-api-key"] = PIPELINE_API_KEY
|
|
81
|
+
resp = requests.post(
|
|
82
|
+
f"{PIPELINE_URL}{path}",
|
|
83
|
+
headers=h,
|
|
84
|
+
files=files,
|
|
85
|
+
data=data or {},
|
|
86
|
+
timeout=300
|
|
87
|
+
)
|
|
88
|
+
return resp.text
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
# MCP Resources
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
PIPELINE_CONFIG_REFERENCE = """\
|
|
96
|
+
# Datris Pipeline Configuration Reference
|
|
97
|
+
|
|
98
|
+
A pipeline defines a complete data processing flow. The config JSON has these top-level sections:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"name": "pipeline_name",
|
|
103
|
+
"source": { ... },
|
|
104
|
+
"preprocessor": { ... },
|
|
105
|
+
"dataQuality": { ... },
|
|
106
|
+
"transformation": { ... },
|
|
107
|
+
"destination": { ... }
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Source
|
|
112
|
+
|
|
113
|
+
Defines the input file format and schema.
|
|
114
|
+
|
|
115
|
+
### fileAttributes (choose one)
|
|
116
|
+
|
|
117
|
+
**csvAttributes** — for CSV files:
|
|
118
|
+
```json
|
|
119
|
+
"csvAttributes": {
|
|
120
|
+
"delimiter": ",",
|
|
121
|
+
"header": true,
|
|
122
|
+
"encoding": "UTF-8"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**jsonAttributes** — for JSON files:
|
|
127
|
+
```json
|
|
128
|
+
"jsonAttributes": {
|
|
129
|
+
"everyRowContainsObject": false,
|
|
130
|
+
"encoding": "UTF-8"
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**xmlAttributes** — for XML files:
|
|
135
|
+
```json
|
|
136
|
+
"xmlAttributes": {
|
|
137
|
+
"everyRowContainsObject": false,
|
|
138
|
+
"encoding": "UTF-8"
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**xlsAttributes** — for Excel files:
|
|
143
|
+
```json
|
|
144
|
+
"xlsAttributes": {
|
|
145
|
+
"worksheet": 0,
|
|
146
|
+
"tempCsvFileDelimiter": ","
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**unstructuredAttributes** — for PDFs, DOCX, TXT (used with vector DB destinations):
|
|
151
|
+
```json
|
|
152
|
+
"unstructuredAttributes": {
|
|
153
|
+
"fileExtension": "pdf",
|
|
154
|
+
"preserveFilename": true
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### schemaProperties (required for structured data, omit for unstructured)
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
"schemaProperties": {
|
|
162
|
+
"fields": [
|
|
163
|
+
{"name": "column_name", "type": "string"},
|
|
164
|
+
{"name": "price", "type": "double"},
|
|
165
|
+
{"name": "quantity", "type": "int"}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Supported field types: `string`, `int`, `bigint`, `float`, `double`, `boolean`, `date`, `timestamp`
|
|
171
|
+
|
|
172
|
+
### streamAttributes (optional, for streaming sources like Kafka)
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
"streamAttributes": {
|
|
176
|
+
"type": "kafka"
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### databaseAttributes (optional, for database pull sources)
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
"databaseAttributes": {
|
|
184
|
+
"type": "postgres",
|
|
185
|
+
"postgresSecretsName": "oss/postgres",
|
|
186
|
+
"database": "mydb",
|
|
187
|
+
"schema": "public",
|
|
188
|
+
"table": "source_table",
|
|
189
|
+
"cronExpression": "0 0 * * *",
|
|
190
|
+
"timestampFieldName": "updated_at",
|
|
191
|
+
"includeFields": ["col1", "col2"],
|
|
192
|
+
"sqlOverride": "SELECT * FROM source_table WHERE active = true"
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Preprocessor (optional)
|
|
197
|
+
|
|
198
|
+
A REST endpoint called before processing each file. Use for custom validation or enrichment.
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
"preprocessor": {
|
|
202
|
+
"endpoint": "https://my-service.example.com/preprocess",
|
|
203
|
+
"bearerToken": "token123",
|
|
204
|
+
"apiKey": "key123",
|
|
205
|
+
"timeoutMs": 300000,
|
|
206
|
+
"async": false
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Data Quality (optional)
|
|
211
|
+
|
|
212
|
+
Validation rules applied to data before transformation and destination.
|
|
213
|
+
|
|
214
|
+
### columnRules — regex or function-based validation per column
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
"columnRules": [
|
|
218
|
+
{
|
|
219
|
+
"columnName": "email",
|
|
220
|
+
"function": "regex",
|
|
221
|
+
"parameter": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,}$",
|
|
222
|
+
"onFailureIsError": true,
|
|
223
|
+
"description": "Must be a valid email"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"columnName": "price",
|
|
227
|
+
"function": "regex",
|
|
228
|
+
"parameter": "^[0-9]+(\\\\.[0-9]+)?$",
|
|
229
|
+
"onFailureIsError": false,
|
|
230
|
+
"description": "Must be a positive number"
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### aiRule — AI-powered validation using natural language
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
"aiRule": {
|
|
239
|
+
"instruction": "All price columns must be positive and not exceed $1,000,000. Volume must be a positive integer.",
|
|
240
|
+
"onFailureIsError": false,
|
|
241
|
+
"sample": true,
|
|
242
|
+
"sampleSize": 200
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### validationSchema — JSON Schema file reference
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
"validationSchema": "my-schema.json"
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Upload the schema file first using the `upload_config` tool with type "validation-schema".
|
|
253
|
+
|
|
254
|
+
### validateFileHeader — check CSV headers match schema
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
"validateFileHeader": true
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Transformation (optional)
|
|
261
|
+
|
|
262
|
+
Transform data after validation, before writing to destination.
|
|
263
|
+
|
|
264
|
+
### Basic transformations
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
"transformation": {
|
|
268
|
+
"trimColumnWhitespace": true,
|
|
269
|
+
"deduplicate": true
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### rowFunctions — JavaScript-based row transformations
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
"rowFunctions": [
|
|
277
|
+
{
|
|
278
|
+
"function": "javascript",
|
|
279
|
+
"parameters": ["transform.js"]
|
|
280
|
+
}
|
|
281
|
+
]
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Upload the JS file first using the `upload_config` tool with type "javascript".
|
|
285
|
+
|
|
286
|
+
### rowFunctions — REST endpoint transformations
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
"rowFunctions": [
|
|
290
|
+
{
|
|
291
|
+
"function": "restEndpoint",
|
|
292
|
+
"parameters": ["http://my-service:5600/transform", "row", "30000", "", ""]
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Parameters: [endpoint URL, mode ("row" or "batch"), timeout ms, bearer token, API key].
|
|
298
|
+
In row mode, the endpoint receives `{"pipelineName": "...", "pipelineToken": "...", "row": {...}}` and returns `{"status": "success", "row": {...}}` (or `"row": null` to remove the row).
|
|
299
|
+
In batch mode, it receives `{"rows": [...]}` and returns `{"status": "success", "rows": [...]}` (null entries are removed).
|
|
300
|
+
|
|
301
|
+
### aiTransformation — AI-powered data transformation
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
"aiTransformation": {
|
|
305
|
+
"instruction": "Convert all date values from MM/DD/YYYY to YYYY-MM-DD format",
|
|
306
|
+
"sample": true,
|
|
307
|
+
"sampleSize": 200
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Destination (required — choose one)
|
|
312
|
+
|
|
313
|
+
### database — PostgreSQL
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
"destination": {
|
|
317
|
+
"database": {
|
|
318
|
+
"dbName": "datris",
|
|
319
|
+
"schema": "public",
|
|
320
|
+
"table": "my_table",
|
|
321
|
+
"usePostgres": true,
|
|
322
|
+
"keyFields": ["id"],
|
|
323
|
+
"truncateBeforeWrite": false,
|
|
324
|
+
"manageTableManually": false
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### database — MongoDB
|
|
330
|
+
|
|
331
|
+
```json
|
|
332
|
+
"destination": {
|
|
333
|
+
"database": {
|
|
334
|
+
"dbName": "datris",
|
|
335
|
+
"table": "my_collection",
|
|
336
|
+
"useMongoDB": true
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### objectStore — S3/MinIO
|
|
342
|
+
|
|
343
|
+
```json
|
|
344
|
+
"destination": {
|
|
345
|
+
"objectStore": {
|
|
346
|
+
"prefixKey": "data/output/",
|
|
347
|
+
"fileFormat": "parquet",
|
|
348
|
+
"writeMode": "overwrite",
|
|
349
|
+
"partitionBy": ["date", "region"]
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
File formats: `parquet`, `csv`, `json`, `orc`
|
|
355
|
+
Write modes: `overwrite`, `append`, `ignore`, `error`
|
|
356
|
+
|
|
357
|
+
### kafka
|
|
358
|
+
|
|
359
|
+
```json
|
|
360
|
+
"destination": {
|
|
361
|
+
"kafka": {
|
|
362
|
+
"topic": "my-topic",
|
|
363
|
+
"keyField": "id"
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### activeMQ
|
|
369
|
+
|
|
370
|
+
```json
|
|
371
|
+
"destination": {
|
|
372
|
+
"activeMQ": {
|
|
373
|
+
"queueName": "my-queue"
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### restEndpoint
|
|
379
|
+
|
|
380
|
+
```json
|
|
381
|
+
"destination": {
|
|
382
|
+
"restEndpoint": {
|
|
383
|
+
"endpoint": "https://my-service.example.com/ingest",
|
|
384
|
+
"bearerToken": "token123",
|
|
385
|
+
"timeoutMs": 300000
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Vector databases (for RAG / semantic search)
|
|
391
|
+
|
|
392
|
+
All vector DB destinations share a common structure with chunking and embedding config. Use with unstructured source files (PDF, DOCX, TXT).
|
|
393
|
+
|
|
394
|
+
**qdrant:**
|
|
395
|
+
```json
|
|
396
|
+
"destination": {
|
|
397
|
+
"qdrant": {
|
|
398
|
+
"collectionName": "my_documents",
|
|
399
|
+
"chunking": {"strategy": "recursive", "chunkSize": 500, "chunkOverlap": 50},
|
|
400
|
+
"metadata": {"source": "annual_reports", "year": "2026"},
|
|
401
|
+
"embeddingSecretName": "oss/embedding",
|
|
402
|
+
"qdrantSecretName": "oss/qdrant"
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**weaviate:**
|
|
408
|
+
```json
|
|
409
|
+
"destination": {
|
|
410
|
+
"weaviate": {
|
|
411
|
+
"className": "MyDocuments",
|
|
412
|
+
"chunking": {"strategy": "recursive", "chunkSize": 500, "chunkOverlap": 50},
|
|
413
|
+
"metadata": {"source": "annual_reports"},
|
|
414
|
+
"embeddingSecretName": "oss/embedding",
|
|
415
|
+
"weaviateSecretName": "oss/weaviate"
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**milvus:**
|
|
421
|
+
```json
|
|
422
|
+
"destination": {
|
|
423
|
+
"milvus": {
|
|
424
|
+
"collectionName": "my_documents",
|
|
425
|
+
"chunking": {"strategy": "recursive", "chunkSize": 500, "chunkOverlap": 50},
|
|
426
|
+
"metadata": {"source": "annual_reports"},
|
|
427
|
+
"embeddingSecretName": "oss/embedding",
|
|
428
|
+
"milvusSecretName": "oss/milvus"
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
**chroma:**
|
|
434
|
+
```json
|
|
435
|
+
"destination": {
|
|
436
|
+
"chroma": {
|
|
437
|
+
"collectionName": "my_documents",
|
|
438
|
+
"chunking": {"strategy": "recursive", "chunkSize": 500, "chunkOverlap": 50},
|
|
439
|
+
"metadata": {"source": "annual_reports"},
|
|
440
|
+
"embeddingSecretName": "oss/embedding",
|
|
441
|
+
"chromaSecretName": "oss/chroma"
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
**pgvector:**
|
|
447
|
+
```json
|
|
448
|
+
"destination": {
|
|
449
|
+
"pgvector": {
|
|
450
|
+
"tableName": "my_documents",
|
|
451
|
+
"schemaName": "public",
|
|
452
|
+
"chunking": {"strategy": "recursive", "chunkSize": 500, "chunkOverlap": 50},
|
|
453
|
+
"metadata": {"source": "annual_reports"},
|
|
454
|
+
"embeddingSecretName": "oss/embedding",
|
|
455
|
+
"postgresSecretName": "oss/pgvector"
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Complete Examples
|
|
461
|
+
|
|
462
|
+
### CSV → PostgreSQL with AI data quality
|
|
463
|
+
|
|
464
|
+
```json
|
|
465
|
+
{
|
|
466
|
+
"name": "stock_prices",
|
|
467
|
+
"source": {
|
|
468
|
+
"fileAttributes": {
|
|
469
|
+
"csvAttributes": {"delimiter": ",", "header": true, "encoding": "UTF-8"}
|
|
470
|
+
},
|
|
471
|
+
"schemaProperties": {
|
|
472
|
+
"fields": [
|
|
473
|
+
{"name": "symbol", "type": "string"},
|
|
474
|
+
{"name": "date", "type": "string"},
|
|
475
|
+
{"name": "close", "type": "double"},
|
|
476
|
+
{"name": "volume", "type": "int"}
|
|
477
|
+
]
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
"dataQuality": {
|
|
481
|
+
"aiRule": {
|
|
482
|
+
"instruction": "All price columns must be positive. Volume must be a positive integer.",
|
|
483
|
+
"onFailureIsError": false
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
"destination": {
|
|
487
|
+
"database": {
|
|
488
|
+
"dbName": "datris",
|
|
489
|
+
"schema": "public",
|
|
490
|
+
"table": "stock_prices",
|
|
491
|
+
"usePostgres": true
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### PDF → pgvector for RAG
|
|
498
|
+
|
|
499
|
+
```json
|
|
500
|
+
{
|
|
501
|
+
"name": "financial_docs",
|
|
502
|
+
"source": {
|
|
503
|
+
"fileAttributes": {
|
|
504
|
+
"unstructuredAttributes": {"fileExtension": "pdf", "preserveFilename": true}
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
"destination": {
|
|
508
|
+
"pgvector": {
|
|
509
|
+
"tableName": "financial_documents",
|
|
510
|
+
"schemaName": "public",
|
|
511
|
+
"chunking": {"strategy": "recursive", "chunkSize": 500, "chunkOverlap": 50},
|
|
512
|
+
"metadata": {"company": "Acme Corp", "document_type": "10-K"},
|
|
513
|
+
"embeddingSecretName": "oss/embedding",
|
|
514
|
+
"postgresSecretName": "oss/pgvector"
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### JSON → MongoDB
|
|
521
|
+
|
|
522
|
+
```json
|
|
523
|
+
{
|
|
524
|
+
"name": "events",
|
|
525
|
+
"source": {
|
|
526
|
+
"fileAttributes": {
|
|
527
|
+
"jsonAttributes": {"everyRowContainsObject": false, "encoding": "UTF-8"}
|
|
528
|
+
},
|
|
529
|
+
"schemaProperties": {
|
|
530
|
+
"fields": [{"name": "_json", "type": "string"}]
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
"destination": {
|
|
534
|
+
"database": {
|
|
535
|
+
"dbName": "datris",
|
|
536
|
+
"table": "events",
|
|
537
|
+
"useMongoDB": true
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
"""
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
@server.list_resources()
|
|
546
|
+
async def list_resources():
|
|
547
|
+
return [
|
|
548
|
+
Resource(
|
|
549
|
+
uri="datris://pipeline-config-reference",
|
|
550
|
+
name="Pipeline Configuration Reference",
|
|
551
|
+
description="Complete reference for building Datris pipeline configurations. Covers all source types (CSV, JSON, XML, PDF), data quality rules (regex, AI), transformations, and all destination types (PostgreSQL, MongoDB, Kafka, vector databases). Read this before using create_pipeline.",
|
|
552
|
+
mimeType="text/plain",
|
|
553
|
+
)
|
|
554
|
+
]
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
@server.read_resource()
|
|
558
|
+
async def read_resource(uri):
|
|
559
|
+
if str(uri) == "datris://pipeline-config-reference":
|
|
560
|
+
return PIPELINE_CONFIG_REFERENCE
|
|
561
|
+
raise ValueError(f"Unknown resource: {uri}")
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
# ---------------------------------------------------------------------------
|
|
565
|
+
# MCP Tools
|
|
566
|
+
# ---------------------------------------------------------------------------
|
|
567
|
+
|
|
568
|
+
@server.list_tools()
|
|
569
|
+
async def list_tools():
|
|
570
|
+
return [
|
|
571
|
+
# --- Pipeline Management ---
|
|
572
|
+
Tool(
|
|
573
|
+
name="list_pipelines",
|
|
574
|
+
description="List all registered pipeline configurations. Each pipeline defines a complete data processing flow: source format and schema, data quality rules (regex + AI), transformations, and destination (database, message queue, or vector store).",
|
|
575
|
+
inputSchema={
|
|
576
|
+
"type": "object",
|
|
577
|
+
"properties": {},
|
|
578
|
+
}
|
|
579
|
+
),
|
|
580
|
+
Tool(
|
|
581
|
+
name="get_pipeline",
|
|
582
|
+
description="Get a specific pipeline configuration by name. Returns the full JSON config including source, dataQuality, transformation, preprocessor, and destination sections.",
|
|
583
|
+
inputSchema={
|
|
584
|
+
"type": "object",
|
|
585
|
+
"properties": {
|
|
586
|
+
"pipeline": {
|
|
587
|
+
"type": "string",
|
|
588
|
+
"description": "Pipeline name"
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
"required": ["pipeline"]
|
|
592
|
+
}
|
|
593
|
+
),
|
|
594
|
+
Tool(
|
|
595
|
+
name="create_pipeline",
|
|
596
|
+
description="Create or update a pipeline configuration. The config JSON must include 'name' and typically includes: source (fileAttributes, schemaProperties with field definitions), dataQuality (column rules with regex, AI rules, schema validation), transformation (deduplication, trimming, AI transforms, row functions), and destination (database, objectStore, kafka, activeMQ, restEndpoint, or vector DB like qdrant/weaviate/milvus/chroma/pgvector). Read the 'Pipeline Configuration Reference' resource for full field details and examples. Use generate_schema to auto-create a config from a sample file.",
|
|
597
|
+
inputSchema={
|
|
598
|
+
"type": "object",
|
|
599
|
+
"properties": {
|
|
600
|
+
"config": {
|
|
601
|
+
"type": "object",
|
|
602
|
+
"description": "Full pipeline configuration JSON including name, source, destination, dataQuality, transformation"
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
"required": ["config"]
|
|
606
|
+
}
|
|
607
|
+
),
|
|
608
|
+
Tool(
|
|
609
|
+
name="delete_pipeline",
|
|
610
|
+
description="Delete a registered pipeline configuration by name. This removes the config but does not delete any data already processed by the pipeline.",
|
|
611
|
+
inputSchema={
|
|
612
|
+
"type": "object",
|
|
613
|
+
"properties": {
|
|
614
|
+
"pipeline": {
|
|
615
|
+
"type": "string",
|
|
616
|
+
"description": "Pipeline name to delete"
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
"required": ["pipeline"]
|
|
620
|
+
}
|
|
621
|
+
),
|
|
622
|
+
Tool(
|
|
623
|
+
name="upload_file",
|
|
624
|
+
description="Upload a data file (CSV, JSON, XML, or compressed archive) to a registered pipeline for processing. The pipeline's rules are applied: schema validation, data quality checks, transformations, then routing to the configured destination. Returns a pipeline token for tracking job status via get_job_status.",
|
|
625
|
+
inputSchema={
|
|
626
|
+
"type": "object",
|
|
627
|
+
"properties": {
|
|
628
|
+
"file_path": {
|
|
629
|
+
"type": "string",
|
|
630
|
+
"description": "Absolute path to the file to upload"
|
|
631
|
+
},
|
|
632
|
+
"pipeline": {
|
|
633
|
+
"type": "string",
|
|
634
|
+
"description": "Pipeline name to process the file with"
|
|
635
|
+
}
|
|
636
|
+
},
|
|
637
|
+
"required": ["file_path", "pipeline"]
|
|
638
|
+
}
|
|
639
|
+
),
|
|
640
|
+
Tool(
|
|
641
|
+
name="get_job_status",
|
|
642
|
+
description="Get job status. Query by pipeline_token (from upload_file) for detailed status of a specific job, or by pipeline_name for a paginated summary of all jobs for that pipeline. Status values: RUNNING, COMPLETED, FAILED, CANCELLED.",
|
|
643
|
+
inputSchema={
|
|
644
|
+
"type": "object",
|
|
645
|
+
"properties": {
|
|
646
|
+
"pipeline_token": {
|
|
647
|
+
"type": "string",
|
|
648
|
+
"description": "Pipeline token returned from upload_file"
|
|
649
|
+
},
|
|
650
|
+
"pipeline_name": {
|
|
651
|
+
"type": "string",
|
|
652
|
+
"description": "Pipeline name to get latest status for"
|
|
653
|
+
},
|
|
654
|
+
"page": {
|
|
655
|
+
"type": "integer",
|
|
656
|
+
"description": "Page number for paginated results (default: 1)"
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
),
|
|
661
|
+
Tool(
|
|
662
|
+
name="kill_job",
|
|
663
|
+
description="Kill a running pipeline job by its pipeline token. The job thread will be interrupted and the job marked as cancelled.",
|
|
664
|
+
inputSchema={
|
|
665
|
+
"type": "object",
|
|
666
|
+
"properties": {
|
|
667
|
+
"pipeline_token": {
|
|
668
|
+
"type": "string",
|
|
669
|
+
"description": "Pipeline token of the running job to kill"
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
"required": ["pipeline_token"]
|
|
673
|
+
}
|
|
674
|
+
),
|
|
675
|
+
Tool(
|
|
676
|
+
name="generate_schema",
|
|
677
|
+
description="Upload a sample data file and use AI to automatically generate a complete pipeline configuration with inferred field names, data types, and schema. Supports CSV, JSON, and XML. This is the fastest way to create a new pipeline — generate the config, review/modify it, then register it with create_pipeline.",
|
|
678
|
+
inputSchema={
|
|
679
|
+
"type": "object",
|
|
680
|
+
"properties": {
|
|
681
|
+
"file_path": {
|
|
682
|
+
"type": "string",
|
|
683
|
+
"description": "Absolute path to the file to analyze"
|
|
684
|
+
},
|
|
685
|
+
"pipeline": {
|
|
686
|
+
"type": "string",
|
|
687
|
+
"description": "Pipeline name for the generated configuration"
|
|
688
|
+
},
|
|
689
|
+
"delimiter": {
|
|
690
|
+
"type": "string",
|
|
691
|
+
"description": "CSV delimiter (default: comma)"
|
|
692
|
+
},
|
|
693
|
+
"header": {
|
|
694
|
+
"type": "boolean",
|
|
695
|
+
"description": "Whether CSV has a header row (default: true)"
|
|
696
|
+
}
|
|
697
|
+
},
|
|
698
|
+
"required": ["file_path", "pipeline"]
|
|
699
|
+
}
|
|
700
|
+
),
|
|
701
|
+
Tool(
|
|
702
|
+
name="profile_data",
|
|
703
|
+
description="Upload a data file and use AI to generate a comprehensive data profile: summary statistics per column, data quality issues detected, and suggested validation rules (regex patterns and AI-powered rules). Use the suggested rules when building a pipeline's dataQuality section.",
|
|
704
|
+
inputSchema={
|
|
705
|
+
"type": "object",
|
|
706
|
+
"properties": {
|
|
707
|
+
"file_path": {
|
|
708
|
+
"type": "string",
|
|
709
|
+
"description": "Absolute path to the file to profile"
|
|
710
|
+
},
|
|
711
|
+
"delimiter": {
|
|
712
|
+
"type": "string",
|
|
713
|
+
"description": "CSV delimiter (default: comma)"
|
|
714
|
+
},
|
|
715
|
+
"header": {
|
|
716
|
+
"type": "boolean",
|
|
717
|
+
"description": "Whether CSV has a header row (default: true)"
|
|
718
|
+
},
|
|
719
|
+
"sample_size": {
|
|
720
|
+
"type": "integer",
|
|
721
|
+
"description": "Number of rows to sample for profiling (default: 200)"
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
"required": ["file_path"]
|
|
725
|
+
}
|
|
726
|
+
),
|
|
727
|
+
Tool(
|
|
728
|
+
name="get_version",
|
|
729
|
+
description="Get the Datris server version.",
|
|
730
|
+
inputSchema={
|
|
731
|
+
"type": "object",
|
|
732
|
+
"properties": {},
|
|
733
|
+
}
|
|
734
|
+
),
|
|
735
|
+
Tool(
|
|
736
|
+
name="check_service_health",
|
|
737
|
+
description="Check which backend services are up, down, or not configured. Returns the health status of PostgreSQL, MongoDB, MinIO, ActiveMQ, Kafka, and any configured vector databases (Qdrant, Weaviate, Milvus, Chroma, pgvector). Call this before attempting search or query operations to know which services are available.",
|
|
738
|
+
inputSchema={
|
|
739
|
+
"type": "object",
|
|
740
|
+
"properties": {},
|
|
741
|
+
}
|
|
742
|
+
),
|
|
743
|
+
# --- Vector Database Search Tools ---
|
|
744
|
+
Tool(
|
|
745
|
+
name="search_qdrant",
|
|
746
|
+
description="Semantic search across a Qdrant vector database collection. Takes a natural language query, generates an embedding, and returns the most similar document chunks with similarity scores. For RAG: pass the returned text to ai_answer with the user's question.",
|
|
747
|
+
inputSchema={
|
|
748
|
+
"type": "object",
|
|
749
|
+
"properties": {
|
|
750
|
+
"query": {"type": "string", "description": "Natural language search query"},
|
|
751
|
+
"collection": {"type": "string", "description": "Qdrant collection name (default: financial_documents)"},
|
|
752
|
+
"top_k": {"type": "integer", "description": "Number of results to return (default: 5)"},
|
|
753
|
+
},
|
|
754
|
+
"required": ["query"]
|
|
755
|
+
}
|
|
756
|
+
),
|
|
757
|
+
Tool(
|
|
758
|
+
name="search_weaviate",
|
|
759
|
+
description="Semantic search across a Weaviate vector database class. Takes a natural language query, generates an embedding, and returns the most similar document chunks with similarity scores. For RAG: pass the returned text to ai_answer with the user's question.",
|
|
760
|
+
inputSchema={
|
|
761
|
+
"type": "object",
|
|
762
|
+
"properties": {
|
|
763
|
+
"query": {"type": "string", "description": "Natural language search query"},
|
|
764
|
+
"class_name": {"type": "string", "description": "Weaviate class name (default: FinancialDocuments)"},
|
|
765
|
+
"top_k": {"type": "integer", "description": "Number of results to return (default: 5)"},
|
|
766
|
+
},
|
|
767
|
+
"required": ["query"]
|
|
768
|
+
}
|
|
769
|
+
),
|
|
770
|
+
Tool(
|
|
771
|
+
name="search_milvus",
|
|
772
|
+
description="Semantic search across a Milvus vector database collection. Takes a natural language query, generates an embedding, and returns the most similar document chunks with similarity scores. For RAG: pass the returned text to ai_answer with the user's question.",
|
|
773
|
+
inputSchema={
|
|
774
|
+
"type": "object",
|
|
775
|
+
"properties": {
|
|
776
|
+
"query": {"type": "string", "description": "Natural language search query"},
|
|
777
|
+
"collection": {"type": "string", "description": "Milvus collection name (default: financial_documents)"},
|
|
778
|
+
"top_k": {"type": "integer", "description": "Number of results to return (default: 5)"},
|
|
779
|
+
},
|
|
780
|
+
"required": ["query"]
|
|
781
|
+
}
|
|
782
|
+
),
|
|
783
|
+
Tool(
|
|
784
|
+
name="search_pgvector",
|
|
785
|
+
description="Semantic search across a PostgreSQL pgvector table using cosine distance. Takes a natural language query, generates an embedding, and returns the most similar document chunks with similarity scores. Use list_postgres_tables with vector_only=true to discover available pgvector tables. For RAG: pass the returned text to ai_answer.",
|
|
786
|
+
inputSchema={
|
|
787
|
+
"type": "object",
|
|
788
|
+
"properties": {
|
|
789
|
+
"query": {"type": "string", "description": "Natural language search query"},
|
|
790
|
+
"table": {"type": "string", "description": "Table name (default: financial_documents)"},
|
|
791
|
+
"schema": {"type": "string", "description": "PostgreSQL schema (default: public)"},
|
|
792
|
+
"top_k": {"type": "integer", "description": "Number of results to return (default: 5)"},
|
|
793
|
+
},
|
|
794
|
+
"required": ["query"]
|
|
795
|
+
}
|
|
796
|
+
),
|
|
797
|
+
Tool(
|
|
798
|
+
name="search_chroma",
|
|
799
|
+
description="Semantic search across a Chroma vector database collection. Takes a natural language query, generates an embedding, and returns the most similar document chunks with similarity scores. For RAG: pass the returned text to ai_answer with the user's question.",
|
|
800
|
+
inputSchema={
|
|
801
|
+
"type": "object",
|
|
802
|
+
"properties": {
|
|
803
|
+
"query": {"type": "string", "description": "Natural language search query"},
|
|
804
|
+
"collection": {"type": "string", "description": "Chroma collection name (default: financial_documents)"},
|
|
805
|
+
"top_k": {"type": "integer", "description": "Number of results to return (default: 5)"},
|
|
806
|
+
},
|
|
807
|
+
"required": ["query"]
|
|
808
|
+
}
|
|
809
|
+
),
|
|
810
|
+
# --- Database Query Tools ---
|
|
811
|
+
Tool(
|
|
812
|
+
name="query_postgres",
|
|
813
|
+
description="Execute a read-only SQL SELECT query against PostgreSQL. Use the metadata discovery tools (list_postgres_databases, list_postgres_schemas, list_postgres_tables, list_postgres_columns) first to explore available data before constructing queries. Only SELECT is allowed; LIMIT is auto-appended if missing.",
|
|
814
|
+
inputSchema={
|
|
815
|
+
"type": "object",
|
|
816
|
+
"properties": {
|
|
817
|
+
"sql": {"type": "string", "description": "SQL SELECT query to execute"},
|
|
818
|
+
"limit": {"type": "integer", "description": "Maximum rows to return (default: 100)"},
|
|
819
|
+
},
|
|
820
|
+
"required": ["sql"]
|
|
821
|
+
}
|
|
822
|
+
),
|
|
823
|
+
Tool(
|
|
824
|
+
name="query_mongodb",
|
|
825
|
+
description="Query a MongoDB collection with optional filter and projection. Use list_mongodb_databases and list_mongodb_collections first to discover available data. Returns matching documents as JSON.",
|
|
826
|
+
inputSchema={
|
|
827
|
+
"type": "object",
|
|
828
|
+
"properties": {
|
|
829
|
+
"collection": {"type": "string", "description": "MongoDB collection name"},
|
|
830
|
+
"filter": {"type": "object", "description": "MongoDB query filter (default: {})"},
|
|
831
|
+
"projection": {"type": "object", "description": "Fields to include/exclude (default: all fields)"},
|
|
832
|
+
"limit": {"type": "integer", "description": "Maximum documents to return (default: 20)"},
|
|
833
|
+
},
|
|
834
|
+
"required": ["collection"]
|
|
835
|
+
}
|
|
836
|
+
),
|
|
837
|
+
# --- Metadata Discovery Tools ---
|
|
838
|
+
Tool(
|
|
839
|
+
name="list_postgres_databases",
|
|
840
|
+
description="List all PostgreSQL databases available in the Datris platform. Use this as the first step when exploring what data has been ingested into PostgreSQL destinations.",
|
|
841
|
+
inputSchema={
|
|
842
|
+
"type": "object",
|
|
843
|
+
"properties": {},
|
|
844
|
+
}
|
|
845
|
+
),
|
|
846
|
+
Tool(
|
|
847
|
+
name="list_postgres_schemas",
|
|
848
|
+
description="List all schemas in a PostgreSQL database. Schemas organize tables within a database (e.g., 'public', 'analytics'). Use after list_postgres_databases to drill into a specific database.",
|
|
849
|
+
inputSchema={
|
|
850
|
+
"type": "object",
|
|
851
|
+
"properties": {
|
|
852
|
+
"database": {"type": "string", "description": "Database name (default: datris)"},
|
|
853
|
+
},
|
|
854
|
+
}
|
|
855
|
+
),
|
|
856
|
+
Tool(
|
|
857
|
+
name="list_postgres_tables",
|
|
858
|
+
description="List all tables in a PostgreSQL schema. Set vector_only=true to show only pgvector embedding tables, or false (default) to show regular data tables. Use after list_postgres_schemas.",
|
|
859
|
+
inputSchema={
|
|
860
|
+
"type": "object",
|
|
861
|
+
"properties": {
|
|
862
|
+
"database": {"type": "string", "description": "Database name (default: datris)"},
|
|
863
|
+
"schema": {"type": "string", "description": "Schema name (default: public)"},
|
|
864
|
+
"vector_only": {"type": "boolean", "description": "If true, only return tables with an embedding column (pgvector tables). Default: false"},
|
|
865
|
+
},
|
|
866
|
+
}
|
|
867
|
+
),
|
|
868
|
+
Tool(
|
|
869
|
+
name="list_postgres_columns",
|
|
870
|
+
description="List all columns and their data types for a specific PostgreSQL table. Use this to understand table structure before writing a query_postgres SQL query.",
|
|
871
|
+
inputSchema={
|
|
872
|
+
"type": "object",
|
|
873
|
+
"properties": {
|
|
874
|
+
"database": {"type": "string", "description": "Database name (default: datris)"},
|
|
875
|
+
"schema": {"type": "string", "description": "Schema name (default: public)"},
|
|
876
|
+
"table": {"type": "string", "description": "Table name"},
|
|
877
|
+
},
|
|
878
|
+
"required": ["table"]
|
|
879
|
+
}
|
|
880
|
+
),
|
|
881
|
+
Tool(
|
|
882
|
+
name="list_mongodb_databases",
|
|
883
|
+
description="List all MongoDB databases available in the Datris platform. Use this as the first step when exploring what data has been ingested into MongoDB destinations.",
|
|
884
|
+
inputSchema={
|
|
885
|
+
"type": "object",
|
|
886
|
+
"properties": {},
|
|
887
|
+
}
|
|
888
|
+
),
|
|
889
|
+
Tool(
|
|
890
|
+
name="list_mongodb_collections",
|
|
891
|
+
description="List MongoDB collections. If database is specified, lists collections in that database. If omitted, lists all collections across all databases in 'db.collection' format.",
|
|
892
|
+
inputSchema={
|
|
893
|
+
"type": "object",
|
|
894
|
+
"properties": {
|
|
895
|
+
"database": {"type": "string", "description": "Database name (optional; omit to list from all databases)"},
|
|
896
|
+
},
|
|
897
|
+
}
|
|
898
|
+
),
|
|
899
|
+
# --- Vector Store Metadata ---
|
|
900
|
+
Tool(
|
|
901
|
+
name="list_qdrant_collections",
|
|
902
|
+
description="List all collections in the Qdrant vector database. Use this to discover available collections before running search_qdrant.",
|
|
903
|
+
inputSchema={
|
|
904
|
+
"type": "object",
|
|
905
|
+
"properties": {},
|
|
906
|
+
}
|
|
907
|
+
),
|
|
908
|
+
Tool(
|
|
909
|
+
name="list_weaviate_classes",
|
|
910
|
+
description="List all classes in the Weaviate vector database. Use this to discover available classes before running search_weaviate.",
|
|
911
|
+
inputSchema={
|
|
912
|
+
"type": "object",
|
|
913
|
+
"properties": {},
|
|
914
|
+
}
|
|
915
|
+
),
|
|
916
|
+
Tool(
|
|
917
|
+
name="list_milvus_collections",
|
|
918
|
+
description="List all collections in the Milvus vector database. Use this to discover available collections before running search_milvus.",
|
|
919
|
+
inputSchema={
|
|
920
|
+
"type": "object",
|
|
921
|
+
"properties": {},
|
|
922
|
+
}
|
|
923
|
+
),
|
|
924
|
+
Tool(
|
|
925
|
+
name="list_chroma_collections",
|
|
926
|
+
description="List all collections in the Chroma vector database. Use this to discover available collections before running search_chroma.",
|
|
927
|
+
inputSchema={
|
|
928
|
+
"type": "object",
|
|
929
|
+
"properties": {},
|
|
930
|
+
}
|
|
931
|
+
),
|
|
932
|
+
Tool(
|
|
933
|
+
name="list_pgvector_collections",
|
|
934
|
+
description="List all pgvector tables (tables with an embedding column) in PostgreSQL. Use this to discover available collections before running search_pgvector.",
|
|
935
|
+
inputSchema={
|
|
936
|
+
"type": "object",
|
|
937
|
+
"properties": {},
|
|
938
|
+
}
|
|
939
|
+
),
|
|
940
|
+
# --- AI Tools ---
|
|
941
|
+
Tool(
|
|
942
|
+
name="ai_answer",
|
|
943
|
+
description="Ask the Datris AI to answer a question based on provided context. Ideal for RAG workflows: first retrieve relevant chunks using a search tool (search_qdrant, search_pgvector, etc.), then pass the retrieved text as context along with the user's question to get a synthesized answer.",
|
|
944
|
+
inputSchema={
|
|
945
|
+
"type": "object",
|
|
946
|
+
"properties": {
|
|
947
|
+
"query": {"type": "string", "description": "The question to answer"},
|
|
948
|
+
"context": {"type": "string", "description": "Context text to base the answer on (e.g., retrieved document chunks)"},
|
|
949
|
+
},
|
|
950
|
+
"required": ["query", "context"]
|
|
951
|
+
}
|
|
952
|
+
),
|
|
953
|
+
# --- Configuration Tools ---
|
|
954
|
+
Tool(
|
|
955
|
+
name="upload_config",
|
|
956
|
+
description="Upload a configuration file to the Datris platform. Supports two types: 'validation-schema' (JSON Schema files used in pipeline dataQuality schema validation) and 'javascript' (JS files used in pipeline transformation row functions). The file is stored and can be referenced by pipeline configurations.",
|
|
957
|
+
inputSchema={
|
|
958
|
+
"type": "object",
|
|
959
|
+
"properties": {
|
|
960
|
+
"file_path": {"type": "string", "description": "Absolute path to the configuration file to upload"},
|
|
961
|
+
"type": {"type": "string", "enum": ["validation-schema", "javascript"], "description": "Config file type: 'validation-schema' for JSON Schema or 'javascript' for transformation scripts"},
|
|
962
|
+
},
|
|
963
|
+
"required": ["file_path", "type"]
|
|
964
|
+
}
|
|
965
|
+
),
|
|
966
|
+
]
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
@server.call_tool()
|
|
970
|
+
async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
|
|
971
|
+
try:
|
|
972
|
+
result = _dispatch(name, arguments)
|
|
973
|
+
return [TextContent(type="text", text=result)]
|
|
974
|
+
except Exception as e:
|
|
975
|
+
return [TextContent(type="text", text=json.dumps({"error": str(e)}))]
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
def _dispatch(name: str, args: dict) -> str:
|
|
979
|
+
# --- Pipeline Management ---
|
|
980
|
+
if name == "list_pipelines":
|
|
981
|
+
return _call("get", "/api/v1/pipelines")
|
|
982
|
+
|
|
983
|
+
elif name == "get_pipeline":
|
|
984
|
+
return _call("get", "/api/v1/pipeline", params={"pipeline": args["pipeline"]})
|
|
985
|
+
|
|
986
|
+
elif name == "create_pipeline":
|
|
987
|
+
return _call("post", "/api/v1/pipeline", json=args["config"])
|
|
988
|
+
|
|
989
|
+
elif name == "delete_pipeline":
|
|
990
|
+
return _call("delete", "/api/v1/pipeline", params={"pipeline": args["pipeline"]})
|
|
991
|
+
|
|
992
|
+
elif name == "upload_file":
|
|
993
|
+
data = {"pipeline": args["pipeline"]}
|
|
994
|
+
return _upload("/api/v1/pipeline/upload", args["file_path"], data)
|
|
995
|
+
|
|
996
|
+
elif name == "get_job_status":
|
|
997
|
+
params = {}
|
|
998
|
+
if args.get("pipeline_token"):
|
|
999
|
+
params["pipelinetoken"] = args["pipeline_token"]
|
|
1000
|
+
if args.get("pipeline_name"):
|
|
1001
|
+
params["pipelinename"] = args["pipeline_name"]
|
|
1002
|
+
if args.get("page"):
|
|
1003
|
+
params["page"] = args["page"]
|
|
1004
|
+
return _call("get", "/api/v1/pipeline/status", params=params)
|
|
1005
|
+
|
|
1006
|
+
elif name == "kill_job":
|
|
1007
|
+
payload = {"pipelineToken": args["pipeline_token"]}
|
|
1008
|
+
return _call("post", "/api/v1/job/kill", json=payload)
|
|
1009
|
+
|
|
1010
|
+
elif name == "generate_schema":
|
|
1011
|
+
data = {"pipeline": args["pipeline"]}
|
|
1012
|
+
if args.get("delimiter"):
|
|
1013
|
+
data["delimiter"] = args["delimiter"]
|
|
1014
|
+
if args.get("header") is not None:
|
|
1015
|
+
data["header"] = str(args["header"]).lower()
|
|
1016
|
+
return _upload("/api/v1/pipeline/generate", args["file_path"], data)
|
|
1017
|
+
|
|
1018
|
+
elif name == "profile_data":
|
|
1019
|
+
data = {}
|
|
1020
|
+
if args.get("delimiter"):
|
|
1021
|
+
data["delimiter"] = args["delimiter"]
|
|
1022
|
+
if args.get("header") is not None:
|
|
1023
|
+
data["header"] = str(args["header"]).lower()
|
|
1024
|
+
if args.get("sample_size"):
|
|
1025
|
+
data["sampleSize"] = str(args["sample_size"])
|
|
1026
|
+
return _upload("/api/v1/pipeline/profile", args["file_path"], data)
|
|
1027
|
+
|
|
1028
|
+
elif name == "get_version":
|
|
1029
|
+
return _call("get", "/api/v1/version")
|
|
1030
|
+
|
|
1031
|
+
elif name == "check_service_health":
|
|
1032
|
+
return _call("get", "/api/v1/health/services")
|
|
1033
|
+
|
|
1034
|
+
# --- Vector Database Search (via REST API) ---
|
|
1035
|
+
elif name == "search_qdrant":
|
|
1036
|
+
payload = {"query": args["query"]}
|
|
1037
|
+
if args.get("collection"):
|
|
1038
|
+
payload["collection"] = args["collection"]
|
|
1039
|
+
if args.get("top_k"):
|
|
1040
|
+
payload["topK"] = args["top_k"]
|
|
1041
|
+
return _call("post", "/api/v1/search/qdrant", json=payload)
|
|
1042
|
+
|
|
1043
|
+
elif name == "search_weaviate":
|
|
1044
|
+
payload = {"query": args["query"]}
|
|
1045
|
+
if args.get("class_name"):
|
|
1046
|
+
payload["className"] = args["class_name"]
|
|
1047
|
+
if args.get("top_k"):
|
|
1048
|
+
payload["topK"] = args["top_k"]
|
|
1049
|
+
return _call("post", "/api/v1/search/weaviate", json=payload)
|
|
1050
|
+
|
|
1051
|
+
elif name == "search_milvus":
|
|
1052
|
+
payload = {"query": args["query"]}
|
|
1053
|
+
if args.get("collection"):
|
|
1054
|
+
payload["collection"] = args["collection"]
|
|
1055
|
+
if args.get("top_k"):
|
|
1056
|
+
payload["topK"] = args["top_k"]
|
|
1057
|
+
return _call("post", "/api/v1/search/milvus", json=payload)
|
|
1058
|
+
|
|
1059
|
+
elif name == "search_pgvector":
|
|
1060
|
+
payload = {"query": args["query"]}
|
|
1061
|
+
if args.get("table"):
|
|
1062
|
+
payload["table"] = args["table"]
|
|
1063
|
+
if args.get("schema"):
|
|
1064
|
+
payload["schema"] = args["schema"]
|
|
1065
|
+
if args.get("top_k"):
|
|
1066
|
+
payload["topK"] = args["top_k"]
|
|
1067
|
+
return _call("post", "/api/v1/search/pgvector", json=payload)
|
|
1068
|
+
|
|
1069
|
+
elif name == "search_chroma":
|
|
1070
|
+
payload = {"query": args["query"]}
|
|
1071
|
+
if args.get("collection"):
|
|
1072
|
+
payload["collection"] = args["collection"]
|
|
1073
|
+
if args.get("top_k"):
|
|
1074
|
+
payload["topK"] = args["top_k"]
|
|
1075
|
+
return _call("post", "/api/v1/search/chroma", json=payload)
|
|
1076
|
+
|
|
1077
|
+
# --- Database Queries (via REST API) ---
|
|
1078
|
+
elif name == "query_postgres":
|
|
1079
|
+
payload = {"sql": args["sql"]}
|
|
1080
|
+
if args.get("limit"):
|
|
1081
|
+
payload["limit"] = args["limit"]
|
|
1082
|
+
return _call("post", "/api/v1/query/postgres", json=payload)
|
|
1083
|
+
|
|
1084
|
+
elif name == "query_mongodb":
|
|
1085
|
+
payload = {"collection": args["collection"]}
|
|
1086
|
+
if args.get("filter"):
|
|
1087
|
+
payload["filter"] = args["filter"]
|
|
1088
|
+
if args.get("projection"):
|
|
1089
|
+
payload["projection"] = args["projection"]
|
|
1090
|
+
if args.get("limit"):
|
|
1091
|
+
payload["limit"] = args["limit"]
|
|
1092
|
+
return _call("post", "/api/v1/query/mongodb", json=payload)
|
|
1093
|
+
|
|
1094
|
+
# --- Metadata Discovery (via REST API) ---
|
|
1095
|
+
elif name == "list_postgres_databases":
|
|
1096
|
+
return _call("get", "/api/v1/metadata/postgres/databases")
|
|
1097
|
+
|
|
1098
|
+
elif name == "list_postgres_schemas":
|
|
1099
|
+
params = {}
|
|
1100
|
+
if args.get("database"):
|
|
1101
|
+
params["database"] = args["database"]
|
|
1102
|
+
return _call("get", "/api/v1/metadata/postgres/schemas", params=params)
|
|
1103
|
+
|
|
1104
|
+
elif name == "list_postgres_tables":
|
|
1105
|
+
params = {}
|
|
1106
|
+
if args.get("database"):
|
|
1107
|
+
params["database"] = args["database"]
|
|
1108
|
+
if args.get("schema"):
|
|
1109
|
+
params["schema"] = args["schema"]
|
|
1110
|
+
if args.get("vector_only") is not None:
|
|
1111
|
+
params["vectorOnly"] = str(args["vector_only"]).lower()
|
|
1112
|
+
return _call("get", "/api/v1/metadata/postgres/tables", params=params)
|
|
1113
|
+
|
|
1114
|
+
elif name == "list_postgres_columns":
|
|
1115
|
+
params = {"table": args["table"]}
|
|
1116
|
+
if args.get("database"):
|
|
1117
|
+
params["database"] = args["database"]
|
|
1118
|
+
if args.get("schema"):
|
|
1119
|
+
params["schema"] = args["schema"]
|
|
1120
|
+
return _call("get", "/api/v1/metadata/postgres/columns", params=params)
|
|
1121
|
+
|
|
1122
|
+
elif name == "list_mongodb_databases":
|
|
1123
|
+
return _call("get", "/api/v1/metadata/mongodb/databases")
|
|
1124
|
+
|
|
1125
|
+
elif name == "list_mongodb_collections":
|
|
1126
|
+
params = {}
|
|
1127
|
+
if args.get("database"):
|
|
1128
|
+
params["database"] = args["database"]
|
|
1129
|
+
return _call("get", "/api/v1/metadata/mongodb/collections", params=params)
|
|
1130
|
+
|
|
1131
|
+
# --- Vector Store Metadata ---
|
|
1132
|
+
elif name == "list_qdrant_collections":
|
|
1133
|
+
return _call("get", "/api/v1/metadata/qdrant/collections")
|
|
1134
|
+
|
|
1135
|
+
elif name == "list_weaviate_classes":
|
|
1136
|
+
return _call("get", "/api/v1/metadata/weaviate/classes")
|
|
1137
|
+
|
|
1138
|
+
elif name == "list_milvus_collections":
|
|
1139
|
+
return _call("get", "/api/v1/metadata/milvus/collections")
|
|
1140
|
+
|
|
1141
|
+
elif name == "list_chroma_collections":
|
|
1142
|
+
return _call("get", "/api/v1/metadata/chroma/collections")
|
|
1143
|
+
|
|
1144
|
+
elif name == "list_pgvector_collections":
|
|
1145
|
+
return _call("get", "/api/v1/metadata/postgres/tables", params={"vectorOnly": "true"})
|
|
1146
|
+
|
|
1147
|
+
# --- AI ---
|
|
1148
|
+
elif name == "ai_answer":
|
|
1149
|
+
payload = {"query": args["query"], "context": args["context"]}
|
|
1150
|
+
return _call("post", "/api/v1/ai/answer", json=payload)
|
|
1151
|
+
|
|
1152
|
+
# --- Config ---
|
|
1153
|
+
elif name == "upload_config":
|
|
1154
|
+
data = {"type": args["type"]}
|
|
1155
|
+
return _upload("/api/v1/config/upload", args["file_path"], data)
|
|
1156
|
+
|
|
1157
|
+
else:
|
|
1158
|
+
return json.dumps({"error": f"Unknown tool: {name}"})
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
# ---------------------------------------------------------------------------
|
|
1162
|
+
# Main
|
|
1163
|
+
# ---------------------------------------------------------------------------
|
|
1164
|
+
|
|
1165
|
+
async def run_stdio():
|
|
1166
|
+
from mcp.server.stdio import stdio_server
|
|
1167
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
1168
|
+
await server.run(read_stream, write_stream, server.create_initialization_options())
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
async def run_sse(port: int):
|
|
1172
|
+
from mcp.server.sse import SseServerTransport
|
|
1173
|
+
from starlette.applications import Starlette
|
|
1174
|
+
from starlette.routing import Route
|
|
1175
|
+
import uvicorn
|
|
1176
|
+
|
|
1177
|
+
sse = SseServerTransport("/messages")
|
|
1178
|
+
|
|
1179
|
+
async def handle_sse(request):
|
|
1180
|
+
async with sse.connect_sse(request.scope, request.receive, request._send) as streams:
|
|
1181
|
+
await server.run(streams[0], streams[1], server.create_initialization_options())
|
|
1182
|
+
|
|
1183
|
+
async def handle_messages(request):
|
|
1184
|
+
await sse.handle_post_message(request.scope, request.receive, request._send)
|
|
1185
|
+
|
|
1186
|
+
app = Starlette(routes=[
|
|
1187
|
+
Route("/sse", endpoint=handle_sse),
|
|
1188
|
+
Route("/messages", endpoint=handle_messages, methods=["POST"]),
|
|
1189
|
+
])
|
|
1190
|
+
|
|
1191
|
+
config = uvicorn.Config(app, host="0.0.0.0", port=port)
|
|
1192
|
+
srv = uvicorn.Server(config)
|
|
1193
|
+
await srv.serve()
|
|
1194
|
+
|
|
1195
|
+
|
|
1196
|
+
def main():
|
|
1197
|
+
import asyncio
|
|
1198
|
+
|
|
1199
|
+
parser = argparse.ArgumentParser(description="Datris MCP Server")
|
|
1200
|
+
parser.add_argument("--sse", action="store_true", help="Run in SSE mode (default: stdio)")
|
|
1201
|
+
parser.add_argument("--port", type=int, default=3000, help="SSE port (default: 3000)")
|
|
1202
|
+
args = parser.parse_args()
|
|
1203
|
+
|
|
1204
|
+
if args.sse:
|
|
1205
|
+
asyncio.run(run_sse(args.port))
|
|
1206
|
+
else:
|
|
1207
|
+
asyncio.run(run_stdio())
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
if __name__ == "__main__":
|
|
1211
|
+
main()
|