zaturn 0.2.0__py3-none-any.whl → 0.2.2__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.
- zaturn/mcp/__init__.py +13 -10
- zaturn/tools/config.py +0 -83
- zaturn/tools/query_utils.py +13 -1
- {zaturn-0.2.0.dist-info → zaturn-0.2.2.dist-info}/METADATA +4 -4
- {zaturn-0.2.0.dist-info → zaturn-0.2.2.dist-info}/RECORD +9 -9
- {zaturn-0.2.0.dist-info → zaturn-0.2.2.dist-info}/WHEEL +0 -0
- {zaturn-0.2.0.dist-info → zaturn-0.2.2.dist-info}/entry_points.txt +0 -0
- {zaturn-0.2.0.dist-info → zaturn-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {zaturn-0.2.0.dist-info → zaturn-0.2.2.dist-info}/top_level.txt +0 -0
zaturn/mcp/__init__.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
import argparse
|
2
|
+
import importlib.resources
|
2
3
|
import os
|
3
4
|
import platformdirs
|
4
|
-
import pkg_resources
|
5
5
|
import sys
|
6
6
|
|
7
7
|
from fastmcp import FastMCP
|
8
|
+
from fastmcp.tools.tool import Tool
|
8
9
|
|
9
10
|
from zaturn.tools import ZaturnTools
|
10
11
|
|
@@ -30,19 +31,17 @@ if not source_list:
|
|
30
31
|
source_list = args.sources
|
31
32
|
|
32
33
|
if not source_list:
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
]
|
34
|
+
with importlib.resources.path(
|
35
|
+
'zaturn.tools.example_data', 'all_pokemon_data.csv'
|
36
|
+
) as source_path:
|
37
|
+
source_list = [str(source_path)]
|
38
|
+
|
39
39
|
print("No data sources provided. Loading example dataset for demonstration.")
|
40
40
|
print(f"\nTo load your datasets, add them to {SOURCES_FILE} (one source URL or full file path per line)")
|
41
41
|
print("\nOr use command line args to specify data sources:")
|
42
42
|
print("zaturn_mcp sqlite:///path/to/mydata.db /path/to/my_file.csv")
|
43
43
|
print(f"\nNOTE: Sources in command line args will be ignored if sources are found in {SOURCES_FILE}")
|
44
44
|
|
45
|
-
|
46
45
|
SOURCES = {}
|
47
46
|
for s in source_list:
|
48
47
|
source = s.lower()
|
@@ -56,6 +55,10 @@ for s in source_list:
|
|
56
55
|
source_type = 'mysql'
|
57
56
|
s = s.replace('mysql://', 'mysql+pymysql://')
|
58
57
|
source_name = source.split('/')[-1].split('?')[0]
|
58
|
+
elif source.startswith("mssql://"):
|
59
|
+
source_type = 'mssql'
|
60
|
+
s = s.replace('mssql://', 'mssql+pymssql://')
|
61
|
+
source_name = source.split('/')[-1].split('?')[0]
|
59
62
|
elif source.startswith('clickhouse://'):
|
60
63
|
source_type = 'clickhouse'
|
61
64
|
source_name = source.split('/')[-1].split('?')[0]
|
@@ -86,8 +89,8 @@ for s in source_list:
|
|
86
89
|
def ZaturnMCP(sources):
|
87
90
|
zaturn_tools = ZaturnTools(sources)
|
88
91
|
zaturn_mcp = FastMCP()
|
89
|
-
for
|
90
|
-
zaturn_mcp.add_tool(
|
92
|
+
for tool_function in zaturn_tools.tools:
|
93
|
+
zaturn_mcp.add_tool(Tool.from_function(tool_function))
|
91
94
|
|
92
95
|
return zaturn_mcp
|
93
96
|
|
zaturn/tools/config.py
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
import argparse
|
2
1
|
import os
|
3
2
|
import platformdirs
|
4
|
-
import pkg_resources
|
5
|
-
import sys
|
6
3
|
|
7
4
|
# Basic Setup
|
8
5
|
USER_DATA_DIR = platformdirs.user_data_dir('zaturn', 'zaturn')
|
@@ -13,83 +10,3 @@ SOURCES_FILE = os.path.join(USER_DATA_DIR, 'sources.txt')
|
|
13
10
|
os.makedirs(QUERIES_DIR, exist_ok=True)
|
14
11
|
os.makedirs(VISUALS_DIR, exist_ok=True)
|
15
12
|
|
16
|
-
# Parse command line args
|
17
|
-
parser = argparse.ArgumentParser(
|
18
|
-
description="Zaturn: A read-only BI tool for analyzing various data sources"
|
19
|
-
)
|
20
|
-
parser.add_argument('--noimg', action='store_const',
|
21
|
-
const=True, default=False,
|
22
|
-
help='Return image file paths instead of images for visuals. Use when MCP client cannot render images.',
|
23
|
-
)
|
24
|
-
parser.add_argument('sources', nargs=argparse.REMAINDER, default=[],
|
25
|
-
help='Data source (can be specified multiple times). Can be SQLite, MySQL, PostgreSQL connection string, or a path to CSV, Parquet, or DuckDB file.'
|
26
|
-
)
|
27
|
-
args = parser.parse_args()
|
28
|
-
|
29
|
-
# Read and parse sources
|
30
|
-
source_list = []
|
31
|
-
if os.path.exists(SOURCES_FILE):
|
32
|
-
with open(SOURCES_FILE) as f:
|
33
|
-
source_list = [line.strip('\n') for line in f.readlines() if line.strip('\n')]
|
34
|
-
|
35
|
-
if not source_list:
|
36
|
-
source_list = args.sources
|
37
|
-
|
38
|
-
if not source_list:
|
39
|
-
source_list = [
|
40
|
-
pkg_resources.resource_filename(
|
41
|
-
'zaturn',
|
42
|
-
os.path.join('mcp', 'example_data', 'all_pokemon_data.csv')
|
43
|
-
)
|
44
|
-
]
|
45
|
-
print("No data sources provided. Loading example dataset for demonstration.")
|
46
|
-
print(f"\nTo load your datasets, add them to {SOURCES_FILE} (one source URL or full file path per line)")
|
47
|
-
print("\nOr use command line args to specify data sources:")
|
48
|
-
print("zaturn_mcp sqlite:///path/to/mydata.db /path/to/my_file.csv")
|
49
|
-
print(f"\nNOTE: Sources in command line args will be ignored if sources are found in {SOURCES_FILE}")
|
50
|
-
|
51
|
-
CLI_SOURCES = {}
|
52
|
-
for s in source_list:
|
53
|
-
source = s.lower()
|
54
|
-
if source.startswith('sqlite://'):
|
55
|
-
source_type = 'sqlite'
|
56
|
-
source_name = source.split('/')[-1].split('?')[0].split('.db')[0]
|
57
|
-
elif source.startswith('postgresql://'):
|
58
|
-
source_type = 'postgresql'
|
59
|
-
source_name = source.split('/')[-1].split('?')[0]
|
60
|
-
elif source.startswith("mysql://") or source.startswith("mysql+pymysql://"):
|
61
|
-
source_type = 'mysql'
|
62
|
-
s = s.replace('mysql://', 'mysql+pymysql://')
|
63
|
-
source_name = source.split('/')[-1].split('?')[0]
|
64
|
-
elif source.startswith('clickhouse://'):
|
65
|
-
source_type = 'clickhouse'
|
66
|
-
source_name = source.split('/')[-1].split('?')[0]
|
67
|
-
elif source.endswith(".duckdb"):
|
68
|
-
source_type = "duckdb"
|
69
|
-
source_name = source.split('/')[-1].split('.')[0]
|
70
|
-
elif source.endswith(".csv"):
|
71
|
-
source_type = "csv"
|
72
|
-
source_name = source.split('/')[-1].split('.')[0]
|
73
|
-
elif source.endswith(".parquet") or source.endswith(".pq"):
|
74
|
-
source_type = "parquet"
|
75
|
-
source_name = source.split('/')[-1].split('.')[0]
|
76
|
-
else:
|
77
|
-
continue
|
78
|
-
|
79
|
-
source_id = f'{source_name}-{source_type}'
|
80
|
-
if source_id in CLI_SOURCES:
|
81
|
-
i = 2
|
82
|
-
while True:
|
83
|
-
source_id = f'{source_name}{i}-{source_type}'
|
84
|
-
if source_id not in SOURCES:
|
85
|
-
break
|
86
|
-
i += 1
|
87
|
-
|
88
|
-
CLI_SOURCES[source_id] = {'url': s, 'type': source_type}
|
89
|
-
|
90
|
-
|
91
|
-
# Other Settings
|
92
|
-
CLI_RETURN_IMAGES = not args.noimg
|
93
|
-
|
94
|
-
|
95
|
-
|
zaturn/tools/query_utils.py
CHANGED
@@ -30,6 +30,10 @@ def list_tables(source):
|
|
30
30
|
for col in list(result):
|
31
31
|
if col.startswith("Tables_in_"):
|
32
32
|
return result[col].to_list()
|
33
|
+
|
34
|
+
case "mssql":
|
35
|
+
result = execute_query(source, "SELECT name FROM sys.tables")
|
36
|
+
return result['name'].to_list()
|
33
37
|
|
34
38
|
case "duckdb" | "csv" | "parquet" | "clickhouse":
|
35
39
|
result = execute_query(source, "SHOW TABLES")
|
@@ -46,7 +50,7 @@ def describe_table(source, table_name):
|
|
46
50
|
f'PRAGMA table_info("{table_name}");'
|
47
51
|
)
|
48
52
|
|
49
|
-
case 'postgresql':
|
53
|
+
case 'postgresql' | 'mssql':
|
50
54
|
return execute_query(source,
|
51
55
|
f"SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '{table_name}';"
|
52
56
|
)
|
@@ -81,6 +85,14 @@ def execute_query(source: dict, query: str):
|
|
81
85
|
result = session.execute(sqlalchemy.text(query))
|
82
86
|
return pd.DataFrame(result)
|
83
87
|
|
88
|
+
case "mssql":
|
89
|
+
engine = sqlalchemy.create_engine(url)
|
90
|
+
with Session(engine) as session:
|
91
|
+
# no known way to ensure read-only here
|
92
|
+
# please use read-only credentials
|
93
|
+
result = session.execute(sqlalchemy.text(query))
|
94
|
+
return pd.DataFrame(result)
|
95
|
+
|
84
96
|
case "postgresql":
|
85
97
|
engine = sqlalchemy.create_engine(url)
|
86
98
|
with engine.connect() as conn:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: zaturn
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.2
|
4
4
|
Summary: AI Data Analysis MCP & Studio
|
5
5
|
Author-email: Karthik Devan <krtdvn@gmail.com>
|
6
6
|
Maintainer-email: Karthik Devan <krtdvn@gmail.com>
|
@@ -25,6 +25,7 @@ Requires-Dist: platformdirs>=4.3.7
|
|
25
25
|
Requires-Dist: plotly[express]>=6.0.1
|
26
26
|
Requires-Dist: psycopg2-binary>=2.9.10
|
27
27
|
Requires-Dist: pyarrow>=19.0.1
|
28
|
+
Requires-Dist: pymssql>=2.3.7
|
28
29
|
Requires-Dist: pymysql>=1.1.1
|
29
30
|
Requires-Dist: python-lsp-server>=1.12.2
|
30
31
|
Requires-Dist: python-slugify>=8.0.4
|
@@ -58,7 +59,7 @@ https://github.com/user-attachments/assets/d42dc433-e5ec-4b3e-bef0-5cfc097396ab
|
|
58
59
|
### Multiple Data Sources
|
59
60
|
|
60
61
|
Zaturn can currently connect to the following data sources:
|
61
|
-
- SQL Databases: PostgreSQL, SQLite, DuckDB, MySQL, ClickHouse
|
62
|
+
- SQL Databases: PostgreSQL, SQLite, DuckDB, MySQL, ClickHouse, SQL Server
|
62
63
|
- Files: CSV, Parquet
|
63
64
|
|
64
65
|
Connectors for more data sources are being added.
|
@@ -77,7 +78,7 @@ More visualization capabilities are being added.
|
|
77
78
|
|
78
79
|
## Installation & Setup
|
79
80
|
|
80
|
-
See [https://zaturn.pro/install](https://zaturn.pro/install)
|
81
|
+
See [https://zaturn.pro/docs/install](https://zaturn.pro/docs/install)
|
81
82
|
|
82
83
|
|
83
84
|
## Roadmap
|
@@ -97,7 +98,6 @@ Analyst:
|
|
97
98
|
I can get it done right now. Actually, you can do it right now.
|
98
99
|
You know what? The boss can do it right now.
|
99
100
|
```
|
100
|
-
- A native notebook interface
|
101
101
|
|
102
102
|
## Help And Feedback
|
103
103
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
zaturn/mcp/__init__.py,sha256
|
1
|
+
zaturn/mcp/__init__.py,sha256=8pTcOyJ3tK7TJcP4HBDVQA_MVuMrfYJwa8sWKfCvXIE,3466
|
2
2
|
zaturn/studio/__init__.py,sha256=A1QxWydtsE0VmsJlvF7O9Fx1-WKjV3MBPje7knFSDzw,83
|
3
3
|
zaturn/studio/agent_wrapper.py,sha256=4mEMRfE1Tfv63D7OIabqUbEJdIgO2yo8UXO6PKvNrmI,4603
|
4
4
|
zaturn/studio/app.py,sha256=LEoSjs5wpZGX1B7pcJ2e_6CaxOFjbO6JitxlkJv__30,9208
|
@@ -26,14 +26,14 @@ zaturn/studio/templates/setup_prompt.html,sha256=K8l3ewlEyrUFiMmw2qPyDFbFNkq1RCB
|
|
26
26
|
zaturn/studio/templates/user_message.html,sha256=WbvdVr4AVzVx6MG0eQ_ulMeHB9rpScZc-EZlCoYFEgw,90
|
27
27
|
zaturn/studio/templates/css/style.css,sha256=cKImLAjLp6XjHdT16v9yJS0DHG2wWx8djR18SHfMqvE,6264
|
28
28
|
zaturn/tools/__init__.py,sha256=vOcHneRuZll9k-2e3sra7m8qfdc4IoAFYc2g_xK79HE,262
|
29
|
-
zaturn/tools/config.py,sha256=
|
29
|
+
zaturn/tools/config.py,sha256=Ia-WzK_G6vQqB7norvaffb3fKLjtmXupVqAWsxdu6Gs,354
|
30
30
|
zaturn/tools/core.py,sha256=4fHxSIbO22cTIejJk02GzUBfWimh1Ql9mZ3JgI1qLBY,2893
|
31
|
-
zaturn/tools/query_utils.py,sha256=
|
31
|
+
zaturn/tools/query_utils.py,sha256=OnXbONmEgsHcScrJ76b7P3BRLOKgi8TMkhxBcLQK9cY,5306
|
32
32
|
zaturn/tools/visualizations.py,sha256=aPXCN1YIjCz6uiwJjduKCiLLtnBFX92hTvjy5EqQYk8,9509
|
33
33
|
zaturn/tools/example_data/all_pokemon_data.csv,sha256=SUlGHHWbehuLg-ch1YUrQ6-xBtqHGw6rIkyn70fAgCk,130893
|
34
|
-
zaturn-0.2.
|
35
|
-
zaturn-0.2.
|
36
|
-
zaturn-0.2.
|
37
|
-
zaturn-0.2.
|
38
|
-
zaturn-0.2.
|
39
|
-
zaturn-0.2.
|
34
|
+
zaturn-0.2.2.dist-info/licenses/LICENSE,sha256=mZSuFlbEBZGl0-8ULRMLdRDbhau5hrWRNQOjytYeaug,1070
|
35
|
+
zaturn-0.2.2.dist-info/METADATA,sha256=BSnrqH9k7MNfqmRVPEO6q3884y-8Ym1X6ctklZz6NZc,3978
|
36
|
+
zaturn-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
37
|
+
zaturn-0.2.2.dist-info/entry_points.txt,sha256=MWMWX0dE_ZQM4StGKCLxJym_D91F82RcBi2LBj0hEho,82
|
38
|
+
zaturn-0.2.2.dist-info/top_level.txt,sha256=KLUnwQwVZkfd5YCnnqR35MOOs8KLhanPGelvmRo2MVA,7
|
39
|
+
zaturn-0.2.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|