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,450 @@
1
+ from fastapi import APIRouter, HTTPException
2
+ from pydantic import BaseModel
3
+ from typing import Optional, List, Dict, Any
4
+ from lib.route_decorators import requires_role
5
+ from lib.providers.services import service_manager
6
+ import httpx
7
+ import json
8
+ import uuid
9
+ import asyncio
10
+
11
+ # Create router with admin role requirement
12
+ router = APIRouter(
13
+ dependencies=[requires_role('admin')]
14
+ )
15
+
16
+ class McpServerPublishRequest(BaseModel):
17
+ name: str
18
+ description: str
19
+ server_type: str # 'local' or 'remote'
20
+ tools: List[Dict[str, Any]]
21
+ # Local server fields
22
+ command: Optional[str] = None
23
+ args: Optional[List[str]] = None
24
+ env: Optional[Dict[str, str]] = None
25
+ # Remote server fields
26
+ url: Optional[str] = None
27
+
28
+ class McpTestRemoteRequest(BaseModel):
29
+ url: str
30
+ name: Optional[str] = None
31
+
32
+ class McpTestLocalRequest(BaseModel):
33
+ name: str
34
+ command: str
35
+ args: List[str] = []
36
+ env: Dict[str, str] = {}
37
+
38
+ class McpCompleteOAuthRequest(BaseModel):
39
+ server_name: str
40
+ code: str
41
+ state: Optional[str] = None
42
+
43
+ @router.post("/mcp/publish")
44
+ async def publish_mcp_server(request: McpServerPublishRequest):
45
+ """Publish an MCP server to the registry."""
46
+ try:
47
+ # Validate request based on server type
48
+ if request.server_type == 'local':
49
+ if not request.command:
50
+ raise HTTPException(status_code=400, detail="Command is required for local servers")
51
+ elif request.server_type == 'remote':
52
+ if not request.url:
53
+ raise HTTPException(status_code=400, detail="URL is required for remote servers")
54
+ else:
55
+ raise HTTPException(status_code=400, detail="Server type must be 'local' or 'remote'")
56
+
57
+ # Prepare registry publish data
58
+ publish_data = {
59
+ "title": request.name,
60
+ "description": request.description,
61
+ "category": "mcp_server",
62
+ "content_type": "mcp_server",
63
+ "version": "1.0.0",
64
+ "tags": ["mcp", "server", request.server_type],
65
+ "dependencies": []
66
+ }
67
+
68
+ # Prepare server data for registry
69
+ server_data = {
70
+ "name": request.name,
71
+ "description": request.description,
72
+ "transport": "stdio" if request.server_type == 'local' else "http",
73
+ "auth_type": "none" if request.server_type == 'local' else "auto",
74
+ "tools": request.tools,
75
+ "server_type": request.server_type
76
+ }
77
+
78
+ # Add type-specific configuration
79
+ if request.server_type == 'local':
80
+ server_data.update({
81
+ "command": request.command,
82
+ "args": request.args or [],
83
+ "env": request.env or {}
84
+ })
85
+ else:
86
+ server_data.update({
87
+ "url": request.url
88
+ })
89
+
90
+ # Prepare registry publish data
91
+ publish_data = {
92
+ "title": request.name,
93
+ "description": request.description,
94
+ "category": "mcp_server",
95
+ "content_type": "mcp_server",
96
+ "version": "1.0.0",
97
+ "data": server_data,
98
+ "tags": ["mcp", "server", request.server_type],
99
+ "dependencies": []
100
+ }
101
+
102
+ # Get registry settings
103
+ registry_url = "https://registry.mindroot.io" # Default
104
+ registry_token = None
105
+
106
+ try:
107
+ import os
108
+ settings_file = 'data/registry_settings.json'
109
+ if os.path.exists(settings_file):
110
+ with open(settings_file, 'r') as f:
111
+ settings = json.load(f)
112
+ registry_url = settings.get("registry_url", registry_url)
113
+ registry_token = settings.get("registry_token")
114
+
115
+ # Try environment variable if no token in file
116
+ if not registry_token:
117
+ registry_token = os.getenv('REGISTRY_TOKEN')
118
+ except Exception as e:
119
+ print(f"Error reading registry settings: {e}")
120
+
121
+ if not registry_token:
122
+ raise HTTPException(
123
+ status_code=401,
124
+ detail="Registry authentication token not configured. Please set REGISTRY_TOKEN or configure in registry settings."
125
+ )
126
+
127
+ # Publish to registry
128
+ async with httpx.AsyncClient(timeout=30.0) as client:
129
+ response = await client.post(
130
+ f"{registry_url}/publish",
131
+ headers={
132
+ "Content-Type": "application/json",
133
+ "Authorization": f"Bearer {registry_token}"
134
+ },
135
+ json=publish_data
136
+ )
137
+
138
+ if response.status_code >= 400:
139
+ try:
140
+ error_detail = response.json().get("detail", response.text)
141
+ except:
142
+ error_detail = response.text
143
+ raise HTTPException(
144
+ status_code=response.status_code,
145
+ detail=f"Registry publishing failed: {error_detail}"
146
+ )
147
+
148
+ result = response.json()
149
+ return {
150
+ "success": True,
151
+ "message": f"MCP Server '{request.name}' published successfully!",
152
+ "data": result
153
+ }
154
+
155
+ except HTTPException:
156
+ raise
157
+ except Exception as e:
158
+ import traceback
159
+ traceback.print_exc()
160
+ raise HTTPException(status_code=500, detail=str(e))
161
+
162
+ @router.post("/mcp/test-local")
163
+ async def test_local_mcp_server(request: McpTestLocalRequest):
164
+ """Test connection to a local MCP server and list its capabilities."""
165
+ try:
166
+ # Get the MCP manager service
167
+ mcp_manager = await service_manager.enhanced_mcp_manager_service(context=None)
168
+ if not mcp_manager:
169
+ raise HTTPException(status_code=500, detail="MCP manager service not available")
170
+
171
+ print(f"DEBUG: test_local_mcp_server: Testing local server {request.name} with command {request.command}")
172
+
173
+ # Use the new testing method from MCPManager
174
+ result = await mcp_manager.test_local_server_capabilities(
175
+ name=request.name,
176
+ command=request.command,
177
+ args=request.args,
178
+ env=request.env
179
+ )
180
+
181
+ print(f"DEBUG: test_local_mcp_server: Result: {result}")
182
+ return result
183
+
184
+ except HTTPException:
185
+ raise
186
+ except Exception as e:
187
+ import traceback
188
+ traceback.print_exc()
189
+ raise HTTPException(status_code=500, detail=f"Local server test failed: {str(e)}")
190
+
191
+ @router.post("/mcp/test-remote")
192
+ async def test_remote_mcp_server(request: McpTestRemoteRequest):
193
+ """Test connection to a remote MCP server and list its tools using MCP manager OAuth flow."""
194
+ try:
195
+ # Get the MCP manager service
196
+ print("Testing remote MCP server connection: ", request.url)
197
+ mcp_manager = await service_manager.enhanced_mcp_manager_service(context=None)
198
+ if not mcp_manager:
199
+ raise HTTPException(status_code=500, detail="MCP manager service not available")
200
+
201
+ # Generate a unique temporary server name
202
+ temp_server_name = f"temp_publish_test_{uuid.uuid4().hex[:8]}"
203
+ server_name = request.name or temp_server_name
204
+ print(f"Using server name: {server_name}")
205
+
206
+ try:
207
+ from mindroot.coreplugins.mcp_.mod import MCPServer
208
+
209
+ # Check if server already exists (from registry install flow)
210
+ existing_server = None
211
+ if server_name in mcp_manager.servers:
212
+ existing_server = mcp_manager.servers[server_name]
213
+ print(f"Found existing server configuration for {server_name}")
214
+ print(f"Existing server auth_type: {existing_server.auth_type}")
215
+ print(f"Existing server client_id: {existing_server.client_id}")
216
+
217
+ if existing_server:
218
+ # Use existing server configuration (registry install flow)
219
+ server_to_test = existing_server
220
+ print(f"Using existing server config with auth_type={server_to_test.auth_type}")
221
+ else:
222
+ # Create temporary server configuration for testing (publish flow)
223
+ temp_server = MCPServer(
224
+ name=server_name,
225
+ description=f"Temporary server for testing {request.url}",
226
+ command="", # Not used for remote servers
227
+ transport="http",
228
+ url=request.url,
229
+ auth_type="oauth2" # Assume OAuth2 for remote servers
230
+ )
231
+
232
+ # Add temporary server to MCP manager
233
+ mcp_manager.add_server(server_name, temp_server)
234
+ server_to_test = temp_server
235
+ print(f"Created temporary server config for publish flow")
236
+
237
+ # Note: OAuth client_id will be discovered during the OAuth flow
238
+
239
+ print("Connecting to remote MCP server: ", server_name)
240
+ print(f"Server URL: {request.url}")
241
+ print("MCP manager is: ", mcp_manager)
242
+ mcp_manager = await service_manager.enhanced_mcp_manager_service(context=None)
243
+ print("MCP manager after re-fetch: ", mcp_manager)
244
+
245
+ print("Running sanity test")
246
+ await mcp_manager.sanity_test()
247
+ # Try to connect (this will handle OAuth flow if needed)
248
+ success = await mcp_manager.connect_server(server_name)
249
+ print("Connection result: ", success)
250
+ if not success:
251
+ # Check if OAuth flow is pending
252
+ oauth_status = mcp_manager.get_oauth_status(server_name)
253
+
254
+ if "oauth_flow" in oauth_status and oauth_status["oauth_flow"]["status"] == "awaiting_authorization":
255
+ # OAuth flow is pending - return the auth URL for frontend to handle
256
+ return {
257
+ "success": False,
258
+ "requires_oauth": True,
259
+ "auth_url": oauth_status["oauth_flow"]["auth_url"],
260
+ "flow_id": oauth_status["oauth_flow"]["flow_id"],
261
+ "server_name": server_name,
262
+ "message": "OAuth authorization required. Please complete the authorization flow."
263
+ }
264
+ else:
265
+ # Try without OAuth first to see if it's a 401
266
+ try:
267
+ await test_direct_connection(request.url)
268
+ except HTTPException as e:
269
+ if e.status_code == 401:
270
+ # Server requires auth - try OAuth connection
271
+ oauth_success = await mcp_manager.connect_oauth_server(server_name)
272
+ if not oauth_success:
273
+ oauth_status = mcp_manager.get_oauth_status(server_name)
274
+ if "oauth_flow" in oauth_status:
275
+ return {
276
+ "success": False,
277
+ "requires_oauth": True,
278
+ "auth_url": oauth_status["oauth_flow"]["auth_url"],
279
+ "flow_id": oauth_status["oauth_flow"]["flow_id"],
280
+ "server_name": server_name,
281
+ "message": "OAuth authorization required. Please complete the authorization flow."
282
+ }
283
+ raise e
284
+
285
+ # Get server capabilities (tools, resources, prompts)
286
+ server = mcp_manager.servers[server_name]
287
+ tools = server.capabilities.get("tools", [])
288
+ resources = server.capabilities.get("resources", [])
289
+ prompts = server.capabilities.get("prompts", [])
290
+
291
+ return {
292
+ "success": True,
293
+ "message": f"Successfully connected to remote MCP server. Found {len(tools)} tools, {len(resources)} resources, {len(prompts)} prompts.",
294
+ "tools": tools,
295
+ "resources": resources,
296
+ "prompts": prompts,
297
+ "server_name": server_name
298
+ }
299
+
300
+ finally:
301
+ # Clean up temporary server
302
+ try:
303
+ # Only clean up if we created a temporary server (not existing one)
304
+ if not existing_server:
305
+ await mcp_manager.disconnect_server(server_name)
306
+ mcp_manager.remove_server(server_name)
307
+ print(f"Cleaned up temporary server {server_name}")
308
+ except Exception as cleanup_error:
309
+ print(f"Error cleaning up temporary server {server_name}: {cleanup_error}")
310
+
311
+ except HTTPException:
312
+ print("HTTPException occurred during MCP server test.")
313
+ import traceback
314
+ traceback.print_exc()
315
+ raise
316
+ except Exception as e:
317
+ import traceback
318
+ traceback.print_exc()
319
+ raise HTTPException(status_code=500, detail=f"Connection test failed: {str(e)}")
320
+
321
+ async def test_direct_connection(url: str):
322
+ """Test direct connection to MCP server without OAuth to check if auth is required."""
323
+ headers = {
324
+ "Content-Type": "application/json"
325
+ }
326
+
327
+ # Initialize connection
328
+ init_request = {
329
+ "jsonrpc": "2.0",
330
+ "id": 1,
331
+ "method": "initialize",
332
+ "params": {
333
+ "protocolVersion": "2024-11-05",
334
+ "capabilities": {
335
+ "tools": {}
336
+ },
337
+ "clientInfo": {
338
+ "name": "mindroot-tester",
339
+ "version": "1.0.0"
340
+ }
341
+ }
342
+ }
343
+
344
+ async with httpx.AsyncClient(timeout=10.0) as client:
345
+ init_response = await client.post(url, headers=headers, json=init_request)
346
+
347
+ if init_response.status_code == 401:
348
+ raise HTTPException(status_code=401, detail="Server requires authentication")
349
+
350
+ if init_response.status_code != 200:
351
+ raise HTTPException(
352
+ status_code=init_response.status_code,
353
+ detail=f"Failed to initialize: {init_response.text}"
354
+ )
355
+
356
+ # List tools
357
+ tools_request = {
358
+ "jsonrpc": "2.0",
359
+ "id": 2,
360
+ "method": "tools/list",
361
+ "params": {}
362
+ }
363
+
364
+ tools_response = await client.post(url, headers=headers, json=tools_request)
365
+
366
+ if tools_response.status_code != 200:
367
+ raise HTTPException(
368
+ status_code=tools_response.status_code,
369
+ detail=f"Failed to list tools: {tools_response.text}"
370
+ )
371
+
372
+ tools_data = tools_response.json()
373
+ tools = tools_data.get("result", {}).get("tools", [])
374
+ return tools
375
+
376
+ @router.post("/mcp/complete-oauth")
377
+ async def complete_oauth_flow(request: McpCompleteOAuthRequest):
378
+ """Complete OAuth flow for MCP server testing."""
379
+ try:
380
+ # Get the enhanced MCP manager service
381
+ mcp_manager = await service_manager.enhanced_mcp_manager_service(context=None)
382
+ if not mcp_manager:
383
+ raise HTTPException(status_code=500, detail="MCP manager service not available")
384
+
385
+ # Complete the OAuth flow
386
+ success = mcp_manager.complete_oauth_flow(request.server_name, request.code, request.state)
387
+
388
+ if not success:
389
+ raise HTTPException(status_code=400, detail="Failed to complete OAuth flow")
390
+
391
+ # Wait a bit longer for the background task to complete the connection
392
+ await asyncio.sleep(6)
393
+
394
+ # Check if server is now connected
395
+ if request.server_name in mcp_manager.servers:
396
+ server = mcp_manager.servers[request.server_name]
397
+ print(f"DEBUG: complete-oauth: server entry found for '{request.server_name}', status={server.status}, tools={len(server.capabilities.get('tools', []))}")
398
+ if server.status == "connected":
399
+ tools = server.capabilities.get("tools", [])
400
+ resources = server.capabilities.get("resources", [])
401
+ prompts = server.capabilities.get("prompts", [])
402
+
403
+ return {
404
+ "success": True,
405
+ "message": f"OAuth completed successfully. Found {len(tools)} tools, {len(resources)} resources, {len(prompts)} prompts.",
406
+ "tools": tools,
407
+ "resources": resources,
408
+ "prompts": prompts
409
+ }
410
+
411
+ # Not yet connected; surface pending status so frontend can poll
412
+ try:
413
+ status = mcp_manager.get_oauth_status(request.server_name)
414
+ print(f"DEBUG: complete-oauth: oauth-status for '{request.server_name}' -> {status}")
415
+ except Exception:
416
+ status = {"status": "pending"}
417
+ return {
418
+ "success": True,
419
+ "message": "OAuth flow completed, but server connection is still pending.",
420
+ "status": status.get("status", "pending")
421
+ }
422
+
423
+
424
+ return {
425
+ "success": True,
426
+ "message": "OAuth flow completed, but server connection is still pending.",
427
+ "status": "pending"
428
+ }
429
+
430
+ except HTTPException:
431
+ raise
432
+ except Exception as e:
433
+ import traceback
434
+ traceback.print_exc()
435
+ raise HTTPException(status_code=500, detail=f"OAuth completion failed: {str(e)}")
436
+
437
+ @router.get("/mcp/oauth-status/{server_name}")
438
+ async def get_oauth_status(server_name: str):
439
+ """Get OAuth flow status for a server."""
440
+ try:
441
+ # Get the enhanced MCP manager service
442
+ mcp_manager = await service_manager.enhanced_mcp_manager_service(context=None)
443
+ if not mcp_manager:
444
+ raise HTTPException(status_code=500, detail="MCP manager service not available")
445
+
446
+ status = mcp_manager.get_oauth_status(server_name)
447
+ return status
448
+
449
+ except Exception as e:
450
+ raise HTTPException(status_code=500, detail=f"Failed to get OAuth status: {str(e)}")