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,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)}")
|