aigroup-stata-mcp 1.0.3__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.
Files changed (38) hide show
  1. aigroup_stata_mcp-1.0.3.dist-info/METADATA +345 -0
  2. aigroup_stata_mcp-1.0.3.dist-info/RECORD +38 -0
  3. aigroup_stata_mcp-1.0.3.dist-info/WHEEL +4 -0
  4. aigroup_stata_mcp-1.0.3.dist-info/entry_points.txt +5 -0
  5. aigroup_stata_mcp-1.0.3.dist-info/licenses/LICENSE +21 -0
  6. stata_mcp/__init__.py +18 -0
  7. stata_mcp/cli/__init__.py +8 -0
  8. stata_mcp/cli/_cli.py +95 -0
  9. stata_mcp/core/__init__.py +14 -0
  10. stata_mcp/core/data_info/__init__.py +11 -0
  11. stata_mcp/core/data_info/_base.py +288 -0
  12. stata_mcp/core/data_info/csv.py +123 -0
  13. stata_mcp/core/data_info/dta.py +70 -0
  14. stata_mcp/core/stata/__init__.py +13 -0
  15. stata_mcp/core/stata/stata_controller/__init__.py +9 -0
  16. stata_mcp/core/stata/stata_controller/controller.py +208 -0
  17. stata_mcp/core/stata/stata_do/__init__.py +9 -0
  18. stata_mcp/core/stata/stata_do/do.py +177 -0
  19. stata_mcp/core/stata/stata_finder/__init__.py +9 -0
  20. stata_mcp/core/stata/stata_finder/base.py +294 -0
  21. stata_mcp/core/stata/stata_finder/finder.py +193 -0
  22. stata_mcp/core/stata/stata_finder/linux.py +43 -0
  23. stata_mcp/core/stata/stata_finder/macos.py +88 -0
  24. stata_mcp/core/stata/stata_finder/windows.py +191 -0
  25. stata_mcp/server/__init__.py +8 -0
  26. stata_mcp/server/main.py +153 -0
  27. stata_mcp/server/prompts/__init__.py +8 -0
  28. stata_mcp/server/prompts/core_prompts.py +122 -0
  29. stata_mcp/server/tools/__init__.py +10 -0
  30. stata_mcp/server/tools/core_tools.py +59 -0
  31. stata_mcp/server/tools/file_tools.py +163 -0
  32. stata_mcp/server/tools/stata_tools.py +221 -0
  33. stata_mcp/utils/Installer/__init__.py +7 -0
  34. stata_mcp/utils/Installer/installer.py +85 -0
  35. stata_mcp/utils/Prompt/__init__.py +74 -0
  36. stata_mcp/utils/Prompt/string.py +91 -0
  37. stata_mcp/utils/__init__.py +23 -0
  38. stata_mcp/utils/usable.py +244 -0
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ from .string import frame
6
+
7
+
8
+ class Prompt:
9
+
10
+
11
+
12
+ def __init__(self):
13
+ """简要描述函数功能"""
14
+ self.prompts = {}
15
+ self.lang = "en"
16
+
17
+ def set_lang(self, lang):
18
+ """简要描述函数功能"""
19
+ self.lang = lang
20
+
21
+ def add_prompt(self, prompt_id: str, lang: str, prompt: str):
22
+ """简要描述函数功能"""
23
+ if prompt_id not in self.prompts:
24
+ self.prompts[prompt_id] = {}
25
+ self.prompts[prompt_id][lang] = prompt
26
+
27
+ def get_prompt(self, prompt_id: str, lang: str = None):
28
+ """简要描述函数功能"""
29
+ if lang is None:
30
+ lang = self.lang
31
+
32
+ if prompt_id not in self.prompts:
33
+ return ""
34
+
35
+ if lang not in self.prompts[prompt_id]:
36
+ lang = "en"
37
+ if lang not in self.prompts[prompt_id]:
38
+ return ""
39
+ return self.prompts[prompt_id][lang]
40
+
41
+ @staticmethod
42
+ def extract(var_name: str):
43
+ """简要描述函数功能"""
44
+ name_list = var_name.split("_")
45
+ lang = name_list[-1]
46
+ prompt_id = "_".join(name_list[:-1])
47
+ return prompt_id, lang
48
+
49
+ def auto_extract(self, prompts_dict: dict):
50
+ """简要描述函数功能"""
51
+ for key, prompt in prompts_dict.items():
52
+ prompt_id, lang = Prompt.extract(key)
53
+ self.add_prompt(prompt_id=prompt_id, lang=lang, prompt=prompt)
54
+
55
+
56
+ def filter_system_vars(dictionary):
57
+ """简要描述函数功能"""
58
+ exclude_prefixes = ["__"]
59
+ exclude_vars = ["inspect", "frame"]
60
+
61
+ filtered_dict = {}
62
+ for key, value in dictionary.items():
63
+ if (
64
+ not any(key.startswith(prefix) for prefix in exclude_prefixes)
65
+ and key not in exclude_vars
66
+ ):
67
+ filtered_dict[key] = value
68
+ return filtered_dict
69
+
70
+
71
+ prompts_dict: dict = filter_system_vars(frame.f_locals)
72
+
73
+ pmp = Prompt()
74
+ pmp.auto_extract(prompts_dict)
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ import inspect
6
+
7
+ frame = inspect.currentframe()
8
+
9
+ stata_assistant_role_en: str = """
10
+ You will play the role of an economics research assistant with strong programming abilities. Stata is a very simple and familiar tool for you.
11
+
12
+ You should view the user as an economist with strong economic intuition but unfamiliar with Stata operations, making your collaboration the strongest economics research team.
13
+
14
+ Your task is to generate Stata code based on the user's instructions, adding comments before each line of code, and then run this dofile.
15
+ The user will provide you with a data path and their research story or regression model.
16
+ What you need to do is understand the data structure based on the data path, and then write Stata regression code according to the user's model.
17
+
18
+ Your output should tell the user how the results look, whether they meet the user's expectations, and inform them of the locations of the dofile and log file.
19
+ """
20
+
21
+ stata_assistant_role_cn: str = """
22
+ 你将扮演一个经济学的研究助理,你有很强的编程能力,Stata在你这里是一个非常简单非常家常的工具。
23
+
24
+ 你应该把用户视为一个经济直觉很强但是不熟悉Stata操作的经济学家,因此你们合作就是最强的经济学研究组合。
25
+
26
+ 你的任务是根据用户的指令去生成Stata代码,并在每行代码前加上注释,然后运行这个dofile。
27
+ 用户会给你一个数据的路径和他的研究故事或者回归模型,
28
+ 而你需要做的是根据数据路径去了解数据结构,然后根据用户的模型去写Stata的回归代码。
29
+
30
+ 你的输出应该是告诉用户这个结果如何,是否是符合用户预期的,并把dofile和log文件的位置都告诉用户。
31
+ """
32
+
33
+ stata_analysis_strategy_en: str = """
34
+ When conducting data analysis using Stata, please follow these strategies:
35
+
36
+ 1. Data preparation and exploration:
37
+ - First use get_data_info() to understand the basic characteristics of the dataset, including variable types, missing values, and distributions
38
+ - Ensure you understand the meaning of each variable and possible encoding methods
39
+ - Assess whether data cleaning, variable transformation, or missing value handling is needed
40
+
41
+ 2. Code generation and execution workflow:
42
+ - Break down the analysis into multiple logical steps, each with a clear objective
43
+ - Use write_dofile() to create the initial do file
44
+ - For complex analyses, first run the basic steps, then use append_dofile() to add more analyses
45
+ - Execute with stata_do() and check results after each modification
46
+
47
+ 3. Results management:
48
+ - Use results_doc_path() to get a unified storage path for results before generating tables or outputs
49
+ - Save this path in the do file using the local output_path command
50
+ - Use commands like outreg2 or esttab to output results to the specified path
51
+
52
+ 4. Reporting results:
53
+ - After executing the do file, use read_log() to view execution results and possible errors
54
+ - Analyze and interpret important statistical results
55
+ - Provide context and explanation of the meaning of results
56
+
57
+ 5. Handling common issues:
58
+ - If syntax errors occur, first check if variable names are correct
59
+ - Check if the dataset has been properly loaded
60
+ - For large datasets, consider using a subsample for preliminary analysis
61
+ """
62
+
63
+ stata_analysis_strategy_cn: str = """
64
+ 使用Stata进行数据分析时,请遵循以下策略:
65
+
66
+ 1. 数据准备和探索:
67
+ - 首先使用get_data_info()了解数据集的基本情况,包括变量类型、缺失值和分布
68
+ - 确保理解每个变量的意义和可能的编码方式
69
+ - 评估是否需要数据清洗、变量转换或缺失值处理
70
+
71
+ 2. 代码生成和执行流程:
72
+ - 将分析分解为多个逻辑步骤,每个步骤都有明确的目标
73
+ - 使用write_dofile()创建初始do文件
74
+ - 对于复杂分析,先运行基础步骤,然后使用append_dofile()添加更多分析
75
+ - 每次修改后使用stata_do()执行并检查结果
76
+
77
+ 3. 结果管理:
78
+ - 在生成表格或输出结果前使用results_doc_path()获取统一的结果存储路径
79
+ - 在do文件中使用local output_path命令保存此路径
80
+ - 使用outreg2或esttab等命令将结果输出到指定路径
81
+
82
+ 4. 报告结果:
83
+ - 执行do文件后使用read_log()查看执行结果和可能的错误
84
+ - 分析并解释重要的统计结果
85
+ - 提供结果的上下文和含义解释
86
+
87
+ 5. 常见问题处理:
88
+ - 如果出现语法错误,先检查变量名称是否正确
89
+ - 检查数据集是否已正确加载
90
+ - 针对大型数据集,考虑使用子样本进行初步分析
91
+ """
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ import platform
6
+ from datetime import datetime
7
+
8
+
9
+ def set_config(key, value):
10
+ """简要描述函数功能"""
11
+ with open(".env", "w+", encoding="utf-8") as f:
12
+ f.write(f"{key}={value}")
13
+ return {key: value}
14
+
15
+
16
+ def get_nowtime():
17
+ """简要描述函数功能"""
18
+ return datetime.strftime(datetime.now(), "%Y%m%d%H%M%S")
19
+
20
+
21
+ def get_os():
22
+ """简要描述函数功能"""
23
+ return platform.system()
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+
5
+ """
6
+ Stata MCP Configuration Check Script
7
+ This script automatically checks if your Stata MCP configuration is correct
8
+ """
9
+
10
+ import os
11
+ import platform
12
+ import subprocess
13
+ import sys
14
+ import time
15
+ from typing import Dict, Tuple
16
+
17
+ from ..core.stata import StataFinder
18
+
19
+
20
+ def print_status(message: str, status: bool) -> None:
21
+ """Print a message with status indicator"""
22
+ status_str = "✅ PASSED" if status else "❌ FAILED"
23
+ print(f"{message}: {status_str}")
24
+
25
+
26
+ def check_os() -> Tuple[str, bool]:
27
+ """Check current operating system"""
28
+ os_name = platform.system()
29
+ os_mapping = {"Darwin": "macOS", "Windows": "Windows", "Linux": "Linux"}
30
+ detected_os = os_mapping.get(os_name, "unknown")
31
+ is_supported = detected_os in [
32
+ "macOS",
33
+ "Windows",
34
+ "Linux",
35
+ ] # All three are now supported
36
+
37
+ return detected_os, is_supported
38
+
39
+
40
+ def check_python_version() -> Tuple[str, bool]:
41
+ """Check if the Python version is compatible"""
42
+ current_version = (
43
+ f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
44
+ )
45
+ is_compatible = sys.version_info.major == 3 and sys.version_info.minor >= 11
46
+
47
+ return current_version, is_compatible
48
+
49
+
50
+ def test_stata_execution(stata_cli_path: str) -> bool:
51
+ """Test if Stata can be executed"""
52
+ if not stata_cli_path or not os.path.exists(stata_cli_path):
53
+ return False
54
+
55
+ sys_os = platform.system()
56
+
57
+ try:
58
+ if sys_os == "Darwin" or sys_os == "Linux": # macOS or Linux
59
+ proc = subprocess.Popen(
60
+ [stata_cli_path],
61
+ stdin=subprocess.PIPE,
62
+ stdout=subprocess.PIPE,
63
+ stderr=subprocess.PIPE,
64
+ text=True,
65
+ shell=True,
66
+ )
67
+ # Send a simple command and exit
68
+ commands = """
69
+ display "Stata-MCP test successful"
70
+ exit, STATA
71
+ """
72
+ proc.communicate(input=commands, timeout=10)
73
+ return proc.returncode == 0
74
+
75
+ elif sys_os == "Windows": # Windows
76
+ # Create a temporary do-file for testing
77
+ temp_dir = os.path.dirname(os.path.abspath(__file__))
78
+ temp_do_file = os.path.join(temp_dir, "temp_test.do")
79
+
80
+ with open(temp_do_file, "w") as f:
81
+ f.write('display "Stata-MCP test successful"\nexit, STATA\n')
82
+
83
+ # Run Stata with the temp do-file
84
+ cmd = f'"{stata_cli_path}" /e do "{temp_do_file}"'
85
+ result = subprocess.run(cmd, shell=True, timeout=10)
86
+
87
+ # Clean up
88
+ if os.path.exists(temp_do_file):
89
+ os.remove(temp_do_file)
90
+
91
+ return result.returncode == 0
92
+
93
+ except (subprocess.TimeoutExpired, subprocess.SubprocessError, OSError) as e:
94
+ print(f" Error testing Stata: {e}")
95
+ return False
96
+
97
+ return False
98
+
99
+
100
+ def check_directories() -> Dict[str, Tuple[str, bool]]:
101
+ """Check if required directories exist and create them if needed"""
102
+ home_dir = os.path.expanduser("~")
103
+ documents_path = os.path.join(home_dir, "Documents")
104
+
105
+ # Define directories that should exist
106
+ base_path = os.path.join(documents_path, "stata-mcp-folder")
107
+ dirs = {
108
+ "base_dir": (base_path, False),
109
+ "log_dir": (os.path.join(base_path, "stata-mcp-log"), False),
110
+ "dofile_dir": (os.path.join(base_path, "stata-mcp-dofile"), False),
111
+ "result_dir": (os.path.join(base_path, "stata-mcp-result"), False),
112
+ }
113
+
114
+ # Check and create directories if they don't exist
115
+ for name, (path, _) in dirs.items():
116
+ exists = os.path.exists(path)
117
+ if not exists:
118
+ try:
119
+ os.makedirs(path, exist_ok=True)
120
+ exists = True
121
+ print(f" Created directory: {path}")
122
+ except Exception as e:
123
+ print(f" Error creating directory {path}: {e}")
124
+
125
+ is_writable = os.access(path, os.W_OK) if exists else False
126
+ dirs[name] = (path, exists and is_writable)
127
+
128
+ return dirs
129
+
130
+
131
+ def check_mcp_installation() -> bool:
132
+ """Check if MCP library is installed"""
133
+ try:
134
+ pass
135
+
136
+ return True
137
+ except ImportError:
138
+ return False
139
+
140
+
141
+ def animate_loading(seconds: int) -> None:
142
+ """Display an animated loading spinner"""
143
+ chars = "|/-\\"
144
+ for _ in range(seconds * 5):
145
+ for char in chars:
146
+ sys.stdout.write(f"\r Finding Stata CLI {char} ")
147
+ sys.stdout.flush()
148
+ time.sleep(0.05)
149
+ sys.stdout.write("\r" + " " * 20 + "\r")
150
+ sys.stdout.flush()
151
+
152
+
153
+ def usable():
154
+ """Main function to check Stata MCP configuration"""
155
+ print("\n===== Stata MCP Configuration Check =====\n")
156
+
157
+ # Check operating system
158
+ detected_os, os_supported = check_os()
159
+ print_status(f"Operating system (Current: {detected_os})", os_supported)
160
+ if not os_supported:
161
+ print(
162
+ " Warning: Your operating system may not be fully supported by Stata-MCP."
163
+ )
164
+
165
+ # Check Python version
166
+ python_version, python_compatible = check_python_version()
167
+ print_status(
168
+ f"Python version (Current: {python_version})",
169
+ python_compatible)
170
+ if not python_compatible:
171
+ print(" Warning: Python 3.11+ is recommended for Stata-MCP.")
172
+
173
+ # Check MCP library
174
+ mcp_installed = check_mcp_installation()
175
+ print_status("MCP library installation", mcp_installed)
176
+ if not mcp_installed:
177
+ print(" Please install MCP library: pip install mcp[cli]")
178
+
179
+ # Find Stata CLI
180
+ print("Locating Stata CLI...")
181
+ animate_loading(2) # Show loading animation for 2 seconds
182
+ stata_cli_path = StataFinder().STATA_CLI
183
+
184
+ stata_found = bool(stata_cli_path and os.path.exists(stata_cli_path))
185
+ print_status(
186
+ f"Stata CLI (Path: {stata_cli_path or 'Not found'})",
187
+ stata_found)
188
+
189
+ # Test Stata execution if found
190
+ stata_works = False
191
+ if stata_found:
192
+ print("Testing Stata execution...")
193
+ stata_works = test_stata_execution(stata_cli_path)
194
+ print_status("Stata execution test", stata_works)
195
+ if not stata_works:
196
+ print(" Warning: Stata was found but could not be executed properly.")
197
+ print(
198
+ " You may need to specify the path manually in config.py or as an environment variable."
199
+ )
200
+
201
+ # Check and create necessary directories
202
+ print("\nChecking required directories...")
203
+ directories = check_directories()
204
+ all_dirs_ok = True
205
+ for name, (path, exists) in directories.items():
206
+ dir_name = name.replace("_", " ").title()
207
+ print_status(f"{dir_name} (Path: {path})", exists)
208
+ if not exists:
209
+ all_dirs_ok = False
210
+ print(f" Warning: Could not create or access {path}")
211
+
212
+ # Overall summary
213
+ print("\n===== Summary =====")
214
+ all_passed = (
215
+ os_supported
216
+ and python_compatible
217
+ and mcp_installed
218
+ and stata_found
219
+ and stata_works
220
+ and all_dirs_ok
221
+ )
222
+
223
+ if all_passed:
224
+ print("\n✅ Success! Your Stata-MCP setup is ready to use.")
225
+ print(
226
+ "You can now use Stata-MCP with your preferred MCP client (Claude, Cherry Studio, etc.)"
227
+ )
228
+ else:
229
+ print(
230
+ "\n⚠️ Some checks failed. Please address the issues above to use Stata-MCP."
231
+ )
232
+ if not stata_found or not stata_works:
233
+ print(
234
+ "\nTo manually specify your Stata path, add this to your MCP configuration:"
235
+ )
236
+ print(' "env": {')
237
+ print(' "stata_cli": "/path/to/your/stata/executable"')
238
+ print(" }")
239
+
240
+ return 0 if all_passed else 1
241
+
242
+
243
+ if __name__ == "__main__":
244
+ sys.exit(usable())