mindroot 9.3.0__py3-none-any.whl → 9.6.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.
Files changed (183) hide show
  1. mindroot/coreplugins/admin/__init__.py +3 -1
  2. mindroot/coreplugins/admin/agent_router.py +250 -7
  3. mindroot/coreplugins/admin/asset_manager.py +164 -0
  4. mindroot/coreplugins/admin/command_router.py +236 -1
  5. mindroot/coreplugins/admin/mcp_catalog_routes.py +156 -0
  6. mindroot/coreplugins/admin/mcp_publish_routes.py +450 -0
  7. mindroot/coreplugins/admin/mcp_registry_routes.py +495 -0
  8. mindroot/coreplugins/admin/mcp_routes.py +216 -0
  9. mindroot/coreplugins/admin/mod.py +62 -0
  10. mindroot/coreplugins/admin/oauth_callback_router.py +84 -0
  11. mindroot/coreplugins/admin/persona_handler.py +15 -6
  12. mindroot/coreplugins/admin/persona_router.py +158 -2
  13. mindroot/coreplugins/admin/plugin_manager.py +105 -9
  14. mindroot/coreplugins/admin/plugin_router_fixed.py +23 -0
  15. mindroot/coreplugins/admin/plugin_router_new_not_working.py +145 -0
  16. mindroot/coreplugins/admin/plugin_routes.py +114 -0
  17. mindroot/coreplugins/admin/registry_settings_routes.py +140 -0
  18. mindroot/coreplugins/admin/router.py +116 -15
  19. mindroot/coreplugins/admin/service_models.py +1 -1
  20. mindroot/coreplugins/admin/settings_router.py +1 -0
  21. mindroot/coreplugins/admin/static/css/admin-custom.css +357 -2
  22. mindroot/coreplugins/admin/static/css/dark.css +1 -0
  23. mindroot/coreplugins/admin/static/css/default.css +4 -0
  24. mindroot/coreplugins/admin/static/js/about-info.js +367 -0
  25. mindroot/coreplugins/admin/static/js/agent-form.js +83 -3
  26. mindroot/coreplugins/admin/static/js/api-key-script.js +307 -0
  27. mindroot/coreplugins/admin/static/js/mcp-manager.js +348 -0
  28. mindroot/coreplugins/admin/static/js/mcp-publisher.js +780 -0
  29. mindroot/coreplugins/admin/static/js/persona-editor.js +34 -5
  30. mindroot/coreplugins/admin/static/js/plugin-toggle.js +1 -1
  31. mindroot/coreplugins/admin/static/js/recommended-plugin-install.js +63 -0
  32. mindroot/coreplugins/admin/static/js/registry-auth-section.js +132 -0
  33. mindroot/coreplugins/admin/static/js/registry-manager-base.js +613 -0
  34. mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
  35. mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
  36. mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
  37. mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
  38. mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
  39. mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
  40. mindroot/coreplugins/admin/static/js/registry-shared-services.js +903 -0
  41. mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
  42. mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
  43. mindroot/coreplugins/admin/static/logo.png +0 -0
  44. mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
  45. mindroot/coreplugins/agent/Assistant/agent.json +27 -11
  46. mindroot/coreplugins/agent/agent.py +2 -2
  47. mindroot/coreplugins/agent/command_parser.py +25 -10
  48. mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
  49. mindroot/coreplugins/chat/__init__.py +4 -1
  50. mindroot/coreplugins/chat/router.py +132 -20
  51. mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
  52. mindroot/coreplugins/chat/services.py +31 -1
  53. mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
  54. mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
  55. mindroot/coreplugins/chat/static/css/dark.css +24 -3
  56. mindroot/coreplugins/chat/static/css/default.css +24 -3
  57. mindroot/coreplugins/chat/static/css/main.css +1 -0
  58. mindroot/coreplugins/chat/static/js/action.js +137 -60
  59. mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
  60. mindroot/coreplugins/chat/static/js/chat.js +59 -16
  61. mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
  62. mindroot/coreplugins/chat/static/js/chatform.js +2 -2
  63. mindroot/coreplugins/chat/static/site.webmanifest +1 -1
  64. mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
  65. mindroot/coreplugins/chat/widget_manager.py +139 -0
  66. mindroot/coreplugins/chat/widget_routes.py +287 -0
  67. mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
  68. mindroot/coreplugins/email/__init__.py +2 -0
  69. mindroot/coreplugins/email/email_provider.py +2 -2
  70. mindroot/coreplugins/email/mod.py +100 -0
  71. mindroot/coreplugins/email/services.py +5 -3
  72. mindroot/coreplugins/email/smtp_handler.py +9 -3
  73. mindroot/coreplugins/email/test_email_service.py +75 -0
  74. mindroot/coreplugins/env_manager/mod.py +61 -25
  75. mindroot/coreplugins/home/router.py +37 -2
  76. mindroot/coreplugins/home/static/imgs/logo.png +0 -0
  77. mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
  78. mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
  79. mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
  80. mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
  81. mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
  82. mindroot/coreplugins/home/templates/home.jinja2 +15 -6
  83. mindroot/coreplugins/index/indices/default/index.json +39 -6
  84. mindroot/coreplugins/jwt_auth/middleware.py +47 -2
  85. mindroot/coreplugins/jwt_auth/mod.py +40 -17
  86. mindroot/coreplugins/l8n/__init__.py +6 -0
  87. mindroot/coreplugins/l8n/debug_loader.py +85 -0
  88. mindroot/coreplugins/l8n/debug_middleware.py +74 -0
  89. mindroot/coreplugins/l8n/l8n_constants.py +19 -0
  90. mindroot/coreplugins/l8n/language_detection.py +183 -0
  91. mindroot/coreplugins/l8n/middleware.py +151 -0
  92. mindroot/coreplugins/l8n/mod.py +277 -0
  93. mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
  94. mindroot/coreplugins/l8n/test_enhanced.py +298 -0
  95. mindroot/coreplugins/l8n/test_l8n.py +95 -0
  96. mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
  97. mindroot/coreplugins/l8n/test_middleware.py +272 -0
  98. mindroot/coreplugins/l8n/utils.py +232 -0
  99. mindroot/coreplugins/mcp_/__init__.py +14 -0
  100. mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
  101. mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
  102. mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
  103. mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
  104. mindroot/coreplugins/mcp_/mod.py +367 -0
  105. mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
  106. mindroot/coreplugins/mcp_/server_installer.py +79 -0
  107. mindroot/coreplugins/mcp_/setup.py +26 -0
  108. mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
  109. mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
  110. mindroot/coreplugins/persona/mod.py +12 -7
  111. mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
  112. mindroot/coreplugins/subscriptions/__init__.py +1 -0
  113. mindroot/coreplugins/subscriptions/mod.py +14 -3
  114. mindroot/coreplugins/subscriptions/router.py +3 -0
  115. mindroot/coreplugins/user_service/__init__.py +1 -2
  116. mindroot/coreplugins/user_service/admin_init.py +1 -0
  117. mindroot/coreplugins/user_service/email_service.py +72 -17
  118. mindroot/coreplugins/user_service/mod.py +10 -2
  119. mindroot/coreplugins/user_service/router.py +2 -0
  120. mindroot/lib/auth/api_key.py +28 -0
  121. mindroot/lib/cli/plugins.py +94 -0
  122. mindroot/lib/plugins/default_plugin_manifest.json +20 -0
  123. mindroot/lib/plugins/installation.py +5 -5
  124. mindroot/lib/plugins/l8n_static_handler.py +225 -0
  125. mindroot/lib/plugins/loader.py +33 -3
  126. mindroot/lib/plugins/loader_with_l8n.py +281 -0
  127. mindroot/lib/plugins/manifest.py +236 -24
  128. mindroot/lib/providers/commands.py +3 -1
  129. mindroot/lib/route_decorators.py +5 -5
  130. mindroot/lib/templates.py +183 -11
  131. mindroot/lib/utils/merge_arrays.py +1 -1
  132. mindroot/migrate.py +39 -20
  133. mindroot/registry/data_access.py +1 -1
  134. mindroot/server.py +42 -13
  135. mindroot/server_missing_normal_args.py +197 -0
  136. mindroot/server_prev.py +173 -0
  137. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/METADATA +7 -2
  138. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/RECORD +143 -113
  139. mindroot/coreplugins/admin/plugin_manager_backup.py +0 -615
  140. mindroot/coreplugins/admin/static/favicon/about.txt +0 -6
  141. mindroot/coreplugins/admin/static/favicon/android-chrome-512x512.png +0 -0
  142. mindroot/coreplugins/admin/static/favicon/apple-touch-icon.png +0 -0
  143. mindroot/coreplugins/admin/static/favicon/favicon-16x16.png +0 -0
  144. mindroot/coreplugins/admin/static/favicon/favicon-32x32.png +0 -0
  145. mindroot/coreplugins/admin/static/favicon/favicon.ico +0 -0
  146. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/about.txt +0 -6
  147. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  148. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  149. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  150. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  151. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  152. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon.ico +0 -0
  153. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  154. mindroot/coreplugins/admin/static/favicon/logo.png +0 -0
  155. mindroot/coreplugins/admin/static/favicon/site.webmanifest +0 -1
  156. mindroot/coreplugins/admin/static/js/backup/agent-editor.js +0 -186
  157. mindroot/coreplugins/admin/static/js/backup/agent-form.js +0 -1133
  158. mindroot/coreplugins/admin/static/js/backup/agent-list.js +0 -94
  159. mindroot/coreplugins/chat/static/favicon/about.txt +0 -6
  160. mindroot/coreplugins/chat/static/favicon/android-chrome-192x192.png +0 -0
  161. mindroot/coreplugins/chat/static/favicon/android-chrome-512x512.png +0 -0
  162. mindroot/coreplugins/chat/static/favicon/apple-touch-icon.png +0 -0
  163. mindroot/coreplugins/chat/static/favicon/favicon-16x16.png +0 -0
  164. mindroot/coreplugins/chat/static/favicon/favicon-32x32.png +0 -0
  165. mindroot/coreplugins/chat/static/favicon/favicon.ico +0 -0
  166. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/about.txt +0 -6
  167. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  168. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  169. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  170. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  171. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  172. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon.ico +0 -0
  173. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  174. mindroot/coreplugins/chat/static/favicon/logo.png +0 -0
  175. mindroot/coreplugins/chat/static/favicon/site.webmanifest +0 -1
  176. mindroot/coreplugins/index/default.json +0 -76
  177. mindroot/coreplugins/user_service/file_trigger_service.py +0 -12
  178. mindroot/coreplugins/user_service/hooks.py +0 -23
  179. /mindroot/coreplugins/{admin/static/favicon/android-chrome-192x192.png → home/static/imgs/backuplogo.png} +0 -0
  180. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/WHEEL +0 -0
  181. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/entry_points.txt +0 -0
  182. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/licenses/LICENSE +0 -0
  183. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,495 @@
1
+ """MCP Registry Integration Routes
2
+
3
+ Handles browsing and installing MCP servers from the registry.
4
+ """
5
+
6
+ from fastapi import APIRouter, HTTPException
7
+ from pydantic import BaseModel
8
+ from typing import Optional, List, Dict, Any
9
+ import httpx
10
+ import json
11
+ import os
12
+ from lib.route_decorators import requires_role
13
+ from lib.providers.services import service_manager
14
+
15
+ # Create router with admin role requirement
16
+ router = APIRouter(
17
+ dependencies=[requires_role('admin')]
18
+ )
19
+
20
+ class RegistryServerInstallRequest(BaseModel):
21
+ registry_id: str
22
+ server_name: Optional[str] = None # Override name if desired
23
+
24
+ class RegistryBrowseRequest(BaseModel):
25
+ category: Optional[str] = None
26
+ search: Optional[str] = None
27
+ page: int = 1
28
+ limit: int = 20
29
+
30
+ @router.get("/mcp/registry/browse")
31
+ async def browse_registry_servers(category: Optional[str] = None, search: Optional[str] = None, page: int = 1, limit: int = 20):
32
+ """Browse MCP servers from the registry."""
33
+ try:
34
+ # Get registry settings
35
+ registry_url = "https://registry.mindroot.io" # Default
36
+ registry_token = None
37
+
38
+ try:
39
+ settings_file = 'data/registry_settings.json'
40
+ if os.path.exists(settings_file):
41
+ with open(settings_file, 'r') as f:
42
+ settings = json.load(f)
43
+ registry_url = settings.get("registry_url", registry_url)
44
+ registry_token = settings.get("registry_token")
45
+
46
+ # Try environment variable if no token in file
47
+ if not registry_token:
48
+ registry_token = os.getenv('REGISTRY_TOKEN')
49
+ except Exception as e:
50
+ print(f"Error reading registry settings: {e}")
51
+
52
+ # Build query parameters
53
+ params = {
54
+ "category": "mcp_server",
55
+ "page": page,
56
+ "limit": limit
57
+ }
58
+
59
+ if category:
60
+ params["filter_category"] = category
61
+ if search:
62
+ params["search"] = search
63
+
64
+ headers = {"Content-Type": "application/json"}
65
+ if registry_token:
66
+ headers["Authorization"] = f"Bearer {registry_token}"
67
+
68
+ # Query registry
69
+ async with httpx.AsyncClient(timeout=30.0) as client:
70
+ response = await client.get(
71
+ f"{registry_url}/browse",
72
+ params=params,
73
+ headers=headers
74
+ )
75
+
76
+ if response.status_code >= 400:
77
+ try:
78
+ error_detail = response.json().get("detail", response.text)
79
+ except:
80
+ error_detail = response.text
81
+ raise HTTPException(
82
+ status_code=response.status_code,
83
+ detail=f"Registry query failed: {error_detail}"
84
+ )
85
+
86
+ result = response.json()
87
+
88
+ # Filter and format MCP servers
89
+ servers = []
90
+ for item in result.get("items", []):
91
+ if item.get("category") == "mcp_server":
92
+ server_data = item.get("data", {})
93
+ servers.append({
94
+ "registry_id": item.get("id"),
95
+ "title": item.get("title"),
96
+ "description": item.get("description"),
97
+ "version": item.get("version"),
98
+ "author": item.get("author"),
99
+ "tags": item.get("tags", []),
100
+ "server_type": server_data.get("server_type", "unknown"),
101
+ "transport": server_data.get("transport", "unknown"),
102
+ "url": server_data.get("url"),
103
+ "tools": server_data.get("tools", []),
104
+ "auth_type": server_data.get("auth_type", "none"),
105
+ "created_at": item.get("created_at"),
106
+ "updated_at": item.get("updated_at")
107
+ })
108
+
109
+ return {
110
+ "success": True,
111
+ "servers": servers,
112
+ "total": len(servers),
113
+ "page": page,
114
+ "limit": limit
115
+ }
116
+
117
+ except HTTPException:
118
+ raise
119
+ except Exception as e:
120
+ import traceback
121
+ traceback.print_exc()
122
+ raise HTTPException(status_code=500, detail=f"Registry browse failed: {str(e)}")
123
+
124
+ @router.get("/mcp/registry/server/{registry_id}")
125
+ async def get_registry_server_details(registry_id: str):
126
+ """Get detailed information about a specific registry server."""
127
+ try:
128
+ # Get registry settings
129
+ registry_url = "https://registry.mindroot.io" # Default
130
+ registry_token = None
131
+
132
+ try:
133
+ settings_file = 'data/registry_settings.json'
134
+ if os.path.exists(settings_file):
135
+ with open(settings_file, 'r') as f:
136
+ settings = json.load(f)
137
+ registry_url = settings.get("registry_url", registry_url)
138
+ registry_token = settings.get("registry_token")
139
+
140
+ if not registry_token:
141
+ registry_token = os.getenv('REGISTRY_TOKEN')
142
+ except Exception as e:
143
+ print(f"Error reading registry settings: {e}")
144
+
145
+ headers = {"Content-Type": "application/json"}
146
+ if registry_token:
147
+ headers["Authorization"] = f"Bearer {registry_token}"
148
+
149
+ # Get server details from registry
150
+ async with httpx.AsyncClient(timeout=30.0) as client:
151
+ response = await client.get(
152
+ f"{registry_url}/item/{registry_id}",
153
+ headers=headers
154
+ )
155
+
156
+ if response.status_code >= 400:
157
+ try:
158
+ error_detail = response.json().get("detail", response.text)
159
+ except:
160
+ error_detail = response.text
161
+ raise HTTPException(
162
+ status_code=response.status_code,
163
+ detail=f"Registry server lookup failed: {error_detail}"
164
+ )
165
+
166
+ item = response.json()
167
+
168
+ if item.get("category") != "mcp_server":
169
+ raise HTTPException(status_code=400, detail="Item is not an MCP server")
170
+
171
+ server_data = item.get("data", {})
172
+
173
+ return {
174
+ "success": True,
175
+ "server": {
176
+ "registry_id": item.get("id"),
177
+ "title": item.get("title"),
178
+ "description": item.get("description"),
179
+ "version": item.get("version"),
180
+ "author": item.get("author"),
181
+ "tags": item.get("tags", []),
182
+ "server_type": server_data.get("server_type", "unknown"),
183
+ "transport": server_data.get("transport", "unknown"),
184
+ "url": server_data.get("url"),
185
+ "command": server_data.get("command"),
186
+ "args": server_data.get("args", []),
187
+ "env": server_data.get("env", {}),
188
+ "tools": server_data.get("tools", []),
189
+ "resources": server_data.get("resources", []),
190
+ "prompts": server_data.get("prompts", []),
191
+ "auth_type": server_data.get("auth_type", "none"),
192
+ "created_at": item.get("created_at"),
193
+ "updated_at": item.get("updated_at")
194
+ }
195
+ }
196
+
197
+ except HTTPException:
198
+ raise
199
+ except Exception as e:
200
+ import traceback
201
+ traceback.print_exc()
202
+ raise HTTPException(status_code=500, detail=f"Registry server lookup failed: {str(e)}")
203
+
204
+ @router.post("/mcp/registry/install")
205
+ async def install_registry_server(request: RegistryServerInstallRequest):
206
+ """Install an MCP server from the registry."""
207
+ try:
208
+ # First get server details from registry
209
+ server_details_response = await get_registry_server_details(request.registry_id)
210
+ server_info = server_details_response["server"]
211
+
212
+ # Determine server name
213
+ server_name = request.server_name or server_info["title"].lower().replace(" ", "_")
214
+
215
+ # Get MCP manager
216
+ mcp_manager = await service_manager.enhanced_mcp_manager_service()
217
+ if not mcp_manager:
218
+ raise HTTPException(status_code=500, detail="MCP manager service not available")
219
+
220
+ # Check if server already exists
221
+ if server_name in mcp_manager.servers:
222
+ raise HTTPException(status_code=400, detail=f"Server '{server_name}' already exists")
223
+
224
+ # Create server configuration based on type
225
+ if server_info["server_type"] == "remote":
226
+ # Remote server - use OAuth if needed
227
+ from mindroot.coreplugins.mcp_.mod import MCPServer
228
+ import os
229
+
230
+ # Get BASE_URL for callback
231
+ base_url = os.getenv('BASE_URL', 'http://localhost:3000')
232
+
233
+ server = MCPServer(
234
+ name=server_name,
235
+ description=server_info["description"],
236
+ command=None, # Not used for remote servers
237
+ transport="http",
238
+ url=server_info["url"],
239
+ auth_type=server_info.get("auth_type", "oauth2"),
240
+ redirect_uri=f"{base_url}/mcp_oauth_cb"
241
+ )
242
+
243
+ # Add server to manager
244
+ mcp_manager.add_server(server_name, server)
245
+
246
+ # Try to connect (will handle OAuth if needed)
247
+ success = await mcp_manager.connect_server(server_name)
248
+
249
+ if not success:
250
+ # Check if OAuth flow is pending
251
+ oauth_status = mcp_manager.get_oauth_status(server_name)
252
+
253
+ if "oauth_flow" in oauth_status and oauth_status["oauth_flow"]["status"] == "awaiting_authorization":
254
+ return {
255
+ "success": False,
256
+ "requires_oauth": True,
257
+ "auth_url": oauth_status["oauth_flow"]["auth_url"],
258
+ "flow_id": oauth_status["oauth_flow"]["flow_id"],
259
+ "server_name": server_name,
260
+ "message": "OAuth authorization required to complete installation."
261
+ }
262
+ else:
263
+ # Remove failed server
264
+ mcp_manager.remove_server(server_name)
265
+ raise HTTPException(status_code=500, detail="Failed to connect to remote server")
266
+
267
+ # Success - server is connected
268
+ tools_count = len(server.capabilities.get("tools", []))
269
+
270
+ # Mark as installed (will only save to config if local)
271
+ mcp_manager.mark_server_as_installed(server_name, request.registry_id)
272
+ is_local = mcp_manager._is_local_server(server)
273
+
274
+ return {
275
+ "success": True,
276
+ "message": f"Successfully installed '{server_name}' with {tools_count} tools.",
277
+ "server_name": server_name,
278
+ "tools_count": tools_count,
279
+ "server_type": "local" if is_local else "remote",
280
+ "persisted": is_local,
281
+ "installed": True
282
+ }
283
+
284
+ elif server_info["server_type"] == "local":
285
+ # Local server - use enhanced manager for installation
286
+ try:
287
+ enhanced_mcp_manager = await service_manager.enhanced_mcp_manager_service()
288
+ if not enhanced_mcp_manager:
289
+ raise HTTPException(status_code=500, detail="Enhanced MCP manager service not available")
290
+
291
+ from mindroot.coreplugins.mcp_.mod import MCPServer
292
+
293
+ # Create enhanced server configuration
294
+ enhanced_server = MCPServer( name=server_name,
295
+ description=server_info["description"],
296
+ command=server_info["command"],
297
+ args=server_info.get("args", []),
298
+ env=server_info.get("env", {}),
299
+ install_method="manual", # Registry servers are pre-configured
300
+ auto_install=False
301
+ )
302
+
303
+ # Add to enhanced manager
304
+ enhanced_mcp_manager.add_server(server_name, enhanced_server)
305
+
306
+ # Connect to server
307
+ success = await enhanced_mcp_manager.connect_server(server_name)
308
+
309
+ if not success:
310
+ enhanced_mcp_manager.remove_server(server_name)
311
+ raise HTTPException(status_code=500, detail="Failed to connect to local server")
312
+
313
+ # Get tools count
314
+ server = enhanced_mcp_manager.servers[server_name]
315
+ tools_count = len(server.capabilities.get("tools", []))
316
+
317
+ # Mark as installed (will only save to config if local)
318
+ enhanced_mcp_manager.mark_server_as_installed(server_name, request.registry_id)
319
+ is_local = enhanced_mcp_manager._is_local_server(server)
320
+
321
+ return {
322
+ "success": True,
323
+ "message": f"Successfully installed '{server_name}' with {tools_count} tools.",
324
+ "server_name": server_name,
325
+ "tools_count": tools_count,
326
+ "server_type": "local" if is_local else "remote",
327
+ "persisted": is_local,
328
+ "installed": True
329
+ }
330
+
331
+ except Exception as e:
332
+ raise HTTPException(status_code=500, detail=f"Local server installation failed: {str(e)}")
333
+
334
+ else:
335
+ raise HTTPException(status_code=400, detail=f"Unsupported server type: {server_info['server_type']}")
336
+
337
+ except HTTPException:
338
+ raise
339
+ except Exception as e:
340
+ import traceback
341
+ traceback.print_exc()
342
+ raise HTTPException(status_code=500, detail=f"Registry server installation failed: {str(e)}")
343
+
344
+ @router.post("/mcp/registry/complete-oauth")
345
+ async def complete_registry_oauth(server_name: str, code: str, state: Optional[str] = None):
346
+ """Complete OAuth flow for registry server installation."""
347
+ try:
348
+ # Get the MCP manager service
349
+ mcp_manager = await service_manager.enhanced_mcp_manager_service()
350
+ if not mcp_manager:
351
+ raise HTTPException(status_code=500, detail="MCP manager service not available")
352
+
353
+ # Complete the OAuth flow
354
+ success = mcp_manager.complete_oauth_flow(server_name, code, state)
355
+
356
+ if not success:
357
+ raise HTTPException(status_code=400, detail="Failed to complete OAuth flow")
358
+
359
+ # Wait a moment for the OAuth flow to complete
360
+ import asyncio
361
+ await asyncio.sleep(1)
362
+
363
+ # Check if server is now connected
364
+ if server_name in mcp_manager.servers:
365
+ server = mcp_manager.servers[server_name]
366
+ if server.status == "connected":
367
+ tools = server.capabilities.get("tools", [])
368
+ resources = server.capabilities.get("resources", [])
369
+ prompts = server.capabilities.get("prompts", [])
370
+
371
+ return {
372
+ "success": True,
373
+ "message": f"OAuth completed successfully. Registry server '{server_name}' installed with {len(tools)} tools, {len(resources)} resources, {len(prompts)} prompts.",
374
+ "server_name": server_name,
375
+ "tools_count": len(tools),
376
+ "resources_count": len(resources),
377
+ "prompts_count": len(prompts)
378
+ }
379
+
380
+ return {
381
+ "success": True,
382
+ "message": "OAuth flow completed, but server connection is still pending.",
383
+ "server_name": server_name
384
+ }
385
+
386
+ except HTTPException:
387
+ raise
388
+ except Exception as e:
389
+ import traceback
390
+ traceback.print_exc()
391
+ raise HTTPException(status_code=500, detail=f"OAuth completion failed: {str(e)}")
392
+
393
+ @router.get("/mcp/registry/installation-status")
394
+ async def get_installation_status():
395
+ """Get installation status of all registry servers."""
396
+ try:
397
+ mcp_manager = await service_manager.enhanced_mcp_manager_service()
398
+ if not mcp_manager:
399
+ return {"success": False, "installed_servers": []}
400
+
401
+ installed_servers = []
402
+ for name, server in mcp_manager.servers.items():
403
+ # Include servers with registry_id OR servers that are marked as installed
404
+ has_registry_id = hasattr(server, 'registry_id') and server.registry_id
405
+ is_installed = hasattr(server, 'installed') and server.installed
406
+
407
+ if has_registry_id or is_installed:
408
+ installed_servers.append({
409
+ "registry_id": getattr(server, 'registry_id', None),
410
+ "server_name": name,
411
+ "status": server.status,
412
+ "server_type": "local" if mcp_manager._is_local_server(server) else "remote",
413
+ "persisted": mcp_manager._is_local_server(server),
414
+ "tools_count": len(server.capabilities.get("tools", []))
415
+ })
416
+
417
+ return {"success": True, "installed_servers": installed_servers}
418
+ except Exception as e:
419
+ import traceback
420
+ traceback.print_exc()
421
+ return {"success": False, "installed_servers": [], "error": str(e)}
422
+
423
+ @router.get("/mcp/registry/categories")
424
+ async def get_registry_categories():
425
+ """Get available MCP server categories from the registry."""
426
+ try:
427
+ # Get registry settings
428
+ registry_url = "https://registry.mindroot.io" # Default
429
+ registry_token = None
430
+
431
+ try:
432
+ settings_file = 'data/registry_settings.json'
433
+ if os.path.exists(settings_file):
434
+ with open(settings_file, 'r') as f:
435
+ settings = json.load(f)
436
+ registry_url = settings.get("registry_url", registry_url)
437
+ registry_token = settings.get("registry_token")
438
+
439
+ if not registry_token:
440
+ registry_token = os.getenv('REGISTRY_TOKEN')
441
+ except Exception as e:
442
+ print(f"Error reading registry settings: {e}")
443
+
444
+ headers = {"Content-Type": "application/json"}
445
+ if registry_token:
446
+ headers["Authorization"] = f"Bearer {registry_token}"
447
+
448
+ # Get categories from registry
449
+ async with httpx.AsyncClient(timeout=30.0) as client:
450
+ response = await client.get(
451
+ f"{registry_url}/categories",
452
+ headers=headers
453
+ )
454
+
455
+ if response.status_code >= 400:
456
+ # Fallback to default categories if endpoint doesn't exist
457
+ return {
458
+ "success": True,
459
+ "categories": ["utilities", "development", "database", "communication", "ai", "automation"]
460
+ }
461
+
462
+ result = response.json()
463
+ return {
464
+ "success": True,
465
+ "categories": result.get("categories", [])
466
+ }
467
+
468
+ except Exception as e:
469
+ # Fallback to default categories
470
+ return {
471
+ "success": True,
472
+ "categories": ["utilities", "development", "database", "communication", "ai", "automation"]
473
+ }
474
+
475
+ @router.post("/mcp/registry/mark-installed")
476
+ async def mark_server_installed(server_name: str, registry_id: str = None):
477
+ """Manually mark a server as installed (for testing/fixing)."""
478
+ try:
479
+ mcp_manager = await service_manager.enhanced_mcp_manager_service()
480
+ if not mcp_manager:
481
+ raise HTTPException(status_code=500, detail="MCP manager service not available")
482
+
483
+ if server_name not in mcp_manager.servers:
484
+ raise HTTPException(status_code=404, detail=f"Server '{server_name}' not found")
485
+
486
+ mcp_manager.mark_server_as_installed(server_name, registry_id)
487
+
488
+ return {
489
+ "success": True,
490
+ "message": f"Server '{server_name}' marked as installed"
491
+ }
492
+ except HTTPException:
493
+ raise
494
+ except Exception as e:
495
+ raise HTTPException(status_code=500, detail=str(e))