aigroup-econ-mcp 0.1.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.
Potentially problematic release.
This version of aigroup-econ-mcp might be problematic. Click here for more details.
- aigroup_econ_mcp/__init__.py +19 -0
- aigroup_econ_mcp/cli.py +78 -0
- aigroup_econ_mcp/server.py +442 -0
- aigroup_econ_mcp/tools/__init__.py +7 -0
- aigroup_econ_mcp/tools/regression.py +214 -0
- aigroup_econ_mcp/tools/statistics.py +134 -0
- aigroup_econ_mcp/tools/time_series.py +260 -0
- aigroup_econ_mcp-0.1.0.dist-info/METADATA +226 -0
- aigroup_econ_mcp-0.1.0.dist-info/RECORD +12 -0
- aigroup_econ_mcp-0.1.0.dist-info/WHEEL +4 -0
- aigroup_econ_mcp-0.1.0.dist-info/entry_points.txt +2 -0
- aigroup_econ_mcp-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIGroup 计量经济学 MCP 服务
|
|
3
|
+
专业计量经济学MCP工具 - 让大模型直接进行数据分析
|
|
4
|
+
|
|
5
|
+
提供:
|
|
6
|
+
- 描述性统计分析
|
|
7
|
+
- OLS回归分析
|
|
8
|
+
- 时间序列分析
|
|
9
|
+
- 假设检验
|
|
10
|
+
- 模型诊断
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
__author__ = "AIGroup"
|
|
15
|
+
__description__ = "专业计量经济学MCP工具 - 让大模型直接进行数据分析"
|
|
16
|
+
|
|
17
|
+
from .server import create_mcp_server
|
|
18
|
+
|
|
19
|
+
__all__ = ["create_mcp_server", "__version__", "__author__", "__description__"]
|
aigroup_econ_mcp/cli.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIGroup 计量经济学 MCP 服务命令行入口
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
import uvicorn
|
|
7
|
+
from .server import create_mcp_server
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command()
|
|
11
|
+
@click.option('--port', default=8000, help='服务器端口')
|
|
12
|
+
@click.option('--host', default='127.0.0.1', help='服务器地址')
|
|
13
|
+
@click.option('--transport', default='streamable-http',
|
|
14
|
+
type=click.Choice(['stdio', 'streamable-http', 'sse']),
|
|
15
|
+
help='传输协议')
|
|
16
|
+
@click.option('--debug', is_flag=True, help='启用调试模式')
|
|
17
|
+
@click.option('--mount-path', default=None, help='挂载路径')
|
|
18
|
+
def main(port: int, host: str, transport: str, debug: bool, mount_path: str):
|
|
19
|
+
"""启动aigroup-econ-mcp服务器"""
|
|
20
|
+
|
|
21
|
+
# 创建MCP服务器
|
|
22
|
+
mcp_server = create_mcp_server()
|
|
23
|
+
|
|
24
|
+
# 设置调试模式
|
|
25
|
+
if debug:
|
|
26
|
+
mcp_server.settings.debug = True
|
|
27
|
+
click.echo(f"[DEBUG] 调试模式已启用")
|
|
28
|
+
|
|
29
|
+
click.echo(f"[INFO] Starting aigroup-econ-mcp server")
|
|
30
|
+
click.echo(f"[INFO] Professional econometrics MCP tool for AI data analysis")
|
|
31
|
+
click.echo(f"[INFO] Transport protocol: {transport}")
|
|
32
|
+
click.echo(f"[INFO] Service address: http://{host}:{port}")
|
|
33
|
+
if mount_path:
|
|
34
|
+
click.echo(f"[INFO] Mount path: {mount_path}")
|
|
35
|
+
|
|
36
|
+
# 根据传输协议启动服务器
|
|
37
|
+
if transport == 'stdio':
|
|
38
|
+
# stdio模式直接运行
|
|
39
|
+
mcp_server.run(transport='stdio')
|
|
40
|
+
elif transport == 'streamable-http':
|
|
41
|
+
# Streamable HTTP模式
|
|
42
|
+
mcp_server.run(
|
|
43
|
+
transport='streamable-http',
|
|
44
|
+
host=host,
|
|
45
|
+
port=port,
|
|
46
|
+
mount_path=mount_path or '/mcp'
|
|
47
|
+
)
|
|
48
|
+
elif transport == 'sse':
|
|
49
|
+
# SSE模式
|
|
50
|
+
mcp_server.run(
|
|
51
|
+
transport='sse',
|
|
52
|
+
host=host,
|
|
53
|
+
port=port,
|
|
54
|
+
mount_path=mount_path or '/sse'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@click.command()
|
|
59
|
+
def version():
|
|
60
|
+
"""Show version information"""
|
|
61
|
+
click.echo("aigroup-econ-mcp v0.1.0")
|
|
62
|
+
click.echo("Professional econometrics MCP tool")
|
|
63
|
+
click.echo("Author: AIGroup")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@click.group()
|
|
67
|
+
def cli():
|
|
68
|
+
"""AIGroup 计量经济学 MCP 工具"""
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# 添加子命令
|
|
73
|
+
cli.add_command(main)
|
|
74
|
+
cli.add_command(version)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
cli()
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIGroup 计量经济学 MCP 服务器
|
|
3
|
+
使用最新的MCP特性提供专业计量经济学分析工具
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import List, Dict, Any, Optional, Annotated
|
|
7
|
+
from collections.abc import AsyncIterator
|
|
8
|
+
from contextlib import asynccontextmanager
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
import pandas as pd
|
|
12
|
+
import numpy as np
|
|
13
|
+
import statsmodels.api as sm
|
|
14
|
+
from scipy import stats
|
|
15
|
+
from pydantic import BaseModel, Field
|
|
16
|
+
|
|
17
|
+
from mcp.server.fastmcp import FastMCP, Context, Icon
|
|
18
|
+
from mcp.server.session import ServerSession
|
|
19
|
+
from mcp.types import CallToolResult, TextContent
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# 数据模型定义 - 使用Pydantic实现结构化输出
|
|
23
|
+
class DescriptiveStatsResult(BaseModel):
|
|
24
|
+
"""描述性统计结果"""
|
|
25
|
+
count: int = Field(description="样本数量")
|
|
26
|
+
mean: float = Field(description="均值")
|
|
27
|
+
std: float = Field(description="标准差")
|
|
28
|
+
min: float = Field(description="最小值")
|
|
29
|
+
max: float = Field(description="最大值")
|
|
30
|
+
median: float = Field(description="中位数")
|
|
31
|
+
skewness: float = Field(description="偏度")
|
|
32
|
+
kurtosis: float = Field(description="峰度")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class OLSRegressionResult(BaseModel):
|
|
36
|
+
"""OLS回归分析结果"""
|
|
37
|
+
rsquared: float = Field(description="R²")
|
|
38
|
+
rsquared_adj: float = Field(description="调整R²")
|
|
39
|
+
f_statistic: float = Field(description="F统计量")
|
|
40
|
+
f_pvalue: float = Field(description="F检验p值")
|
|
41
|
+
aic: float = Field(description="AIC信息准则")
|
|
42
|
+
bic: float = Field(description="BIC信息准则")
|
|
43
|
+
coefficients: Dict[str, Dict[str, float]] = Field(description="回归系数详情")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class HypothesisTestResult(BaseModel):
|
|
47
|
+
"""假设检验结果"""
|
|
48
|
+
test_type: str = Field(description="检验类型")
|
|
49
|
+
statistic: float = Field(description="检验统计量")
|
|
50
|
+
p_value: float = Field(description="p值")
|
|
51
|
+
significant: bool = Field(description="是否显著(5%水平)")
|
|
52
|
+
confidence_interval: Optional[List[float]] = Field(default=None, description="置信区间")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TimeSeriesStatsResult(BaseModel):
|
|
56
|
+
"""时间序列统计结果"""
|
|
57
|
+
adf_statistic: float = Field(description="ADF检验统计量")
|
|
58
|
+
adf_pvalue: float = Field(description="ADF检验p值")
|
|
59
|
+
stationary: bool = Field(description="是否平稳")
|
|
60
|
+
acf: List[float] = Field(description="自相关函数")
|
|
61
|
+
pacf: List[float] = Field(description="偏自相关函数")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# 应用上下文
|
|
65
|
+
@dataclass
|
|
66
|
+
class AppContext:
|
|
67
|
+
"""应用上下文,包含共享资源"""
|
|
68
|
+
config: Dict[str, Any]
|
|
69
|
+
version: str = "0.1.0"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# 服务器图标
|
|
73
|
+
server_icon = Icon(
|
|
74
|
+
src="https://img.icons8.com/fluency/48/bar-chart.png",
|
|
75
|
+
mimeType="image/png",
|
|
76
|
+
sizes=["48x48"]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@asynccontextmanager
|
|
81
|
+
async def lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
|
|
82
|
+
"""服务器生命周期管理"""
|
|
83
|
+
# 启动时初始化资源
|
|
84
|
+
config = {
|
|
85
|
+
"max_sample_size": 10000,
|
|
86
|
+
"default_significance_level": 0.05,
|
|
87
|
+
"supported_tests": ["t_test", "f_test", "chi_square", "adf"],
|
|
88
|
+
"data_types": ["cross_section", "time_series", "panel"]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
yield AppContext(config=config, version="0.1.0")
|
|
93
|
+
finally:
|
|
94
|
+
# 清理资源
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# 创建MCP服务器实例
|
|
99
|
+
mcp = FastMCP(
|
|
100
|
+
name="aigroup-econ-mcp",
|
|
101
|
+
instructions="Econometrics MCP Server - Provides data analysis, regression analysis, hypothesis testing and more",
|
|
102
|
+
lifespan=lifespan
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@mcp.resource("dataset://sample/{dataset_name}")
|
|
107
|
+
def get_sample_dataset(dataset_name: str) -> str:
|
|
108
|
+
"""Get sample dataset"""
|
|
109
|
+
datasets = {
|
|
110
|
+
"economic_growth": """
|
|
111
|
+
GDP Growth,Inflation Rate,Unemployment Rate,Investment Rate
|
|
112
|
+
3.2,2.1,4.5,15.2
|
|
113
|
+
2.8,2.3,4.2,14.8
|
|
114
|
+
3.5,1.9,4.0,16.1
|
|
115
|
+
2.9,2.4,4.3,15.5
|
|
116
|
+
""",
|
|
117
|
+
"stock_returns": """
|
|
118
|
+
Stock A,Stock B,Stock C
|
|
119
|
+
0.02,-0.01,0.015
|
|
120
|
+
-0.015,0.025,-0.008
|
|
121
|
+
0.018,-0.005,0.012
|
|
122
|
+
""",
|
|
123
|
+
"time_series": """
|
|
124
|
+
Date,Sales,Advertising Expense
|
|
125
|
+
2023-01,12000,800
|
|
126
|
+
2023-02,13500,900
|
|
127
|
+
2023-03,11800,750
|
|
128
|
+
2023-04,14200,1000
|
|
129
|
+
"""
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if dataset_name not in datasets:
|
|
133
|
+
return f"Available datasets: {', '.join(datasets.keys())}"
|
|
134
|
+
|
|
135
|
+
return datasets[dataset_name]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@mcp.prompt(title="Economic Data Analysis")
|
|
139
|
+
def economic_analysis_prompt(data_description: str, analysis_type: str = "descriptive") -> str:
|
|
140
|
+
"""Economic data analysis prompt template"""
|
|
141
|
+
prompts = {
|
|
142
|
+
"descriptive": "Please perform descriptive statistical analysis on the following economic data:",
|
|
143
|
+
"regression": "Please perform regression analysis on the following economic data to identify key factors:",
|
|
144
|
+
"hypothesis": "Please perform hypothesis testing on the following economic data to verify research assumptions:",
|
|
145
|
+
"time_series": "Please analyze the following time series data to check stationarity and correlation:"
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return f"{prompts.get(analysis_type, prompts['descriptive'])}\n\nData description: {data_description}"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@mcp.tool()
|
|
152
|
+
async def descriptive_statistics(
|
|
153
|
+
ctx: Context[ServerSession, AppContext],
|
|
154
|
+
data: Dict[str, List[float]]
|
|
155
|
+
) -> Annotated[CallToolResult, DescriptiveStatsResult]:
|
|
156
|
+
"""计算描述性统计量
|
|
157
|
+
|
|
158
|
+
Args:
|
|
159
|
+
data: 字典格式的数据,键为变量名,值为数值列表
|
|
160
|
+
ctx: MCP上下文对象
|
|
161
|
+
"""
|
|
162
|
+
await ctx.info(f"开始计算描述性统计,处理 {len(data)} 个变量")
|
|
163
|
+
|
|
164
|
+
try:
|
|
165
|
+
df = pd.DataFrame(data)
|
|
166
|
+
|
|
167
|
+
# 基础统计量
|
|
168
|
+
result = DescriptiveStatsResult(
|
|
169
|
+
count=len(df),
|
|
170
|
+
mean=df.mean().iloc[0], # 简化示例,实际应返回所有变量
|
|
171
|
+
std=df.std().iloc[0],
|
|
172
|
+
min=df.min().iloc[0],
|
|
173
|
+
max=df.max().iloc[0],
|
|
174
|
+
median=df.median().iloc[0],
|
|
175
|
+
skewness=df.skew().iloc[0],
|
|
176
|
+
kurtosis=df.kurtosis().iloc[0]
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# 计算相关系数矩阵
|
|
180
|
+
correlation_matrix = df.corr().round(4)
|
|
181
|
+
|
|
182
|
+
await ctx.info(f"描述性统计计算完成,样本大小: {len(df)}")
|
|
183
|
+
|
|
184
|
+
return CallToolResult(
|
|
185
|
+
content=[
|
|
186
|
+
TextContent(
|
|
187
|
+
type="text",
|
|
188
|
+
text=f"描述性统计结果:\n"
|
|
189
|
+
f"均值: {result.mean:.4f}\n"
|
|
190
|
+
f"标准差: {result.std:.4f}\n"
|
|
191
|
+
f"最小值: {result.min:.4f}\n"
|
|
192
|
+
f"最大值: {result.max:.4f}\n"
|
|
193
|
+
f"中位数: {result.median:.4f}\n"
|
|
194
|
+
f"偏度: {result.skewness:.4f}\n"
|
|
195
|
+
f"峰度: {result.kurtosis:.4f}\n\n"
|
|
196
|
+
f"相关系数矩阵:\n{correlation_matrix.to_string()}"
|
|
197
|
+
)
|
|
198
|
+
],
|
|
199
|
+
structuredContent=result.model_dump()
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
except Exception as e:
|
|
203
|
+
await ctx.error(f"计算描述性统计时出错: {str(e)}")
|
|
204
|
+
return CallToolResult(
|
|
205
|
+
content=[TextContent(type="text", text=f"错误: {str(e)}")],
|
|
206
|
+
isError=True
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
@mcp.tool()
|
|
211
|
+
async def ols_regression(
|
|
212
|
+
ctx: Context[ServerSession, AppContext],
|
|
213
|
+
y_data: List[float],
|
|
214
|
+
x_data: List[List[float]],
|
|
215
|
+
feature_names: Optional[List[str]] = None
|
|
216
|
+
) -> Annotated[CallToolResult, OLSRegressionResult]:
|
|
217
|
+
"""执行OLS回归分析
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
y_data: 因变量数据
|
|
221
|
+
x_data: 自变量数据,每行一个观测
|
|
222
|
+
feature_names: 自变量名称
|
|
223
|
+
ctx: MCP上下文对象
|
|
224
|
+
"""
|
|
225
|
+
await ctx.info(f"开始OLS回归分析,样本大小: {len(y_data)},自变量数量: {len(x_data[0]) if x_data else 0}")
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
# 准备数据
|
|
229
|
+
X = np.column_stack(x_data) if x_data else np.ones((len(y_data), 1))
|
|
230
|
+
if x_data: # 只有当有自变量时才添加常数项
|
|
231
|
+
X = sm.add_constant(X)
|
|
232
|
+
|
|
233
|
+
# 拟合模型
|
|
234
|
+
model = sm.OLS(y_data, X).fit()
|
|
235
|
+
|
|
236
|
+
# 构建结果
|
|
237
|
+
result = OLSRegressionResult(
|
|
238
|
+
rsquared=model.rsquared,
|
|
239
|
+
rsquared_adj=model.rsquared_adj,
|
|
240
|
+
f_statistic=model.fvalue,
|
|
241
|
+
f_pvalue=model.f_pvalue,
|
|
242
|
+
aic=model.aic,
|
|
243
|
+
bic=model.bic,
|
|
244
|
+
coefficients={}
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# 添加系数详情
|
|
248
|
+
conf_int = model.conf_int()
|
|
249
|
+
for i, coef in enumerate(model.params):
|
|
250
|
+
var_name = "const" if i == 0 else feature_names[i-1] if feature_names else f"x{i}"
|
|
251
|
+
result.coefficients[var_name] = {
|
|
252
|
+
"coef": coef,
|
|
253
|
+
"std_err": model.bse[i],
|
|
254
|
+
"t_value": model.tvalues[i],
|
|
255
|
+
"p_value": model.pvalues[i],
|
|
256
|
+
"ci_lower": conf_int[i][0],
|
|
257
|
+
"ci_upper": conf_int[i][1]
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
await ctx.info("OLS回归分析完成")
|
|
261
|
+
|
|
262
|
+
return CallToolResult(
|
|
263
|
+
content=[
|
|
264
|
+
TextContent(
|
|
265
|
+
type="text",
|
|
266
|
+
text=f"OLS回归分析结果:\n"
|
|
267
|
+
f"R² = {result.rsquared:.4f}\n"
|
|
268
|
+
f"调整R² = {result.rsquared_adj:.4f}\n"
|
|
269
|
+
f"F统计量 = {result.f_statistic:.4f} (p = {result.f_pvalue:.4f})\n"
|
|
270
|
+
f"AIC = {result.aic:.2f}, BIC = {result.bic:.2f}\n\n"
|
|
271
|
+
f"回归系数:\n{model.summary().tables[1]}"
|
|
272
|
+
)
|
|
273
|
+
],
|
|
274
|
+
structuredContent=result.model_dump()
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
except Exception as e:
|
|
278
|
+
await ctx.error(f"OLS回归分析出错: {str(e)}")
|
|
279
|
+
return CallToolResult(
|
|
280
|
+
content=[TextContent(type="text", text=f"错误: {str(e)}")],
|
|
281
|
+
isError=True
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@mcp.tool()
|
|
286
|
+
async def hypothesis_testing(
|
|
287
|
+
ctx: Context[ServerSession, AppContext],
|
|
288
|
+
data1: List[float],
|
|
289
|
+
data2: Optional[List[float]] = None,
|
|
290
|
+
test_type: str = "t_test"
|
|
291
|
+
) -> Annotated[CallToolResult, HypothesisTestResult]:
|
|
292
|
+
"""执行假设检验
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
data1: 第一组数据
|
|
296
|
+
data2: 第二组数据(可选)
|
|
297
|
+
test_type: 检验类型
|
|
298
|
+
ctx: MCP上下文对象
|
|
299
|
+
"""
|
|
300
|
+
await ctx.info(f"开始假设检验: {test_type}")
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
if test_type == "t_test":
|
|
304
|
+
if data2 is None:
|
|
305
|
+
# 单样本t检验
|
|
306
|
+
result = stats.ttest_1samp(data1, 0)
|
|
307
|
+
ci = stats.t.interval(0.95, len(data1)-1, loc=np.mean(data1), scale=stats.sem(data1))
|
|
308
|
+
else:
|
|
309
|
+
# 双样本t检验
|
|
310
|
+
result = stats.ttest_ind(data1, data2)
|
|
311
|
+
ci = None # 双样本t检验不计算置信区间
|
|
312
|
+
|
|
313
|
+
test_result = HypothesisTestResult(
|
|
314
|
+
test_type=test_type,
|
|
315
|
+
statistic=result.statistic,
|
|
316
|
+
p_value=result.pvalue,
|
|
317
|
+
significant=result.pvalue < 0.05,
|
|
318
|
+
confidence_interval=list(ci) if ci else None
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
elif test_type == "adf":
|
|
322
|
+
# ADF单位根检验
|
|
323
|
+
result = statsmodels.tsa.stattools.adfuller(data1)
|
|
324
|
+
test_result = HypothesisTestResult(
|
|
325
|
+
test_type="adf",
|
|
326
|
+
statistic=result[0],
|
|
327
|
+
p_value=result[1],
|
|
328
|
+
significant=result[1] < 0.05,
|
|
329
|
+
confidence_interval=None
|
|
330
|
+
)
|
|
331
|
+
else:
|
|
332
|
+
raise ValueError(f"不支持的检验类型: {test_type}")
|
|
333
|
+
|
|
334
|
+
await ctx.info(f"假设检验完成: {test_type}")
|
|
335
|
+
|
|
336
|
+
return CallToolResult(
|
|
337
|
+
content=[
|
|
338
|
+
TextContent(
|
|
339
|
+
type="text",
|
|
340
|
+
text=f"{test_type.upper()}检验结果:\n"
|
|
341
|
+
f"检验统计量 = {test_result.statistic:.4f}\n"
|
|
342
|
+
f"p值 = {test_result.p_value:.4f}\n"
|
|
343
|
+
f"{'显著' if test_result.significant else '不显著'} (5%水平)\n"
|
|
344
|
+
f"{f'95%置信区间: [{test_result.confidence_interval[0]:.4f}, {test_result.confidence_interval[1]:.4f}]' if test_result.confidence_interval else ''}"
|
|
345
|
+
)
|
|
346
|
+
],
|
|
347
|
+
structuredContent=test_result.model_dump()
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
except Exception as e:
|
|
351
|
+
await ctx.error(f"假设检验出错: {str(e)}")
|
|
352
|
+
return CallToolResult(
|
|
353
|
+
content=[TextContent(type="text", text=f"错误: {str(e)}")],
|
|
354
|
+
isError=True
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
@mcp.tool()
|
|
359
|
+
async def time_series_analysis(
|
|
360
|
+
ctx: Context[ServerSession, AppContext],
|
|
361
|
+
data: List[float]
|
|
362
|
+
) -> Annotated[CallToolResult, TimeSeriesStatsResult]:
|
|
363
|
+
"""时间序列分析
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
data: 时间序列数据
|
|
367
|
+
ctx: MCP上下文对象
|
|
368
|
+
"""
|
|
369
|
+
await ctx.info(f"开始时间序列分析,数据点数量: {len(data)}")
|
|
370
|
+
|
|
371
|
+
try:
|
|
372
|
+
# ADF单位根检验
|
|
373
|
+
adf_result = statsmodels.tsa.stattools.adfuller(data)
|
|
374
|
+
|
|
375
|
+
# 自相关和偏自相关函数
|
|
376
|
+
acf_values = statsmodels.tsa.stattools.acf(data, nlags=min(20, len(data)-1))
|
|
377
|
+
pacf_values = statsmodels.tsa.stattools.pacf(data, nlags=min(20, len(data)-1))
|
|
378
|
+
|
|
379
|
+
result = TimeSeriesStatsResult(
|
|
380
|
+
adf_statistic=adf_result[0],
|
|
381
|
+
adf_pvalue=adf_result[1],
|
|
382
|
+
stationary=adf_result[1] < 0.05,
|
|
383
|
+
acf=acf_values.tolist(),
|
|
384
|
+
pacf=pacf_values.tolist()
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
await ctx.info("时间序列分析完成")
|
|
388
|
+
|
|
389
|
+
return CallToolResult(
|
|
390
|
+
content=[
|
|
391
|
+
TextContent(
|
|
392
|
+
type="text",
|
|
393
|
+
text=f"时间序列分析结果:\n"
|
|
394
|
+
f"ADF检验统计量 = {result.adf_statistic:.4f}\n"
|
|
395
|
+
f"ADF检验p值 = {result.adf_pvalue:.4f}\n"
|
|
396
|
+
f"{'平稳' if result.stationary else '非平稳'}序列\n"
|
|
397
|
+
f"ACF前5阶: {result.acf[:5]}\n"
|
|
398
|
+
f"PACF前5阶: {result.pacf[:5]}"
|
|
399
|
+
)
|
|
400
|
+
],
|
|
401
|
+
structuredContent=result.model_dump()
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
except Exception as e:
|
|
405
|
+
await ctx.error(f"时间序列分析出错: {str(e)}")
|
|
406
|
+
return CallToolResult(
|
|
407
|
+
content=[TextContent(type="text", text=f"错误: {str(e)}")],
|
|
408
|
+
isError=True
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
@mcp.tool()
|
|
413
|
+
async def correlation_analysis(
|
|
414
|
+
ctx: Context[ServerSession, AppContext],
|
|
415
|
+
data: Dict[str, List[float]],
|
|
416
|
+
method: str = "pearson"
|
|
417
|
+
) -> str:
|
|
418
|
+
"""相关性分析
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
data: 变量数据
|
|
422
|
+
method: 相关系数类型
|
|
423
|
+
ctx: MCP上下文对象
|
|
424
|
+
"""
|
|
425
|
+
await ctx.info(f"开始相关性分析: {method}")
|
|
426
|
+
|
|
427
|
+
try:
|
|
428
|
+
df = pd.DataFrame(data)
|
|
429
|
+
correlation_matrix = df.corr(method=method)
|
|
430
|
+
|
|
431
|
+
await ctx.info("相关性分析完成")
|
|
432
|
+
|
|
433
|
+
return f"{method.title()}相关系数矩阵:\n{correlation_matrix.round(4).to_string()}"
|
|
434
|
+
|
|
435
|
+
except Exception as e:
|
|
436
|
+
await ctx.error(f"相关性分析出错: {str(e)}")
|
|
437
|
+
return f"错误: {str(e)}"
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def create_mcp_server() -> FastMCP:
|
|
441
|
+
"""创建并返回MCP服务器实例"""
|
|
442
|
+
return mcp
|