aigroup-econ-mcp 0.1.2__py3-none-any.whl → 0.1.6__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.

@@ -1,19 +1,19 @@
1
- """
2
- AIGroup 计量经济学 MCP 服务
3
- 专业计量经济学MCP工具 - 让大模型直接进行数据分析
4
-
5
- 提供:
6
- - 描述性统计分析
7
- - OLS回归分析
8
- - 时间序列分析
9
- - 假设检验
10
- - 模型诊断
11
- """
12
-
13
- __version__ = "0.1.2"
14
- __author__ = "AIGroup"
15
- __description__ = "专业计量经济学MCP工具 - 让大模型直接进行数据分析"
16
-
17
- from .server import create_mcp_server
18
-
1
+ """
2
+ AIGroup 计量经济学 MCP 服务
3
+ 专业计量经济学MCP工具 - 让大模型直接进行数据分析
4
+
5
+ 提供:
6
+ - 描述性统计分析
7
+ - OLS回归分析
8
+ - 时间序列分析
9
+ - 假设检验
10
+ - 模型诊断
11
+ """
12
+
13
+ __version__ = "0.1.6"
14
+ __author__ = "AIGroup"
15
+ __description__ = "专业计量经济学MCP工具 - 让大模型直接进行数据分析"
16
+
17
+ from .server import create_mcp_server
18
+
19
19
  __all__ = ["create_mcp_server", "__version__", "__author__", "__description__"]
aigroup_econ_mcp/cli.py CHANGED
@@ -1,82 +1,87 @@
1
- """
2
- AIGroup 计量经济学 MCP 服务命令行入口
3
- """
4
-
5
- import sys
6
- import click
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='stdio',
14
- type=click.Choice(['stdio', 'streamable-http', 'sse']),
15
- help='传输协议 (默认: stdio)')
16
- @click.option('--debug', is_flag=True, help='启用调试模式')
17
- @click.option('--mount-path', default=None, help='挂载路径')
18
- @click.option('--version', is_flag=True, help='显示版本信息')
19
- def cli(port: int, host: str, transport: str, debug: bool, mount_path: str, version: bool):
20
- """AIGroup 计量经济学 MCP 工具
21
-
22
- 默认以stdio模式启动MCP服务器,适用于MCP客户端集成。
23
- """
24
-
25
- # 处理版本标志
26
- if version:
27
- click.echo("aigroup-econ-mcp v0.1.0", err=True)
28
- click.echo("Professional econometrics MCP tool", err=True)
29
- click.echo("Author: AIGroup", err=True)
30
- sys.exit(0)
31
-
32
- # 创建MCP服务器
33
- mcp_server = create_mcp_server()
34
-
35
- # 设置调试模式
36
- if debug:
37
- mcp_server.settings.debug = True
38
- click.echo(f"[DEBUG] 调试模式已启用", err=True)
39
-
40
- # 根据传输协议启动服务器
41
- if transport == 'stdio':
42
- # stdio模式直接运行,不输出任何日志到stdout(MCP协议通信)
43
- # 所有日志输出到stderr
44
- if debug:
45
- click.echo(f"[DEBUG] Starting in stdio mode", err=True)
46
- mcp_server.run(transport='stdio')
47
-
48
- elif transport == 'streamable-http':
49
- # Streamable HTTP模式
50
- click.echo(f"[INFO] Starting aigroup-econ-mcp server", err=True)
51
- click.echo(f"[INFO] Professional econometrics MCP tool for AI data analysis", err=True)
52
- click.echo(f"[INFO] Transport protocol: {transport}", err=True)
53
- click.echo(f"[INFO] Service address: http://{host}:{port}", err=True)
54
- if mount_path:
55
- click.echo(f"[INFO] Mount path: {mount_path}", err=True)
56
-
57
- mcp_server.run(
58
- transport='streamable-http',
59
- host=host,
60
- port=port,
61
- mount_path=mount_path or '/mcp'
62
- )
63
-
64
- elif transport == 'sse':
65
- # SSE模式
66
- click.echo(f"[INFO] Starting aigroup-econ-mcp server", err=True)
67
- click.echo(f"[INFO] Professional econometrics MCP tool for AI data analysis", err=True)
68
- click.echo(f"[INFO] Transport protocol: {transport}", err=True)
69
- click.echo(f"[INFO] Service address: http://{host}:{port}", err=True)
70
- if mount_path:
71
- click.echo(f"[INFO] Mount path: {mount_path}", err=True)
72
-
73
- mcp_server.run(
74
- transport='sse',
75
- host=host,
76
- port=port,
77
- mount_path=mount_path or '/sse'
78
- )
79
-
80
-
81
- if __name__ == "__main__":
1
+ """
2
+ AIGroup 计量经济学 MCP 服务命令行入口
3
+ """
4
+
5
+ import sys
6
+ import click
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='stdio',
14
+ type=click.Choice(['stdio', 'streamable-http', 'sse']),
15
+ help='传输协议 (默认: stdio)')
16
+ @click.option('--debug', is_flag=True, help='启用调试模式')
17
+ @click.option('--mount-path', default=None, help='挂载路径')
18
+ @click.option('--version', is_flag=True, help='显示版本信息')
19
+ def cli(port: int, host: str, transport: str, debug: bool, mount_path: str, version: bool):
20
+ """AIGroup 计量经济学 MCP 工具
21
+
22
+ 默认以stdio模式启动MCP服务器,适用于MCP客户端集成。
23
+ """
24
+
25
+ # 处理版本标志
26
+ if version:
27
+ from . import __version__
28
+ click.echo(f"aigroup-econ-mcp v{__version__}", err=True)
29
+ click.echo("Professional econometrics MCP tool", err=True)
30
+ click.echo("Author: AIGroup", err=True)
31
+ sys.exit(0)
32
+
33
+ # 创建MCP服务器
34
+ mcp_server = create_mcp_server()
35
+
36
+ # 设置调试模式
37
+ if debug:
38
+ mcp_server.settings.debug = True
39
+ click.echo(f"[DEBUG] 调试模式已启用", err=True)
40
+
41
+ # 根据传输协议启动服务器
42
+ if transport == 'stdio':
43
+ # stdio模式直接运行,不输出任何日志到stdout(MCP协议通信)
44
+ # 所有日志输出到stderr
45
+ from . import __version__
46
+ click.echo(f"[INFO] aigroup-econ-mcp v{__version__} starting...", err=True)
47
+ click.echo(f"[INFO] Transport: stdio (MCP protocol)", err=True)
48
+ if debug:
49
+ click.echo(f"[DEBUG] Debug mode enabled", err=True)
50
+ click.echo(f"[INFO] Server ready. Waiting for MCP client connection...", err=True)
51
+ mcp_server.run(transport='stdio')
52
+
53
+ elif transport == 'streamable-http':
54
+ # Streamable HTTP模式
55
+ click.echo(f"[INFO] Starting aigroup-econ-mcp server", err=True)
56
+ click.echo(f"[INFO] Professional econometrics MCP tool for AI data analysis", err=True)
57
+ click.echo(f"[INFO] Transport protocol: {transport}", err=True)
58
+ click.echo(f"[INFO] Service address: http://{host}:{port}", err=True)
59
+ if mount_path:
60
+ click.echo(f"[INFO] Mount path: {mount_path}", err=True)
61
+
62
+ mcp_server.run(
63
+ transport='streamable-http',
64
+ host=host,
65
+ port=port,
66
+ mount_path=mount_path or '/mcp'
67
+ )
68
+
69
+ elif transport == 'sse':
70
+ # SSE模式
71
+ click.echo(f"[INFO] Starting aigroup-econ-mcp server", err=True)
72
+ click.echo(f"[INFO] Professional econometrics MCP tool for AI data analysis", err=True)
73
+ click.echo(f"[INFO] Transport protocol: {transport}", err=True)
74
+ click.echo(f"[INFO] Service address: http://{host}:{port}", err=True)
75
+ if mount_path:
76
+ click.echo(f"[INFO] Mount path: {mount_path}", err=True)
77
+
78
+ mcp_server.run(
79
+ transport='sse',
80
+ host=host,
81
+ port=port,
82
+ mount_path=mount_path or '/sse'
83
+ )
84
+
85
+
86
+ if __name__ == "__main__":
82
87
  cli()
@@ -1,3 +1,4 @@
1
+
1
2
  """
2
3
  AIGroup 计量经济学 MCP 服务器
3
4
  使用最新的MCP特性提供专业计量经济学分析工具
@@ -15,7 +16,7 @@ from statsmodels.tsa import stattools
15
16
  from scipy import stats
16
17
  from pydantic import BaseModel, Field
17
18
 
18
- from mcp.server.fastmcp import FastMCP, Context, Icon
19
+ from mcp.server.fastmcp import FastMCP, Context
19
20
  from mcp.server.session import ServerSession
20
21
  from mcp.types import CallToolResult, TextContent
21
22
 
@@ -70,12 +71,7 @@ class AppContext:
70
71
  version: str = "0.1.0"
71
72
 
72
73
 
73
- # 服务器图标
74
- server_icon = Icon(
75
- src="https://img.icons8.com/fluency/48/bar-chart.png",
76
- mimeType="image/png",
77
- sizes=["48x48"]
78
- )
74
+ # 服务器图标(已移除,因为MCP库不再支持Icon类)
79
75
 
80
76
 
81
77
  @asynccontextmanager
@@ -171,7 +167,7 @@ async def descriptive_statistics(
171
167
  - 建议样本量 >= 30 以获得可靠的统计推断"""
172
168
  )
173
169
  ]
174
- ) -> Annotated[CallToolResult, DescriptiveStatsResult]:
170
+ ) -> CallToolResult:
175
171
  """计算描述性统计量
176
172
 
177
173
  📊 功能说明:
@@ -205,18 +201,26 @@ async def descriptive_statistics(
205
201
  await ctx.info(f"开始计算描述性统计,处理 {len(data)} 个变量")
206
202
 
207
203
  try:
204
+ # 数据验证
205
+ if not data:
206
+ raise ValueError("数据不能为空")
207
+
208
208
  df = pd.DataFrame(data)
209
-
210
- # 基础统计量
209
+
210
+ # 检查数据一致性
211
+ if len(df.columns) == 0:
212
+ raise ValueError("至少需要一个变量")
213
+
214
+ # 基础统计量 - 修复:返回所有变量的综合统计
211
215
  result = DescriptiveStatsResult(
212
216
  count=len(df),
213
- mean=df.mean().iloc[0], # 简化示例,实际应返回所有变量
214
- std=df.std().iloc[0],
215
- min=df.min().iloc[0],
216
- max=df.max().iloc[0],
217
- median=df.median().iloc[0],
218
- skewness=df.skew().iloc[0],
219
- kurtosis=df.kurtosis().iloc[0]
217
+ mean=df.mean().mean(), # 所有变量的均值
218
+ std=df.std().mean(), # 所有变量的标准差均值
219
+ min=df.min().min(), # 所有变量的最小值
220
+ max=df.max().max(), # 所有变量的最大值
221
+ median=df.median().mean(), # 所有变量的中位数均值
222
+ skewness=df.skew().mean(), # 所有变量的偏度均值
223
+ kurtosis=df.kurtosis().mean() # 所有变量的峰度均值
220
224
  )
221
225
 
222
226
  # 计算相关系数矩阵
@@ -301,7 +305,7 @@ async def ols_regression(
301
305
  - 建议使用有意义的名称以便解释结果"""
302
306
  )
303
307
  ] = None
304
- ) -> Annotated[CallToolResult, OLSRegressionResult]:
308
+ ) -> CallToolResult:
305
309
  """执行普通最小二乘法(OLS)回归分析
306
310
 
307
311
  📊 功能说明:
@@ -337,40 +341,63 @@ async def ols_regression(
337
341
  await ctx.info(f"开始OLS回归分析,样本大小: {len(y_data)},自变量数量: {len(x_data[0]) if x_data else 0}")
338
342
 
339
343
  try:
344
+ # 数据验证
345
+ if not y_data:
346
+ raise ValueError("因变量数据不能为空")
347
+ if not x_data:
348
+ raise ValueError("自变量数据不能为空")
349
+ if len(y_data) != len(x_data):
350
+ raise ValueError(f"因变量和自变量的观测数量不一致: y_data={len(y_data)}, x_data={len(x_data)}")
351
+
340
352
  # 准备数据
341
- X = np.column_stack(x_data) if x_data else np.ones((len(y_data), 1))
342
- if x_data: # 只有当有自变量时才添加常数项
343
- X = sm.add_constant(X)
353
+ X = np.array(x_data)
354
+ y = np.array(y_data)
355
+
356
+ # 添加常数项
357
+ X_with_const = sm.add_constant(X)
344
358
 
345
359
  # 拟合模型
346
- model = sm.OLS(y_data, X).fit()
347
-
348
- # 构建结果
349
- result = OLSRegressionResult(
350
- rsquared=model.rsquared,
351
- rsquared_adj=model.rsquared_adj,
352
- f_statistic=model.fvalue,
353
- f_pvalue=model.f_pvalue,
354
- aic=model.aic,
355
- bic=model.bic,
356
- coefficients={}
357
- )
360
+ model = sm.OLS(y, X_with_const).fit()
358
361
 
359
- # 添加系数详情
362
+ # 构建系数字典
360
363
  conf_int = model.conf_int()
364
+ coefficients = {}
365
+
366
+ # 修复:正确处理feature_names为None的情况
367
+ if feature_names is None:
368
+ feature_names = [f"x{i+1}" for i in range(X.shape[1])]
369
+ elif len(feature_names) != X.shape[1]:
370
+ await ctx.warning(f"提供的feature_names数量({len(feature_names)})与自变量数量({X.shape[1]})不匹配,使用默认命名")
371
+ feature_names = [f"x{i+1}" for i in range(X.shape[1])]
372
+
361
373
  for i, coef in enumerate(model.params):
362
- var_name = "const" if i == 0 else feature_names[i-1] if feature_names else f"x{i}"
363
- result.coefficients[var_name] = {
364
- "coef": coef,
365
- "std_err": model.bse[i],
366
- "t_value": model.tvalues[i],
367
- "p_value": model.pvalues[i],
368
- "ci_lower": conf_int[i][0],
369
- "ci_upper": conf_int[i][1]
374
+ if i == 0:
375
+ var_name = "const"
376
+ else:
377
+ var_name = feature_names[i-1]
378
+
379
+ coefficients[var_name] = {
380
+ "coef": float(coef), # 转换numpy.float64为float
381
+ "std_err": float(model.bse[i]),
382
+ "t_value": float(model.tvalues[i]),
383
+ "p_value": float(model.pvalues[i]),
384
+ "ci_lower": float(conf_int[i][0]),
385
+ "ci_upper": float(conf_int[i][1])
370
386
  }
387
+
388
+ # 构建结果
389
+ result = OLSRegressionResult(
390
+ rsquared=float(model.rsquared),
391
+ rsquared_adj=float(model.rsquared_adj),
392
+ f_statistic=float(model.fvalue),
393
+ f_pvalue=float(model.f_pvalue),
394
+ aic=float(model.aic),
395
+ bic=float(model.bic),
396
+ coefficients=coefficients
397
+ )
371
398
 
372
399
  await ctx.info("OLS回归分析完成")
373
-
400
+
374
401
  return CallToolResult(
375
402
  content=[
376
403
  TextContent(
@@ -422,7 +449,8 @@ async def hypothesis_testing(
422
449
  说明:
423
450
  - 仅在双样本t检验时需要提供
424
451
  - 单样本t检验时保持为None
425
- - 两组数据可以有不同的样本量
452
+ - 两组数据可以
453
+ 有不同的样本量
426
454
  - ADF检验不需要第二组数据"""
427
455
  )
428
456
  ] = None,
@@ -447,7 +475,7 @@ async def hypothesis_testing(
447
475
  - 检验时间序列平稳性 → 使用 adf"""
448
476
  )
449
477
  ] = "t_test"
450
- ) -> Annotated[CallToolResult, HypothesisTestResult]:
478
+ ) -> CallToolResult:
451
479
  """执行统计假设检验
452
480
 
453
481
  📊 功能说明:
@@ -574,7 +602,7 @@ async def time_series_analysis(
574
602
  - 日均气温数据"""
575
603
  )
576
604
  ]
577
- ) -> Annotated[CallToolResult, TimeSeriesStatsResult]:
605
+ ) -> CallToolResult:
578
606
  """时间序列统计分析
579
607
 
580
608
  📊 功能说明:
@@ -632,19 +660,40 @@ async def time_series_analysis(
632
660
  await ctx.info(f"开始时间序列分析,数据点数量: {len(data)}")
633
661
 
634
662
  try:
663
+ # 数据验证
664
+ if not data:
665
+ raise ValueError("时间序列数据不能为空")
666
+ if len(data) < 5:
667
+ raise ValueError("时间序列数据至少需要5个观测点")
668
+
635
669
  # ADF单位根检验
636
670
  adf_result = stattools.adfuller(data)
637
671
 
638
672
  # 自相关和偏自相关函数
639
- acf_values = stattools.acf(data, nlags=min(20, len(data)-1))
640
- pacf_values = stattools.pacf(data, nlags=min(20, len(data)-1))
641
-
673
+ # 修复:安全计算nlags,避免PACF计算失败
674
+ max_nlags = min(20, len(data) - 1, len(data) // 2)
675
+ if max_nlags < 1:
676
+ max_nlags = 1 # 确保至少计算1阶
677
+
678
+ # 修复:使用try-except处理ACF/PACF计算可能失败的情况
679
+ try:
680
+ acf_values = stattools.acf(data, nlags=max_nlags)
681
+ pacf_values = stattools.pacf(data, nlags=max_nlags)
682
+ except Exception as acf_error:
683
+ await ctx.warning(f"ACF/PACF计算遇到问题: {str(acf_error)},使用简化计算")
684
+ # 使用更简单的计算方法
685
+ acf_values = np.zeros(max_nlags + 1)
686
+ pacf_values = np.zeros(max_nlags + 1)
687
+ acf_values[0] = 1.0 # 0阶自相关总是1
688
+ pacf_values[0] = 1.0 # 0阶偏自相关总是1
689
+
690
+ # 转换numpy类型为Python原生类型
642
691
  result = TimeSeriesStatsResult(
643
- adf_statistic=adf_result[0],
644
- adf_pvalue=adf_result[1],
645
- stationary=adf_result[1] < 0.05,
646
- acf=acf_values.tolist(),
647
- pacf=pacf_values.tolist()
692
+ adf_statistic=float(adf_result[0]),
693
+ adf_pvalue=float(adf_result[1]),
694
+ stationary=bool(adf_result[1] < 0.05),
695
+ acf=[float(x) for x in acf_values.tolist()],
696
+ pacf=[float(x) for x in pacf_values.tolist()]
648
697
  )
649
698
 
650
699
  await ctx.info("时间序列分析完成")
@@ -730,7 +779,7 @@ async def correlation_analysis(
730
779
  - 有序分类数据 → kendall"""
731
780
  )
732
781
  ] = "pearson"
733
- ) -> str:
782
+ ) -> CallToolResult:
734
783
  """变量间相关性分析
735
784
 
736
785
  📊 功能说明:
@@ -773,16 +822,33 @@ async def correlation_analysis(
773
822
  await ctx.info(f"开始相关性分析: {method}")
774
823
 
775
824
  try:
825
+ # 数据验证
826
+ if not data:
827
+ raise ValueError("数据不能为空")
828
+ if len(data) < 2:
829
+ raise ValueError("至少需要2个变量进行相关性分析")
830
+
776
831
  df = pd.DataFrame(data)
777
832
  correlation_matrix = df.corr(method=method)
778
833
 
779
834
  await ctx.info("相关性分析完成")
780
835
 
781
- return f"{method.title()}相关系数矩阵:\n{correlation_matrix.round(4).to_string()}"
836
+ # 修复:返回正确的CallToolResult类型
837
+ return CallToolResult(
838
+ content=[
839
+ TextContent(
840
+ type="text",
841
+ text=f"{method.title()}相关系数矩阵:\n{correlation_matrix.round(4).to_string()}"
842
+ )
843
+ ]
844
+ )
782
845
 
783
846
  except Exception as e:
784
847
  await ctx.error(f"相关性分析出错: {str(e)}")
785
- return f"错误: {str(e)}"
848
+ return CallToolResult(
849
+ content=[TextContent(type="text", text=f"错误: {str(e)}")],
850
+ isError=True
851
+ )
786
852
 
787
853
 
788
854
  def create_mcp_server() -> FastMCP:
@@ -1,7 +1,7 @@
1
- """
2
- 计量经济学工具模块
3
- """
4
-
5
- from . import regression, statistics, time_series
6
-
1
+ """
2
+ 计量经济学工具模块
3
+ """
4
+
5
+ from . import regression, statistics, time_series
6
+
7
7
  __all__ = ["regression", "statistics", "time_series"]