MintOS 0.1.1__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,58 @@
1
+ Metadata-Version: 2.4
2
+ Name: MintOS
3
+ Version: 0.1.1
4
+ Summary: Mind OS or Money OS
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/tkorays/MOS
7
+ Project-URL: Issues, https://github.com/tkorays/MOS/issues
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.13
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: python-dotenv>=1.2.1
13
+ Requires-Dist: loguru
14
+ Requires-Dist: pydantic-settings
15
+ Requires-Dist: pydantic
16
+ Requires-Dist: requests>=2.32.0
17
+ Requires-Dist: click>=8.4.1
18
+ Requires-Dist: pyyaml>=6.0.1
19
+ Requires-Dist: mcp>=1.28.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
22
+ Requires-Dist: pre-commit; extra == "dev"
23
+ Requires-Dist: ruff; extra == "dev"
24
+
25
+ # MOS
26
+
27
+ MOS 是一个基于 Python 的插件化框架,提供核心基础设施和插件管理能力。
28
+
29
+ ## 核心特性
30
+
31
+ - **插件架构**:基于 Python entry_points 的标准插件机制
32
+ - **依赖隔离**:每个插件独立管理依赖,主仓库只包含核心框架
33
+ - **灵活安装**:插件可通过 pip 安装,支持从 Git、PyPI 或本地安装
34
+
35
+ ## 可用插件
36
+
37
+ MOS 通过插件扩展功能,目前已有的插件:
38
+
39
+ | 插件 | 功能 | 安装方式 |
40
+ |------|------|----------|
41
+ | **mos-quant** | 量化交易框架,支持数据采集、回测、实盘交易 | `pip install mos-quant` |
42
+ | **mos-wiki** | 知识库管理系统,支持 Markdown 知识库管理 | `pip install mos-wiki` |
43
+ | **mos-agent** | 智能体系统,支持 LLM Agent 开发 | `pip install mos-agent` |
44
+
45
+ ## 快速开始
46
+
47
+ ```bash
48
+ # 安装核心框架
49
+ pip install mos
50
+
51
+ # 安装插件(从 GitHub)
52
+ pip install git+https://github.com/yourname/mos_quant.git
53
+ pip install git+https://github.com/yourname/mos_wiki.git
54
+ pip install git+https://github.com/yourname/mos_agent.git
55
+
56
+ # 查看已加载插件
57
+ mos plugin list
58
+ ```
@@ -0,0 +1,28 @@
1
+ mos/__init__.py,sha256=qKlfXlBbeex2PhIWuyLo4vst8I-53KBCWXAjXnkOq9w,39
2
+ mos/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ mos/cli/config.py,sha256=McS6hdiRLGKl9d86YtLGblND1b1d9vGdfPBRhvzDnN8,7149
4
+ mos/cli/init.py,sha256=cKaTjfIhobiWLyGucdVCzj0ZmsJhsQ1VRgPRA6Me0WY,6652
5
+ mos/cli/mcp.py,sha256=Jq-KUy01pBz2itynmqp7bh33TNJg5RXBAcXCeyr85S4,271
6
+ mos/cli/mos.py,sha256=HRFVReZSrOTRGy_-4tYjMJaX0YJAdhiSS3KMD0anI34,1171
7
+ mos/cli/plugin.py,sha256=7w0ygOduG_Hjx0bB2wtYRs5vywiR5Yy_pqt6Ae7yDBk,4066
8
+ mos/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ mos/core/baseconfig.py,sha256=2O8IfPsexoyq9OZDMzn6C_0d_xQMxShk9bsmOJRbrhA,7477
10
+ mos/core/config.py,sha256=oTwl7vqkVTXzyHvhkmvYANk72ehpY0GI7jADc3Kr_Go,4302
11
+ mos/core/logging.py,sha256=VIU1OoG4q_3Rsffz_mAjuE8jKHlABMEcR02o2i719MU,1231
12
+ mos/core/mcp.py,sha256=2KXhUHchw5CyBS119NETvkWc6EKLkdMpZv-nV9-meps,89
13
+ mos/core/plugin.py,sha256=GcL7BgFwLFFVAhgIcdoHkC0D4SVMK4QvOex9aPhnPBA,28457
14
+ mos/core/resource.py,sha256=rQwQ5gFdPLuHNV_aPEjhWqKdcWyjjsU-oDc5Jr6ecRo,1117
15
+ mos/core/dataflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ mos/core/dataflow/flat_json_model.py,sha256=Wga71tEu3Uk1gAcRxIeB2Hp3v9NyUj0jqSbyv6z-fts,5512
17
+ mos/core/dataflow/types.py,sha256=Dyr91NzxOSCjKaVjgFoNyQEWp10952ecTMDa7Leq6Og,3123
18
+ mos/core/grafana/__init__.py,sha256=5VsssC6P27gEK7eUTDOUBUV6lVCZct5T6BidAglKLHs,850
19
+ mos/core/grafana/manage.py,sha256=jSNQrjZZOeP-5A4Ai13bt-lPrE43jJXP938ZtCa8vFo,15497
20
+ mos/core/influxdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ mos/core/influxdb/v3.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ mos/core/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ mos/core/llm/openai_compatible_api.py,sha256=QkH54H4GxD3E7S6Q3SVHGP9jkZTmCXxJHUgJedud07g,18689
24
+ mintos-0.1.1.dist-info/METADATA,sha256=dV1k3E8nr7oCeRVgHyhduzHzJpoh6NZL7ucO_Y7KWgo,1862
25
+ mintos-0.1.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
26
+ mintos-0.1.1.dist-info/entry_points.txt,sha256=m13wVRUum4BygNy_kmo4wm_IVGlpAb1R7lQTvU5buyg,40
27
+ mintos-0.1.1.dist-info/top_level.txt,sha256=yHW4Fvt1SB0qOfsMARJMTbafOyckDeaxTpCNRLIGA4k,4
28
+ mintos-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mos = mos.cli.mos:cli
@@ -0,0 +1 @@
1
+ mos
mos/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """MintOS"""
2
+
3
+ __version__ = "0.1.1"
mos/cli/__init__.py ADDED
File without changes
mos/cli/config.py ADDED
@@ -0,0 +1,222 @@
1
+ import json
2
+ from typing import Any, Callable
3
+
4
+ import click
5
+ from pydantic import ValidationError
6
+
7
+ from mos.core.config import get_config, reload_config
8
+ from mos.core.plugin import get_registry
9
+ from mos.core.logging import get_logger
10
+
11
+ logger = get_logger(__name__)
12
+
13
+
14
+ @click.group()
15
+ def config():
16
+ """配置管理命令"""
17
+ pass
18
+
19
+
20
+ def _get_config_funcs(config_type: str) -> tuple[Callable, Callable]:
21
+ """根据配置类型获取对应的配置管理函数
22
+
23
+ Args:
24
+ config_type: 配置类型,'main' 表示主配置,其他为插件名称
25
+
26
+ Returns:
27
+ (get_config_func, reload_config_func)
28
+ """
29
+ if config_type == "main":
30
+ return get_config, reload_config
31
+
32
+ registry = get_registry()
33
+ get_func = registry.get_config_func(config_type)
34
+ reload_func = registry.get_reload_func(config_type)
35
+
36
+ if get_func is None or reload_func is None:
37
+ raise click.ClickException(f"未知的配置类型: '{config_type}'。可用类型: main, {', '.join(registry.list_names())}")
38
+
39
+ return get_func, reload_func
40
+
41
+
42
+ def _format_value(value):
43
+ """格式化配置值为可读字符串"""
44
+ value_type = type(value)
45
+
46
+ if value_type is bool:
47
+ return "true" if value else "false"
48
+ elif value_type in (list, tuple):
49
+ return ", ".join(str(item) for item in value)
50
+ else:
51
+ return str(value)
52
+
53
+
54
+ def _print_config_tree(data, prefix=""):
55
+ """递归打印配置树"""
56
+ data_type = type(data)
57
+
58
+ if data_type is not dict:
59
+ click.echo(f"{prefix}{data}")
60
+ return
61
+
62
+ for key, value in data.items():
63
+ value_type = type(value)
64
+
65
+ if value is None:
66
+ click.echo(f"{prefix}{key}: null")
67
+ elif value_type is dict:
68
+ click.echo(f"{prefix}{key}:")
69
+ _print_config_tree(value, prefix + " ")
70
+ else:
71
+ display_value = _format_value(value)
72
+ click.echo(f"{prefix}{key}: {display_value}")
73
+
74
+
75
+ def _coerce_cli_value(value: str) -> Any:
76
+ """根据 CLI 字符串字面量推断 Python 标量(true/false/int/float/JSON/str)。"""
77
+ if value.startswith("~"):
78
+ return value
79
+ lowered = value.lower()
80
+ if lowered == "true":
81
+ return True
82
+ if lowered == "false":
83
+ return False
84
+ if value.isdigit():
85
+ return int(value)
86
+ try:
87
+ return float(value)
88
+ except ValueError:
89
+ if value.startswith("[") or value.startswith("{"):
90
+ return json.loads(value)
91
+ return value
92
+
93
+
94
+ @click.command()
95
+ @click.option('--type', 'config_type', default='main', help='配置类型:main(主配置) 或插件名称(如 quant, wiki)')
96
+ def list(config_type: str):
97
+ """列出所有配置项
98
+
99
+ 示例:
100
+ mos config list # 列出主配置
101
+ mos config list --type quant # 列出 quant 插件配置
102
+ mos config list --type wiki # 列出 wiki 插件配置
103
+ """
104
+ try:
105
+ get_config_func, _ = _get_config_funcs(config_type)
106
+ config_obj = get_config_func()
107
+ config_dict = config_obj.model_dump()
108
+ config_file = config_obj.config_file_path
109
+
110
+ click.echo(f"当前配置 ({config_type}):\n")
111
+ click.echo(f"配置文件: {config_file}")
112
+ click.echo(f"配置文件存在: {config_file.exists()}")
113
+ click.echo()
114
+
115
+ _print_config_tree(config_dict)
116
+
117
+ click.echo()
118
+ click.echo("提示: 使用 'mos config set <key> <value> --type <type>' 修改配置")
119
+
120
+ except Exception as e:
121
+ logger.error(f"列出配置失败: {e}")
122
+ raise click.ClickException(f"列出配置失败: {e}")
123
+
124
+
125
+ @click.command()
126
+ @click.argument("key")
127
+ @click.argument("value")
128
+ @click.option('--type', 'config_type', default='main', help='配置类型:main(主配置) 或插件名称(如 quant, wiki)')
129
+ def set(key, value, config_type: str):
130
+ """设置配置项
131
+
132
+ 示例:
133
+ mos config set log.level DEBUG
134
+ mos config set debug true --type main
135
+ mos config set database.path ~/.mos/custom_data/ --type quant
136
+ mos config set mini_qmt.path D:\\QMT --type quant
137
+ """
138
+ try:
139
+ get_config_func, reload_config_func = _get_config_funcs(config_type)
140
+ keys = key.split(".")
141
+ if not keys or not all(keys):
142
+ raise click.ClickException(f"无效的 key: {key!r}")
143
+
144
+ # 用当前 Config 作为基线,把 CLI 改的字段 deep-merge 进去。
145
+ # pydantic 在 model_validate 阶段会做类型转换(str→int/bool/...)。
146
+ current = get_config_func()
147
+ override: dict[str, Any] = {}
148
+ cursor: dict[str, Any] = override
149
+ for k in keys[:-1]:
150
+ cursor[k] = {}
151
+ cursor = cursor[k]
152
+ cursor[keys[-1]] = _coerce_cli_value(value)
153
+
154
+ try:
155
+ new_cfg = current.update(**override)
156
+ except ValidationError as e:
157
+ raise click.ClickException(f"配置项 {key!r} 校验失败: {e}")
158
+
159
+ target = new_cfg.save()
160
+ reload_config_func()
161
+
162
+ click.echo(f"[OK] 已设置 {config_type}.{key} = {value}")
163
+ click.echo(f" 配置文件: {target}")
164
+ click.echo()
165
+ click.echo("提示: 某些配置可能需要重启应用才能生效")
166
+
167
+ except click.ClickException:
168
+ raise
169
+ except Exception as e:
170
+ logger.error(f"设置配置失败: {e}")
171
+ raise click.ClickException(f"设置配置失败: {e}")
172
+
173
+
174
+ @click.command()
175
+ @click.argument("key")
176
+ @click.option('--type', 'config_type', default='main', help='配置类型:main(主配置) 或插件名称(如 quant, wiki)')
177
+ def get(key, config_type: str):
178
+ """获取指定配置项的值
179
+
180
+ 示例:
181
+ mos config get log.level
182
+ mos config get debug --type main
183
+ mos config get database.path --type quant
184
+ """
185
+ try:
186
+ get_config_func, _ = _get_config_funcs(config_type)
187
+ config_obj = get_config_func()
188
+ value = config_obj.get(*key.split("."))
189
+
190
+ if value is None:
191
+ click.echo(f"配置项 '{config_type}.{key}' 不存在")
192
+ return
193
+
194
+ click.echo(f"{config_type}.{key} = {value}")
195
+
196
+ except Exception as e:
197
+ logger.error(f"获取配置失败: {e}")
198
+ raise click.ClickException(f"获取配置失败: {e}")
199
+
200
+
201
+ @click.command()
202
+ @click.option('--type', 'config_type', default='main', help='配置类型:main(主配置) 或插件名称(如 quant, wiki)')
203
+ def types(config_type: str):
204
+ """列出所有可用的配置类型"""
205
+ registry = get_registry()
206
+ plugin_names = registry.list_names()
207
+
208
+ click.echo("可用的配置类型:")
209
+ click.echo(" main - MOS 主配置 (log, llm, debug)")
210
+ for name in plugin_names:
211
+ click.echo(f" {name} - {name} 插件配置")
212
+
213
+ click.echo()
214
+ click.echo("示例:")
215
+ click.echo(" mos config list --type main")
216
+ click.echo(" mos config list --type quant")
217
+
218
+
219
+ config.add_command(list)
220
+ config.add_command(set)
221
+ config.add_command(get)
222
+ config.add_command(types)
mos/cli/init.py ADDED
@@ -0,0 +1,229 @@
1
+ import click
2
+ from pathlib import Path
3
+ import json
4
+ from typing import Optional
5
+ from mos.core.config import get_config
6
+ from mos.core.logging import get_logger
7
+ from mos.core.plugin import get_registry
8
+
9
+ logger = get_logger(__name__)
10
+
11
+
12
+ def _get_mos_dir():
13
+ """获取 MOS 根目录"""
14
+ return Path.home() / ".mos"
15
+
16
+
17
+ def _init_config_file(force=False):
18
+ """初始化配置文件"""
19
+ config_file = _get_mos_dir() / "config.json"
20
+
21
+ if config_file.exists():
22
+ if force:
23
+ config_file.unlink()
24
+ click.echo(f" ⚠ 已删除旧配置文件: {config_file}")
25
+ else:
26
+ click.echo(f" [OK] 配置文件已存在: {config_file}")
27
+ return False
28
+
29
+ config = get_config()
30
+ config_dict = config.model_dump()
31
+
32
+ config_file.parent.mkdir(parents=True, exist_ok=True)
33
+ with open(config_file, 'w', encoding='utf-8') as f:
34
+ json.dump(config_dict, f, indent=2, ensure_ascii=False)
35
+
36
+ click.echo(f" [OK] 已创建配置文件: {config_file}")
37
+ return True
38
+
39
+
40
+ def _init_log_dir():
41
+ """初始化日志目录"""
42
+ log_dir = _get_mos_dir() / "logs"
43
+
44
+ if log_dir.exists():
45
+ click.echo(f" [OK] 日志目录已存在: {log_dir}")
46
+ return False
47
+
48
+ log_dir.mkdir(parents=True, exist_ok=True)
49
+ click.echo(f" [OK] 已创建日志目录: {log_dir}")
50
+ return True
51
+
52
+
53
+ def _init_cache_dir():
54
+ """初始化缓存目录"""
55
+ cache_dir = _get_mos_dir() / "cache"
56
+
57
+ if cache_dir.exists():
58
+ click.echo(f" [OK] 缓存目录已存在: {cache_dir}")
59
+ return False
60
+
61
+ cache_dir.mkdir(parents=True, exist_ok=True)
62
+ click.echo(f" [OK] 已创建缓存目录: {cache_dir}")
63
+ return True
64
+
65
+
66
+ def _init_plugin_dir():
67
+ """初始化插件目录。
68
+
69
+ 使用 ``Config.plugin.plugin_path`` 作为根目录;首次安装的
70
+ external 插件(通过 ``mos plugin install``)会被 ``git clone``
71
+ 到该目录下。
72
+ """
73
+ config = get_config()
74
+ plugin_dir = Path(config.plugin.get_expanded_path())
75
+
76
+ if plugin_dir.exists():
77
+ click.echo(f" [OK] 插件目录已存在: {plugin_dir}")
78
+ return False
79
+
80
+ plugin_dir.mkdir(parents=True, exist_ok=True)
81
+ click.echo(f" [OK] 已创建插件目录: {plugin_dir}")
82
+ return True
83
+
84
+
85
+ def _init_core(force=False):
86
+ """初始化 MOS 核心框架。"""
87
+ click.echo()
88
+ click.echo("=" * 60)
89
+ click.echo("MOS 核心框架初始化")
90
+ click.echo("=" * 60)
91
+ click.echo()
92
+
93
+ mos_dir = _get_mos_dir()
94
+ click.echo(f"MOS 根目录: {mos_dir}")
95
+ click.echo()
96
+
97
+ if not mos_dir.exists():
98
+ click.echo("创建 MOS 根目录...")
99
+ mos_dir.mkdir(parents=True, exist_ok=True)
100
+ click.echo(f" [OK] 已创建: {mos_dir}")
101
+ else:
102
+ click.echo(f" [OK] MOS 根目录已存在: {mos_dir}")
103
+
104
+ click.echo()
105
+ click.echo("初始化配置文件...")
106
+ _init_config_file(force)
107
+
108
+ click.echo()
109
+ click.echo("初始化日志目录...")
110
+ _init_log_dir()
111
+
112
+ click.echo()
113
+ click.echo("初始化缓存目录...")
114
+ _init_cache_dir()
115
+
116
+ click.echo()
117
+ click.echo("初始化插件目录...")
118
+ _init_plugin_dir()
119
+
120
+
121
+ def _init_plugin(plugin_name: str, force: bool = False):
122
+ """初始化单个插件。"""
123
+ registry = get_registry()
124
+ plugin = registry.get(plugin_name)
125
+
126
+ if plugin is None:
127
+ click.echo(f" [ERROR] 插件 '{plugin_name}' 未加载或未安装")
128
+ return False
129
+
130
+ if plugin.init is None:
131
+ click.echo(f" [SKIP] 插件 '{plugin_name}' 没有初始化函数")
132
+ return False
133
+
134
+ click.echo()
135
+ click.echo("=" * 60)
136
+ click.echo(f"初始化插件: {plugin_name}")
137
+ click.echo("=" * 60)
138
+ click.echo()
139
+
140
+ try:
141
+ plugin.init(force)
142
+ click.echo()
143
+ click.echo(f"[OK] 插件 '{plugin_name}' 初始化完成")
144
+ return True
145
+ except Exception as e:
146
+ logger.error(f"Plugin {plugin_name} init failed: {e}")
147
+ click.echo(f" [ERROR] 插件 '{plugin_name}' 初始化失败: {e}")
148
+ return False
149
+
150
+
151
+ def _init_all_plugins(force: bool = False):
152
+ """初始化所有已加载的插件。"""
153
+ registry = get_registry()
154
+ plugins = registry.all()
155
+
156
+ if not plugins:
157
+ click.echo(" [INFO] 没有已加载的插件")
158
+ return
159
+
160
+ click.echo()
161
+ click.echo("=" * 60)
162
+ click.echo("初始化所有插件")
163
+ click.echo("=" * 60)
164
+ click.echo()
165
+
166
+ initialized_count = 0
167
+ skipped_count = 0
168
+
169
+ for plugin in plugins:
170
+ if plugin.init is None:
171
+ click.echo(f" [SKIP] 插件 '{plugin.name}' 没有初始化函数")
172
+ skipped_count += 1
173
+ continue
174
+
175
+ click.echo(f"初始化插件: {plugin.name}")
176
+ try:
177
+ plugin.init(force)
178
+ click.echo(f" [OK] 插件 '{plugin.name}' 初始化完成")
179
+ initialized_count += 1
180
+ except Exception as e:
181
+ logger.error(f"Plugin {plugin.name} init failed: {e}")
182
+ click.echo(f" [ERROR] 插件 '{plugin.name}' 初始化失败: {e}")
183
+
184
+ click.echo()
185
+ click.echo(f"初始化完成: {initialized_count} 个插件, {skipped_count} 个跳过")
186
+
187
+
188
+ @click.command()
189
+ @click.option('--force', '-f', is_flag=True, help='强制重新初始化(覆盖现有配置)')
190
+ @click.option('--plugin', '-p', 'plugin_name', help='初始化指定插件')
191
+ @click.option('--all', 'init_all', is_flag=True, help='初始化所有已加载的插件')
192
+ def init(force: bool, plugin_name: Optional[str], init_all: bool):
193
+ """初始化 MOS 配置和环境
194
+
195
+ 首次使用或重新配置时运行此命令。
196
+ 将创建必要的配置文件和目录结构。
197
+
198
+ 使用方式:
199
+ mos init # 初始化核心框架
200
+ mos init -p quant # 初始化 quant 插件
201
+ mos init --all # 初始化所有插件
202
+ mos init -f # 强制重新初始化
203
+ """
204
+ # 如果指定了插件,只初始化插件
205
+ if plugin_name:
206
+ _init_plugin(plugin_name, force)
207
+ return
208
+
209
+ # 如果指定了 --all,初始化所有插件
210
+ if init_all:
211
+ _init_core(force)
212
+ _init_all_plugins(force)
213
+ return
214
+
215
+ # 默认只初始化核心框架
216
+ _init_core(force)
217
+
218
+ click.echo()
219
+ click.echo("=" * 60)
220
+ click.echo("[OK] 初始化完成!")
221
+ click.echo("=" * 60)
222
+ click.echo()
223
+ click.echo("接下来你可以:")
224
+ click.echo(" 1. 使用 'mos config list' 查看配置")
225
+ click.echo(" 2. 使用 'mos config set <key> <value>' 修改配置")
226
+ click.echo(" 3. 使用 'mos init -p <plugin>' 初始化插件")
227
+ click.echo()
228
+ click.echo("更多信息请参考项目文档")
229
+ click.echo()
mos/cli/mcp.py ADDED
@@ -0,0 +1,13 @@
1
+ import click
2
+ from mos.core.logging import get_logger
3
+
4
+ logger = get_logger(__name__)
5
+
6
+ @click.command()
7
+ def mcp():
8
+ """
9
+ MCP命令
10
+ """
11
+ from mos.core.mcp import mcp
12
+ logger.info("MCP server running...")
13
+ mcp.run(transport="streamable-http")
mos/cli/mos.py ADDED
@@ -0,0 +1,43 @@
1
+ import click
2
+
3
+ from mos.core.logging import setup_logging
4
+ from mos.core.config import get_config
5
+ from mos.core.plugin import load_entry_point_plugins
6
+ from mos.cli.config import config
7
+ from mos.cli.init import init
8
+ from mos.cli.plugin import plugin as plugin_cmd
9
+ from mos.cli.mcp import mcp
10
+
11
+
12
+ @click.group()
13
+ @click.version_option()
14
+ def cli():
15
+ setup_logging()
16
+
17
+
18
+ cli.add_command(config)
19
+ cli.add_command(init)
20
+ cli.add_command(plugin_cmd)
21
+ cli.add_command(mcp)
22
+
23
+
24
+ cfg = get_config()
25
+ disabled_plugins = list(cfg.plugin.disabled_plugins)
26
+
27
+ # Load all plugins via entry_points (pip-installed packages)
28
+ ep_results = load_entry_point_plugins(disabled_plugins)
29
+ for r in ep_results:
30
+ if r.status == "loaded" and r.definition is not None:
31
+ cmd = r.definition.command
32
+ if cmd.name and cmd.name not in cli.commands:
33
+ cli.add_command(cmd)
34
+ if r.name != cmd.name and r.name not in cli.commands:
35
+ cli.add_command(cmd, name=r.name)
36
+ # Register MCP tools if plugin supports it
37
+ if r.definition.register_mcp:
38
+ from mos.core.mcp import mcp
39
+ r.definition.register_mcp(mcp)
40
+
41
+
42
+ if __name__ == "__main__":
43
+ cli()