mindroot 9.3.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_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/indices/default/index.json +6 -6
- mindroot/coreplugins/jwt_auth/middleware.py +47 -2
- 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/router.py +2 -0
- 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 +236 -24
- 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 +39 -20
- mindroot/registry/data_access.py +1 -1
- mindroot/server.py +42 -13
- mindroot/server_missing_normal_args.py +197 -0
- mindroot/server_prev.py +173 -0
- {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/METADATA +7 -2
- {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/RECORD +144 -112
- 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 -12
- 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.3.0.dist-info → mindroot-9.5.0.dist-info}/WHEEL +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/entry_points.txt +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.5.0.dist-info}/licenses/LICENSE +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.5.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))
|