mindroot 9.2.0__py3-none-any.whl → 9.5.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.
- mindroot/coreplugins/admin/__init__.py +3 -1
- mindroot/coreplugins/admin/agent_router.py +250 -7
- mindroot/coreplugins/admin/asset_manager.py +164 -0
- mindroot/coreplugins/admin/command_router.py +236 -1
- mindroot/coreplugins/admin/mcp_catalog_routes.py +156 -0
- mindroot/coreplugins/admin/mcp_publish_routes.py +450 -0
- mindroot/coreplugins/admin/mcp_registry_routes.py +495 -0
- mindroot/coreplugins/admin/mcp_routes.py +216 -0
- mindroot/coreplugins/admin/mod.py +62 -0
- mindroot/coreplugins/admin/oauth_callback_router.py +84 -0
- mindroot/coreplugins/admin/persona_handler.py +15 -6
- mindroot/coreplugins/admin/persona_router.py +158 -2
- mindroot/coreplugins/admin/plugin_manager.py +63 -0
- mindroot/coreplugins/admin/plugin_router.py +1 -1
- mindroot/coreplugins/admin/plugin_router_fixed.py +23 -0
- mindroot/coreplugins/admin/plugin_router_new_not_working.py +145 -0
- mindroot/coreplugins/admin/plugin_routes.py +114 -0
- mindroot/coreplugins/admin/registry_settings_routes.py +140 -0
- mindroot/coreplugins/admin/router.py +116 -15
- mindroot/coreplugins/admin/service_models.py +1 -1
- mindroot/coreplugins/admin/settings_router.py +1 -0
- mindroot/coreplugins/admin/static/css/admin-custom.css +357 -2
- mindroot/coreplugins/admin/static/css/dark.css +1 -0
- mindroot/coreplugins/admin/static/css/default.css +4 -0
- mindroot/coreplugins/admin/static/js/about-info.js +367 -0
- mindroot/coreplugins/admin/static/js/agent-form.js +83 -3
- mindroot/coreplugins/admin/static/js/api-key-script.js +307 -0
- mindroot/coreplugins/admin/static/js/mcp-manager.js +348 -0
- mindroot/coreplugins/admin/static/js/mcp-publisher.js +780 -0
- mindroot/coreplugins/admin/static/js/persona-editor.js +34 -5
- mindroot/coreplugins/admin/static/js/plugin-toggle.js +1 -1
- mindroot/coreplugins/admin/static/js/recommended-plugin-install.js +63 -0
- mindroot/coreplugins/admin/static/js/registry-auth-section.js +132 -0
- mindroot/coreplugins/admin/static/js/registry-manager-base.js +613 -0
- mindroot/coreplugins/admin/static/js/registry-manager-old.js +385 -0
- mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
- mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
- mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
- mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
- mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
- mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
- mindroot/coreplugins/admin/static/js/registry-shared-services.js +857 -0
- mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
- mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
- mindroot/coreplugins/admin/static/logo.png +0 -0
- mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
- mindroot/coreplugins/agent/Assistant/agent.json +27 -11
- mindroot/coreplugins/agent/agent.py +2 -2
- mindroot/coreplugins/agent/command_parser.py +25 -10
- mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
- mindroot/coreplugins/chat/__init__.py +4 -1
- mindroot/coreplugins/chat/router.py +132 -20
- mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
- mindroot/coreplugins/chat/services.py +31 -1
- mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
- mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
- mindroot/coreplugins/chat/static/css/dark.css +24 -3
- mindroot/coreplugins/chat/static/css/default.css +24 -3
- mindroot/coreplugins/chat/static/css/main.css +1 -0
- mindroot/coreplugins/chat/static/js/action.js +137 -60
- mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
- mindroot/coreplugins/chat/static/js/chat.js +59 -16
- mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
- mindroot/coreplugins/chat/static/js/chatform.js +2 -2
- mindroot/coreplugins/chat/static/site.webmanifest +1 -1
- mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
- mindroot/coreplugins/chat/widget_manager.py +139 -0
- mindroot/coreplugins/chat/widget_routes.py +287 -0
- mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
- mindroot/coreplugins/email/__init__.py +2 -0
- mindroot/coreplugins/email/email_provider.py +2 -2
- mindroot/coreplugins/email/mod.py +100 -0
- mindroot/coreplugins/email/services.py +5 -3
- mindroot/coreplugins/email/smtp_handler.py +9 -3
- mindroot/coreplugins/email/test_email_service.py +75 -0
- mindroot/coreplugins/env_manager/mod.py +61 -25
- mindroot/coreplugins/home/router.py +37 -2
- mindroot/coreplugins/home/static/imgs/logo.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
- mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
- mindroot/coreplugins/home/templates/home.jinja2 +15 -6
- mindroot/coreplugins/index/handlers/plugin_ops.py +1 -1
- mindroot/coreplugins/index/indices/default/index.json +6 -6
- mindroot/coreplugins/jwt_auth/middleware.py +47 -1
- mindroot/coreplugins/jwt_auth/mod.py +40 -17
- mindroot/coreplugins/l8n/__init__.py +6 -0
- mindroot/coreplugins/l8n/debug_loader.py +85 -0
- mindroot/coreplugins/l8n/debug_middleware.py +74 -0
- mindroot/coreplugins/l8n/l8n_constants.py +19 -0
- mindroot/coreplugins/l8n/language_detection.py +183 -0
- mindroot/coreplugins/l8n/middleware.py +151 -0
- mindroot/coreplugins/l8n/mod.py +277 -0
- mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
- mindroot/coreplugins/l8n/test_enhanced.py +298 -0
- mindroot/coreplugins/l8n/test_l8n.py +95 -0
- mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
- mindroot/coreplugins/l8n/test_middleware.py +272 -0
- mindroot/coreplugins/l8n/utils.py +232 -0
- mindroot/coreplugins/mcp_/__init__.py +14 -0
- mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
- mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
- mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
- mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
- mindroot/coreplugins/mcp_/mod.py +367 -0
- mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
- mindroot/coreplugins/mcp_/server_installer.py +79 -0
- mindroot/coreplugins/mcp_/setup.py +26 -0
- mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
- mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
- mindroot/coreplugins/persona/mod.py +12 -7
- mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
- mindroot/coreplugins/subscriptions/__init__.py +1 -0
- mindroot/coreplugins/subscriptions/mod.py +14 -3
- mindroot/coreplugins/subscriptions/router.py +3 -0
- mindroot/coreplugins/user_service/__init__.py +1 -2
- mindroot/coreplugins/user_service/admin_init.py +1 -0
- mindroot/coreplugins/user_service/email_service.py +72 -17
- mindroot/coreplugins/user_service/mod.py +10 -2
- mindroot/coreplugins/user_service/password_reset_service.py +180 -27
- mindroot/coreplugins/user_service/router.py +84 -22
- mindroot/lib/auth/api_key.py +28 -0
- mindroot/lib/cli/plugins.py +94 -0
- mindroot/lib/plugins/default_plugin_manifest.json +20 -0
- mindroot/lib/plugins/installation.py +5 -5
- mindroot/lib/plugins/l8n_static_handler.py +225 -0
- mindroot/lib/plugins/loader.py +33 -3
- mindroot/lib/plugins/loader_with_l8n.py +281 -0
- mindroot/lib/plugins/manifest.py +238 -17
- mindroot/lib/providers/commands.py +3 -1
- mindroot/lib/route_decorators.py +5 -5
- mindroot/lib/templates.py +183 -11
- mindroot/lib/utils/merge_arrays.py +1 -1
- mindroot/migrate.py +49 -0
- mindroot/registry/data_access.py +1 -1
- mindroot/server.py +47 -13
- mindroot/server_missing_normal_args.py +197 -0
- mindroot/server_prev.py +173 -0
- {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/METADATA +7 -2
- {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/RECORD +147 -114
- mindroot/coreplugins/admin/static/favicon/about.txt +0 -6
- mindroot/coreplugins/admin/static/favicon/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/admin/static/favicon/apple-touch-icon.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon-16x16.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon-32x32.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon.ico +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/about.txt +0 -6
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon.ico +0 -0
- mindroot/coreplugins/admin/static/favicon/favicon_io (1)/site.webmanifest +0 -1
- mindroot/coreplugins/admin/static/favicon/logo.png +0 -0
- mindroot/coreplugins/admin/static/favicon/site.webmanifest +0 -1
- mindroot/coreplugins/admin/static/js/backup/agent-editor.js +0 -186
- mindroot/coreplugins/admin/static/js/backup/agent-form.js +0 -1133
- mindroot/coreplugins/admin/static/js/backup/agent-list.js +0 -94
- mindroot/coreplugins/chat/static/favicon/about.txt +0 -6
- mindroot/coreplugins/chat/static/favicon/android-chrome-192x192.png +0 -0
- mindroot/coreplugins/chat/static/favicon/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/chat/static/favicon/apple-touch-icon.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon-16x16.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon-32x32.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon.ico +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/about.txt +0 -6
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon.ico +0 -0
- mindroot/coreplugins/chat/static/favicon/favicon_io (1)/site.webmanifest +0 -1
- mindroot/coreplugins/chat/static/favicon/logo.png +0 -0
- mindroot/coreplugins/chat/static/favicon/site.webmanifest +0 -1
- mindroot/coreplugins/index/default.json +0 -76
- mindroot/coreplugins/user_service/file_trigger_service.py +0 -72
- mindroot/coreplugins/user_service/hooks.py +0 -23
- /mindroot/coreplugins/{admin/static/favicon/android-chrome-192x192.png → home/static/imgs/backuplogo.png} +0 -0
- {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/WHEEL +0 -0
- {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/entry_points.txt +0 -0
- {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/licenses/LICENSE +0 -0
- {mindroot-9.2.0.dist-info → mindroot-9.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import psutil
|
|
4
|
+
import subprocess
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, List, Optional, Any
|
|
7
|
+
|
|
8
|
+
class MCPCatalogManager:
|
|
9
|
+
"""Manages MCP server catalog and process detection"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, plugin_dir: str = None, working_dir: str = None):
|
|
12
|
+
# Default plugin_dir to the directory containing this module
|
|
13
|
+
if plugin_dir is None:
|
|
14
|
+
plugin_dir = Path(__file__).parent.parent.parent
|
|
15
|
+
self.plugin_dir = Path(plugin_dir)
|
|
16
|
+
|
|
17
|
+
# Use process working directory for data files
|
|
18
|
+
if working_dir is None:
|
|
19
|
+
working_dir = os.getcwd()
|
|
20
|
+
self.working_dir = Path(working_dir)
|
|
21
|
+
|
|
22
|
+
self.catalog_file = self.working_dir / "data" / "mcp" / "servers.json"
|
|
23
|
+
self.default_file = Path(__file__).parent / "data" / "default_mcp_servers.json"
|
|
24
|
+
self.ensure_catalog_exists()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def ensure_catalog_exists(self):
|
|
28
|
+
"""Ensure catalog file exists, create from default if needed"""
|
|
29
|
+
if not self.catalog_file.exists():
|
|
30
|
+
if self.default_file.exists():
|
|
31
|
+
self.init_catalog_from_default()
|
|
32
|
+
else:
|
|
33
|
+
self.create_empty_catalog()
|
|
34
|
+
|
|
35
|
+
def init_catalog_from_default(self):
|
|
36
|
+
"""Initialize catalog from default servers"""
|
|
37
|
+
# Ensure the target directory exists
|
|
38
|
+
self.catalog_file.parent.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
with open(self.default_file, 'r') as f:
|
|
42
|
+
default_servers = json.load(f)
|
|
43
|
+
|
|
44
|
+
catalog = {
|
|
45
|
+
"_metadata": {
|
|
46
|
+
"version": "1.0.0",
|
|
47
|
+
"last_updated": "2025-06-02",
|
|
48
|
+
"description": "MCP Server Catalog for MindRoot",
|
|
49
|
+
"categories": ["utilities", "system", "development", "search", "database", "communication", "cloud", "automation", "ai"]
|
|
50
|
+
},
|
|
51
|
+
"servers": {}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for name, server in default_servers.items():
|
|
55
|
+
server["status"] = "available"
|
|
56
|
+
server["installed"] = False
|
|
57
|
+
server["running"] = False
|
|
58
|
+
catalog["servers"][name] = server
|
|
59
|
+
|
|
60
|
+
with open(self.catalog_file, 'w') as f:
|
|
61
|
+
json.dump(catalog, f, indent=2)
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
print(f"Error initializing catalog: {e}")
|
|
65
|
+
self.create_empty_catalog()
|
|
66
|
+
|
|
67
|
+
def create_empty_catalog(self):
|
|
68
|
+
"""Create empty catalog structure"""
|
|
69
|
+
catalog = {
|
|
70
|
+
"_metadata": {
|
|
71
|
+
"version": "1.0.0",
|
|
72
|
+
"last_updated": "2025-06-02",
|
|
73
|
+
"description": "MCP Server Catalog for MindRoot",
|
|
74
|
+
"categories": []
|
|
75
|
+
},
|
|
76
|
+
"servers": {}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
self.catalog_file.parent.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
with open(self.catalog_file, 'w') as f:
|
|
81
|
+
json.dump(catalog, f, indent=2)
|
|
82
|
+
|
|
83
|
+
def load_catalog(self) -> Dict[str, Any]:
|
|
84
|
+
"""Load the server catalog"""
|
|
85
|
+
try:
|
|
86
|
+
with open(self.catalog_file, 'r') as f:
|
|
87
|
+
return json.load(f)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
print(f"Error loading catalog: {e}")
|
|
90
|
+
return {"_metadata": {}, "servers": {}}
|
|
91
|
+
|
|
92
|
+
def save_catalog(self, catalog: Dict[str, Any]):
|
|
93
|
+
"""Save the server catalog"""
|
|
94
|
+
try:
|
|
95
|
+
with open(self.catalog_file, 'w') as f:
|
|
96
|
+
json.dump(catalog, f, indent=2)
|
|
97
|
+
except Exception as e:
|
|
98
|
+
print(f"Error saving catalog: {e}")
|
|
99
|
+
|
|
100
|
+
def get_running_processes(self) -> List[Dict[str, Any]]:
|
|
101
|
+
"""Get list of running processes with details"""
|
|
102
|
+
processes = []
|
|
103
|
+
try:
|
|
104
|
+
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
|
|
105
|
+
try:
|
|
106
|
+
processes.append({
|
|
107
|
+
'pid': proc.info['pid'],
|
|
108
|
+
'name': proc.info['name'],
|
|
109
|
+
'cmdline': proc.info['cmdline'] or []
|
|
110
|
+
})
|
|
111
|
+
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
|
112
|
+
continue
|
|
113
|
+
except Exception as e:
|
|
114
|
+
print(f"Error getting processes: {e}")
|
|
115
|
+
|
|
116
|
+
return processes
|
|
117
|
+
|
|
118
|
+
def detect_running_servers(self) -> Dict[str, bool]:
|
|
119
|
+
"""Detect which MCP servers are currently running"""
|
|
120
|
+
catalog = self.load_catalog()
|
|
121
|
+
running_status = {}
|
|
122
|
+
processes = self.get_running_processes()
|
|
123
|
+
|
|
124
|
+
for server_name, server_info in catalog.get("servers", {}).items():
|
|
125
|
+
is_running = False
|
|
126
|
+
process_names = server_info.get("process_names", [])
|
|
127
|
+
|
|
128
|
+
for proc in processes:
|
|
129
|
+
# Check process name
|
|
130
|
+
if proc['name'] in process_names:
|
|
131
|
+
is_running = True
|
|
132
|
+
break
|
|
133
|
+
|
|
134
|
+
# Check command line for MCP server patterns
|
|
135
|
+
cmdline_str = ' '.join(proc['cmdline'])
|
|
136
|
+
for process_name in process_names:
|
|
137
|
+
if process_name in cmdline_str:
|
|
138
|
+
is_running = True
|
|
139
|
+
break
|
|
140
|
+
|
|
141
|
+
# Check for specific patterns
|
|
142
|
+
if server_name in cmdline_str or server_info.get('install_package', '') in cmdline_str:
|
|
143
|
+
is_running = True
|
|
144
|
+
break
|
|
145
|
+
|
|
146
|
+
if is_running:
|
|
147
|
+
break
|
|
148
|
+
|
|
149
|
+
running_status[server_name] = is_running
|
|
150
|
+
|
|
151
|
+
return running_status
|
|
152
|
+
|
|
153
|
+
def update_server_status(self):
|
|
154
|
+
"""Update running status for all servers in catalog"""
|
|
155
|
+
catalog = self.load_catalog()
|
|
156
|
+
running_status = self.detect_running_servers()
|
|
157
|
+
|
|
158
|
+
for server_name, is_running in running_status.items():
|
|
159
|
+
if server_name in catalog.get("servers", {}):
|
|
160
|
+
catalog["servers"][server_name]["running"] = is_running
|
|
161
|
+
|
|
162
|
+
self.save_catalog(catalog)
|
|
163
|
+
return catalog
|
|
164
|
+
|
|
165
|
+
def get_servers_by_category(self, category: str = None) -> Dict[str, Any]:
|
|
166
|
+
"""Get servers filtered by category"""
|
|
167
|
+
catalog = self.load_catalog()
|
|
168
|
+
servers = catalog.get("servers", {})
|
|
169
|
+
|
|
170
|
+
if category is None:
|
|
171
|
+
return servers
|
|
172
|
+
|
|
173
|
+
filtered = {}
|
|
174
|
+
for name, server in servers.items():
|
|
175
|
+
if server.get("category") == category:
|
|
176
|
+
filtered[name] = server
|
|
177
|
+
|
|
178
|
+
return filtered
|
|
179
|
+
|
|
180
|
+
def get_server_info(self, server_name: str) -> Optional[Dict[str, Any]]:
|
|
181
|
+
"""Get detailed info for a specific server"""
|
|
182
|
+
catalog = self.load_catalog()
|
|
183
|
+
return catalog.get("servers", {}).get(server_name)
|
|
184
|
+
|
|
185
|
+
def mark_server_installed(self, server_name: str, installed: bool = True):
|
|
186
|
+
"""Mark a server as installed or not"""
|
|
187
|
+
catalog = self.load_catalog()
|
|
188
|
+
if server_name in catalog.get("servers", {}):
|
|
189
|
+
catalog["servers"][server_name]["installed"] = installed
|
|
190
|
+
self.save_catalog(catalog)
|
|
191
|
+
|
|
192
|
+
def add_custom_server(self, server_info: Dict[str, Any]):
|
|
193
|
+
"""Add a custom server to the catalog"""
|
|
194
|
+
catalog = self.load_catalog()
|
|
195
|
+
server_name = server_info.get("name")
|
|
196
|
+
|
|
197
|
+
if server_name:
|
|
198
|
+
# Add default fields if missing
|
|
199
|
+
server_info.setdefault("status", "custom")
|
|
200
|
+
server_info.setdefault("installed", False)
|
|
201
|
+
server_info.setdefault("running", False)
|
|
202
|
+
server_info.setdefault("category", "custom")
|
|
203
|
+
server_info.setdefault("tags", [])
|
|
204
|
+
|
|
205
|
+
catalog["servers"][server_name] = server_info
|
|
206
|
+
self.save_catalog(catalog)
|
|
207
|
+
return True
|
|
208
|
+
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
def remove_server(self, server_name: str):
|
|
212
|
+
"""Remove a server from the catalog"""
|
|
213
|
+
catalog = self.load_catalog()
|
|
214
|
+
if server_name in catalog.get("servers", {}):
|
|
215
|
+
del catalog["servers"][server_name]
|
|
216
|
+
self.save_catalog(catalog)
|
|
217
|
+
return True
|
|
218
|
+
return False
|
|
219
|
+
|
|
220
|
+
def search_servers(self, query: str) -> Dict[str, Any]:
|
|
221
|
+
"""Search servers by name, description, or tags"""
|
|
222
|
+
catalog = self.load_catalog()
|
|
223
|
+
servers = catalog.get("servers", {})
|
|
224
|
+
results = {}
|
|
225
|
+
|
|
226
|
+
query_lower = query.lower()
|
|
227
|
+
|
|
228
|
+
for name, server in servers.items():
|
|
229
|
+
# Search in name
|
|
230
|
+
if query_lower in name.lower():
|
|
231
|
+
results[name] = server
|
|
232
|
+
continue
|
|
233
|
+
|
|
234
|
+
# Search in display name
|
|
235
|
+
if query_lower in server.get("display_name", "").lower():
|
|
236
|
+
results[name] = server
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
# Search in description
|
|
240
|
+
if query_lower in server.get("description", "").lower():
|
|
241
|
+
results[name] = server
|
|
242
|
+
continue
|
|
243
|
+
|
|
244
|
+
# Search in tags
|
|
245
|
+
tags = server.get("tags", [])
|
|
246
|
+
if any(query_lower in tag.lower() for tag in tags):
|
|
247
|
+
results[name] = server
|
|
248
|
+
continue
|
|
249
|
+
|
|
250
|
+
return results
|
|
251
|
+
|
|
252
|
+
def get_categories(self) -> List[str]:
|
|
253
|
+
"""Get list of all categories"""
|
|
254
|
+
catalog = self.load_catalog()
|
|
255
|
+
categories = set(catalog.get("_metadata", {}).get("categories", []))
|
|
256
|
+
|
|
257
|
+
# Add categories from servers
|
|
258
|
+
for server in catalog.get("servers", {}).values():
|
|
259
|
+
category = server.get("category")
|
|
260
|
+
if category:
|
|
261
|
+
categories.add(category)
|
|
262
|
+
|
|
263
|
+
return sorted(list(categories))
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
from typing import Dict, List, Any
|
|
2
|
+
import json
|
|
3
|
+
import inspect
|
|
4
|
+
from lib.providers.commands import command_manager
|
|
5
|
+
|
|
6
|
+
class MCPDynamicCommands:
|
|
7
|
+
"""Handles dynamic registration of MCP tools as MindRoot commands"""
|
|
8
|
+
|
|
9
|
+
def __init__(self):
|
|
10
|
+
self.registered_commands: Dict[str, str] = {} # cmd_name -> server_name
|
|
11
|
+
self.sessions: Dict[str, Any] = {} # Reference to sessions
|
|
12
|
+
|
|
13
|
+
def set_sessions(self, sessions: Dict[str, Any]):
|
|
14
|
+
"""Set reference to MCP sessions"""
|
|
15
|
+
self.sessions = sessions
|
|
16
|
+
|
|
17
|
+
async def register_tools(self, server_name: str, tools: List[Any]):
|
|
18
|
+
"""Register MCP tools as dynamic MindRoot commands"""
|
|
19
|
+
for tool in tools:
|
|
20
|
+
try:
|
|
21
|
+
tool_name = tool.name
|
|
22
|
+
#cmd_name = f"mcp_{server_name}_{tool_name}"
|
|
23
|
+
cmd_name = "mcp_"+tool_name
|
|
24
|
+
|
|
25
|
+
self.registered_commands[cmd_name] = server_name
|
|
26
|
+
|
|
27
|
+
# Create wrapper function with closure to capture current values
|
|
28
|
+
def create_wrapper(srv_name, tl_name):
|
|
29
|
+
async def mcp_tool_wrapper(*args, context=None, **kwargs):
|
|
30
|
+
if srv_name not in self.sessions:
|
|
31
|
+
return f"MCP server {srv_name} not connected"
|
|
32
|
+
|
|
33
|
+
session = self.sessions[srv_name]
|
|
34
|
+
|
|
35
|
+
# Convert arguments to MCP format
|
|
36
|
+
if len(args) == 1 and isinstance(args[0], dict):
|
|
37
|
+
arguments = args[0]
|
|
38
|
+
elif kwargs:
|
|
39
|
+
# Filter out context from kwargs
|
|
40
|
+
arguments = {k: v for k, v in kwargs.items() if k != 'context'}
|
|
41
|
+
else:
|
|
42
|
+
arguments = {}
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
result = await session.call_tool(tl_name, arguments)
|
|
46
|
+
|
|
47
|
+
# Extract content from CallToolResult object
|
|
48
|
+
if hasattr(result, 'content'):
|
|
49
|
+
if isinstance(result.content, list) and len(result.content) > 0:
|
|
50
|
+
# Return the text content of the first item
|
|
51
|
+
first_item = result.content[0]
|
|
52
|
+
return first_item.text if hasattr(first_item, 'text') else str(first_item)
|
|
53
|
+
else:
|
|
54
|
+
return str(result.content)
|
|
55
|
+
else:
|
|
56
|
+
return str(result)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
return f"Error calling MCP tool {tl_name}: {str(e)}"
|
|
59
|
+
|
|
60
|
+
return mcp_tool_wrapper
|
|
61
|
+
|
|
62
|
+
wrapper = create_wrapper(server_name, tool_name)
|
|
63
|
+
|
|
64
|
+
# Create docstring with parameter information
|
|
65
|
+
description = getattr(tool, 'description', 'No description available')
|
|
66
|
+
|
|
67
|
+
# Extract parameter schema if available
|
|
68
|
+
param_info = ""
|
|
69
|
+
example_args = {}
|
|
70
|
+
|
|
71
|
+
if hasattr(tool, 'inputSchema') and tool.inputSchema:
|
|
72
|
+
schema = tool.inputSchema
|
|
73
|
+
if isinstance(schema, dict) and 'properties' in schema:
|
|
74
|
+
properties = schema['properties']
|
|
75
|
+
required = schema.get('required', [])
|
|
76
|
+
|
|
77
|
+
param_lines = []
|
|
78
|
+
for param_name, param_def in properties.items():
|
|
79
|
+
param_type = param_def.get('type', 'any')
|
|
80
|
+
param_desc = param_def.get('description', '')
|
|
81
|
+
is_required = param_name in required
|
|
82
|
+
req_text = ' (required)' if is_required else ' (optional)'
|
|
83
|
+
param_lines.append(f" {param_name} ({param_type}){req_text}: {param_desc}")
|
|
84
|
+
|
|
85
|
+
# Create example value based on type
|
|
86
|
+
if param_type == 'string':
|
|
87
|
+
example_args[param_name] = f"example_{param_name}"
|
|
88
|
+
elif param_type == 'number':
|
|
89
|
+
example_args[param_name] = 42
|
|
90
|
+
elif param_type == 'boolean':
|
|
91
|
+
example_args[param_name] = True
|
|
92
|
+
else:
|
|
93
|
+
example_args[param_name] = f"value_for_{param_name}"
|
|
94
|
+
|
|
95
|
+
if param_lines:
|
|
96
|
+
param_info = "\n\nParameters:\n" + "\n".join(param_lines)
|
|
97
|
+
|
|
98
|
+
# Use actual parameter names in example if available
|
|
99
|
+
example_json = json.dumps(example_args, indent=2) if example_args else '{"arg1": "value1"}'
|
|
100
|
+
|
|
101
|
+
docstring = f"""MCP Tool: {tool_name} from {server_name}
|
|
102
|
+
|
|
103
|
+
{description}{param_info}
|
|
104
|
+
|
|
105
|
+
{description}
|
|
106
|
+
|
|
107
|
+
Example:
|
|
108
|
+
{{ "{cmd_name}": {example_json} }}
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
# Register with command manager
|
|
112
|
+
try:
|
|
113
|
+
command_manager.register_function(
|
|
114
|
+
name=cmd_name,
|
|
115
|
+
provider="mcp",
|
|
116
|
+
implementation=wrapper,
|
|
117
|
+
signature=inspect.signature(wrapper),
|
|
118
|
+
docstring=docstring,
|
|
119
|
+
flags=[]
|
|
120
|
+
)
|
|
121
|
+
print(f"Registered MCP command: {cmd_name}")
|
|
122
|
+
|
|
123
|
+
# Verify registration
|
|
124
|
+
if cmd_name in command_manager.functions:
|
|
125
|
+
print(f" ✅ Verified: {cmd_name} is in command manager")
|
|
126
|
+
else:
|
|
127
|
+
print(f" ❌ Warning: {cmd_name} not found in command manager after registration")
|
|
128
|
+
except Exception as e:
|
|
129
|
+
print(f"Error registering {cmd_name}: {e}")
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
print(f"Error processing tool {getattr(tool, 'name', 'unknown')}: {e}")
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
async def unregister_server_tools(self, server_name: str):
|
|
136
|
+
"""Unregister all tools for a server"""
|
|
137
|
+
commands_to_remove = []
|
|
138
|
+
for cmd_name, srv_name in self.registered_commands.items():
|
|
139
|
+
if srv_name == server_name:
|
|
140
|
+
commands_to_remove.append(cmd_name)
|
|
141
|
+
|
|
142
|
+
for cmd_name in commands_to_remove:
|
|
143
|
+
try:
|
|
144
|
+
if cmd_name in command_manager.functions:
|
|
145
|
+
del command_manager.functions[cmd_name]
|
|
146
|
+
del self.registered_commands[cmd_name]
|
|
147
|
+
print(f"Unregistered MCP command: {cmd_name}")
|
|
148
|
+
except Exception as e:
|
|
149
|
+
print(f"Error unregistering {cmd_name}: {e}")
|
|
150
|
+
|
|
151
|
+
def get_registered_commands(self) -> List[str]:
|
|
152
|
+
"""Get list of registered command names"""
|
|
153
|
+
return list(self.registered_commands.keys())
|
|
154
|
+
|