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.
- 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 +105 -9
- 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-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 +903 -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 +39 -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.6.0.dist-info}/METADATA +7 -2
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/RECORD +143 -113
- mindroot/coreplugins/admin/plugin_manager_backup.py +0 -615
- 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.6.0.dist-info}/WHEEL +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/entry_points.txt +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/licenses/LICENSE +0 -0
- {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
from urllib.parse import parse_qs, urlparse
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
import uuid
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, List, Optional, Any
|
|
11
|
+
from contextlib import AsyncExitStack
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
from pydantic import BaseModel
|
|
15
|
+
|
|
16
|
+
from lib.providers.commands import command
|
|
17
|
+
from lib.providers.services import service
|
|
18
|
+
from .mcp_manager import MCPManager, MCPServer
|
|
19
|
+
from .server_installer import MCPServerInstaller
|
|
20
|
+
from .dynamic_commands import MCPDynamicCommands
|
|
21
|
+
from .oauth_storage import MCPTokenStorage
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from mcp import ClientSession, StdioServerParameters
|
|
25
|
+
from mcp.client.stdio import stdio_client
|
|
26
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
27
|
+
from mcp.client.sse import sse_client
|
|
28
|
+
from mcp.client.auth import OAuthClientProvider, TokenStorage
|
|
29
|
+
from mcp.shared.auth import OAuthClientMetadata, OAuthToken, OAuthClientInformationFull
|
|
30
|
+
from pydantic import AnyUrl
|
|
31
|
+
MCP_AVAILABLE = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
# MCP not installed yet
|
|
34
|
+
ClientSession = None
|
|
35
|
+
StdioServerParameters = None
|
|
36
|
+
stdio_client = None
|
|
37
|
+
streamablehttp_client = None
|
|
38
|
+
sse_client = None
|
|
39
|
+
OAuthClientProvider = None
|
|
40
|
+
OAuthClientMetadata = None
|
|
41
|
+
OAuthToken = None
|
|
42
|
+
OAuthClientInformationFull = None
|
|
43
|
+
AnyUrl = None
|
|
44
|
+
MCP_AVAILABLE = False
|
|
45
|
+
|
|
46
|
+
async def handle_redirect(auth_url: str) -> None:
|
|
47
|
+
print(f"Visit: {auth_url}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async def handle_callback() -> tuple[str, str | None]:
|
|
51
|
+
callback_url = input("Paste callback URL: ")
|
|
52
|
+
params = parse_qs(urlparse(callback_url).query)
|
|
53
|
+
return params["code"][0], params.get("state", [None])[0]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Global MCP manager instance
|
|
57
|
+
mcp_manager = MCPManager()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@service()
|
|
61
|
+
async def mcp_manager_service(context=None):
|
|
62
|
+
"""Service to access the MCP manager"""
|
|
63
|
+
print("returning mcp_manager")
|
|
64
|
+
return mcp_manager
|
|
65
|
+
|
|
66
|
+
@service()
|
|
67
|
+
async def enhanced_mcp_manager_service(context=None):
|
|
68
|
+
"""Service to access enhanced MCP manager (same as mcp_manager_service now)"""
|
|
69
|
+
print("returning enhanced MCP manager service")
|
|
70
|
+
return mcp_manager
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@command()
|
|
74
|
+
async def mcp_connect(server_name: str, context=None):
|
|
75
|
+
"""Connect to an MCP server
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
{ "mcp_connect": { "server_name": "filesystem" } }
|
|
79
|
+
"""
|
|
80
|
+
success = await mcp_manager.connect_server(server_name)
|
|
81
|
+
if success:
|
|
82
|
+
return f"Successfully connected to MCP server: {server_name}"
|
|
83
|
+
else:
|
|
84
|
+
return f"Failed to connect to MCP server: {server_name}"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@command()
|
|
88
|
+
async def mcp_disconnect(server_name: str, context=None):
|
|
89
|
+
"""Disconnect from an MCP server
|
|
90
|
+
|
|
91
|
+
Example:
|
|
92
|
+
{ "mcp_disconnect": { "server_name": "filesystem" } }
|
|
93
|
+
"""
|
|
94
|
+
success = await mcp_manager.disconnect_server(server_name)
|
|
95
|
+
if success:
|
|
96
|
+
return f"Successfully disconnected from MCP server: {server_name}"
|
|
97
|
+
else:
|
|
98
|
+
return f"Failed to disconnect from MCP server: {server_name}"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@command()
|
|
102
|
+
async def mcp_list_servers(context=None):
|
|
103
|
+
"""List all configured MCP servers
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
{ "mcp_list_servers": {} }
|
|
107
|
+
"""
|
|
108
|
+
servers = []
|
|
109
|
+
for name, server in mcp_manager.servers.items():
|
|
110
|
+
servers.append({
|
|
111
|
+
"name": name,
|
|
112
|
+
"description": server.description,
|
|
113
|
+
"status": server.status,
|
|
114
|
+
"transport": server.transport,
|
|
115
|
+
"tools_count": len(server.capabilities.get("tools", [])),
|
|
116
|
+
"resources_count": len(server.capabilities.get("resources", [])),
|
|
117
|
+
"prompts_count": len(server.capabilities.get("prompts", []))
|
|
118
|
+
})
|
|
119
|
+
return servers
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Enhanced commands from enhanced_mod.py
|
|
123
|
+
@command()
|
|
124
|
+
async def mcp_enhanced_connect(server_name: str, context=None):
|
|
125
|
+
"""Connect to MCP server with enhanced features
|
|
126
|
+
|
|
127
|
+
Example:
|
|
128
|
+
{ "mcp_enhanced_connect": { "server_name": "calculator" } }
|
|
129
|
+
"""
|
|
130
|
+
success = await mcp_manager.connect_server(server_name)
|
|
131
|
+
if success:
|
|
132
|
+
return f"Successfully connected to {server_name} with enhanced features"
|
|
133
|
+
else:
|
|
134
|
+
return f"Failed to connect to {server_name}"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@command()
|
|
138
|
+
async def mcp_enhanced_disconnect(server_name: str, context=None):
|
|
139
|
+
"""Disconnect from MCP server
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
{ "mcp_enhanced_disconnect": { "server_name": "calculator" } }
|
|
143
|
+
"""
|
|
144
|
+
success = await mcp_manager.disconnect_server(server_name)
|
|
145
|
+
if success:
|
|
146
|
+
return f"Successfully disconnected from {server_name}"
|
|
147
|
+
else:
|
|
148
|
+
return f"Failed to disconnect from {server_name}"
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@command()
|
|
152
|
+
async def mcp_install_uvx_server(name: str, package: str, description: str = None, context=None):
|
|
153
|
+
"""Install and configure a uvx-based MCP server
|
|
154
|
+
|
|
155
|
+
Example:
|
|
156
|
+
{ "mcp_install_uvx_server": {
|
|
157
|
+
"name": "calculator",
|
|
158
|
+
"package": "mcp-server-calculator",
|
|
159
|
+
"description": "Calculator server"
|
|
160
|
+
} }
|
|
161
|
+
"""
|
|
162
|
+
server = MCPServer(
|
|
163
|
+
name=name,
|
|
164
|
+
description=description or f"MCP server: {name}",
|
|
165
|
+
command="uvx",
|
|
166
|
+
args=[package],
|
|
167
|
+
install_method="uvx",
|
|
168
|
+
install_package=package,
|
|
169
|
+
auto_install=True
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
mcp_manager.add_server(name, server)
|
|
173
|
+
return f"Configured uvx server {name} with package {package}"
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@command()
|
|
177
|
+
async def mcp_install_npx_server(name: str, package: str, description: str = None, context=None):
|
|
178
|
+
"""Install and configure an npx-based MCP server
|
|
179
|
+
|
|
180
|
+
Example:
|
|
181
|
+
{ "mcp_install_npx_server": {
|
|
182
|
+
"name": "github",
|
|
183
|
+
"package": "@modelcontextprotocol/server-github",
|
|
184
|
+
"description": "GitHub server"
|
|
185
|
+
} }
|
|
186
|
+
"""
|
|
187
|
+
server = MCPServer(
|
|
188
|
+
name=name,
|
|
189
|
+
description=description or f"MCP server: {name}",
|
|
190
|
+
command="npx",
|
|
191
|
+
args=["-y", package],
|
|
192
|
+
install_method="npx",
|
|
193
|
+
install_package=package,
|
|
194
|
+
auto_install=True
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
mcp_manager.add_server(name, server)
|
|
198
|
+
return f"Configured npx server {name} with package {package}"
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@command()
|
|
202
|
+
async def mcp_debug_connection(server_name: str, context=None):
|
|
203
|
+
"""Debug MCP server connection and dynamic command registration
|
|
204
|
+
|
|
205
|
+
Example:
|
|
206
|
+
{ "mcp_debug_connection": { "server_name": "calculator" } }
|
|
207
|
+
{ "mcp_debug_connection": { "server_name": "github" } }
|
|
208
|
+
"""
|
|
209
|
+
from .catalog_commands import mcp_catalog_info, mcp_catalog_install_and_run
|
|
210
|
+
from lib.providers.commands import command_manager
|
|
211
|
+
import json
|
|
212
|
+
|
|
213
|
+
debug_info = {
|
|
214
|
+
"server_name": server_name,
|
|
215
|
+
"steps": []
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
# Step 1: Check initial state
|
|
219
|
+
initial_commands = [name for name in command_manager.functions.keys() if name.startswith('mcp_')]
|
|
220
|
+
debug_info["steps"].append({
|
|
221
|
+
"step": "1_initial_state",
|
|
222
|
+
"initial_mcp_commands_count": len(initial_commands),
|
|
223
|
+
"dynamic_commands": mcp_manager.dynamic_commands.get_registered_commands()
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
# Step 2: Get server info
|
|
227
|
+
try:
|
|
228
|
+
server_info = await mcp_catalog_info(server_name)
|
|
229
|
+
debug_info["steps"].append({
|
|
230
|
+
"step": "2_server_info",
|
|
231
|
+
"success": True,
|
|
232
|
+
"server_info": server_info
|
|
233
|
+
})
|
|
234
|
+
except Exception as e:
|
|
235
|
+
debug_info["steps"].append({
|
|
236
|
+
"step": "2_server_info",
|
|
237
|
+
"success": False,
|
|
238
|
+
"error": str(e)
|
|
239
|
+
})
|
|
240
|
+
return debug_info
|
|
241
|
+
|
|
242
|
+
# Step 3: Install and run
|
|
243
|
+
try:
|
|
244
|
+
result = await mcp_catalog_install_and_run(server_name)
|
|
245
|
+
debug_info["steps"].append({
|
|
246
|
+
"step": "3_install_and_run",
|
|
247
|
+
"success": True,
|
|
248
|
+
"result": result
|
|
249
|
+
})
|
|
250
|
+
except Exception as e:
|
|
251
|
+
debug_info["steps"].append({
|
|
252
|
+
"step": "3_install_and_run",
|
|
253
|
+
"success": False,
|
|
254
|
+
"error": str(e)
|
|
255
|
+
})
|
|
256
|
+
return debug_info
|
|
257
|
+
|
|
258
|
+
# Step 4: Check server status
|
|
259
|
+
server_status = {}
|
|
260
|
+
if server_name in mcp_manager.servers:
|
|
261
|
+
server = mcp_manager.servers[server_name]
|
|
262
|
+
server_status = {
|
|
263
|
+
"status": server.status,
|
|
264
|
+
"capabilities": server.capabilities,
|
|
265
|
+
"has_session": server_name in mcp_manager.sessions
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
debug_info["steps"].append({
|
|
269
|
+
"step": "4_server_status",
|
|
270
|
+
"server_found": server_name in mcp_manager.servers,
|
|
271
|
+
"server_status": server_status
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
# Step 5: Check dynamic commands
|
|
275
|
+
final_commands = [name for name in command_manager.functions.keys() if name.startswith('mcp_')]
|
|
276
|
+
new_commands = [cmd for cmd in final_commands if cmd not in initial_commands]
|
|
277
|
+
|
|
278
|
+
debug_info["steps"].append({
|
|
279
|
+
"step": "5_dynamic_commands",
|
|
280
|
+
"total_mcp_commands": len(final_commands),
|
|
281
|
+
"new_commands": new_commands,
|
|
282
|
+
"dynamic_commands_tracker": mcp_manager.dynamic_commands.get_registered_commands()
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
# Step 6: Analyze tools
|
|
286
|
+
if server_name in mcp_manager.servers:
|
|
287
|
+
server = mcp_manager.servers[server_name]
|
|
288
|
+
tools = server.capabilities.get('tools', [])
|
|
289
|
+
tool_analysis = []
|
|
290
|
+
|
|
291
|
+
for tool in tools:
|
|
292
|
+
tool_name = tool.get('name', 'unknown')
|
|
293
|
+
#expected_cmd = f"mcp_{server_name}_{tool_name}"
|
|
294
|
+
expecte_cmd = "mcp_"+tool_name
|
|
295
|
+
is_registered = expected_cmd in command_manager.functions
|
|
296
|
+
tool_analysis.append({
|
|
297
|
+
"tool_name": tool_name,
|
|
298
|
+
"expected_command": expected_cmd,
|
|
299
|
+
"is_registered": is_registered,
|
|
300
|
+
"tool_details": tool
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
debug_info["steps"].append({
|
|
304
|
+
"step": "6_tool_analysis",
|
|
305
|
+
"tools_count": len(tools),
|
|
306
|
+
"tool_analysis": tool_analysis
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
# Step 7: Manual verification if session exists
|
|
310
|
+
if server_name in mcp_manager.sessions:
|
|
311
|
+
try:
|
|
312
|
+
session = mcp_manager.sessions[server_name]
|
|
313
|
+
tools_result = await session.list_tools()
|
|
314
|
+
debug_info["steps"].append({
|
|
315
|
+
"step": "7_manual_verification",
|
|
316
|
+
"success": True,
|
|
317
|
+
"direct_tools_count": len(tools_result.tools),
|
|
318
|
+
"direct_tools": [{
|
|
319
|
+
"name": tool.name,
|
|
320
|
+
"description": tool.description
|
|
321
|
+
} for tool in tools_result.tools]
|
|
322
|
+
})
|
|
323
|
+
except Exception as e:
|
|
324
|
+
debug_info["steps"].append({
|
|
325
|
+
"step": "7_manual_verification",
|
|
326
|
+
"success": False,
|
|
327
|
+
"error": str(e)
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
return debug_info
|
|
331
|
+
|
|
332
|
+
@command()
|
|
333
|
+
async def mcp_check_tools(context=None):
|
|
334
|
+
"""Check availability of installation tools
|
|
335
|
+
|
|
336
|
+
Example:
|
|
337
|
+
{ "mcp_check_tools": {} }
|
|
338
|
+
"""
|
|
339
|
+
tools = await MCPServerInstaller.check_tools()
|
|
340
|
+
return tools
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
@command()
|
|
344
|
+
async def mcp_list_dynamic_commands(context=None):
|
|
345
|
+
"""List dynamically registered MCP commands
|
|
346
|
+
|
|
347
|
+
Example:
|
|
348
|
+
{ "mcp_list_dynamic_commands": {} }
|
|
349
|
+
"""
|
|
350
|
+
commands = mcp_manager.dynamic_commands.get_registered_commands()
|
|
351
|
+
return {"dynamic_commands": commands, "count": len(commands)}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
@command()
|
|
355
|
+
async def mcp_refresh_dynamic_commands(context=None):
|
|
356
|
+
"""Refresh dynamic command registration for all connected servers
|
|
357
|
+
|
|
358
|
+
Example:
|
|
359
|
+
{ "mcp_refresh_dynamic_commands": {} }
|
|
360
|
+
"""
|
|
361
|
+
refreshed = []
|
|
362
|
+
for name, session in mcp_manager.sessions.items():
|
|
363
|
+
tools = await session.list_tools()
|
|
364
|
+
await mcp_manager.dynamic_commands.register_tools(name, tools.tools)
|
|
365
|
+
refreshed.append(f"{name}: {len(tools.tools)} tools")
|
|
366
|
+
|
|
367
|
+
return {"refreshed_servers": refreshed, "total_dynamic_commands": len(mcp_manager.dynamic_commands.get_registered_commands())}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""OAuth token storage implementation for MCP servers."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from mcp.client.auth import TokenStorage
|
|
9
|
+
from mcp.shared.auth import OAuthToken, OAuthClientInformationFull
|
|
10
|
+
except ImportError:
|
|
11
|
+
# Fallback if MCP not available
|
|
12
|
+
class TokenStorage:
|
|
13
|
+
pass
|
|
14
|
+
OAuthToken = None
|
|
15
|
+
OAuthClientInformationFull = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MCPTokenStorage(TokenStorage):
|
|
19
|
+
"""Token storage implementation for MCP OAuth flows.
|
|
20
|
+
|
|
21
|
+
Stores tokens and client information in the MCP server configuration.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, server_name: str, mcp_manager):
|
|
25
|
+
self.server_name = server_name
|
|
26
|
+
self.mcp_manager = mcp_manager
|
|
27
|
+
|
|
28
|
+
async def get_tokens(self) -> Optional[OAuthToken]:
|
|
29
|
+
"""Get stored OAuth tokens for the server."""
|
|
30
|
+
if OAuthToken is None:
|
|
31
|
+
return None
|
|
32
|
+
|
|
33
|
+
if self.server_name not in self.mcp_manager.servers:
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
server = self.mcp_manager.servers[self.server_name]
|
|
37
|
+
|
|
38
|
+
if not server.access_token:
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
# Parse token expiration
|
|
42
|
+
expires_at = None
|
|
43
|
+
if server.token_expires_at:
|
|
44
|
+
try:
|
|
45
|
+
expires_at = datetime.fromisoformat(server.token_expires_at.replace('Z', '+00:00'))
|
|
46
|
+
except ValueError:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
return OAuthToken(
|
|
50
|
+
access_token=server.access_token,
|
|
51
|
+
refresh_token=server.refresh_token,
|
|
52
|
+
expires_at=expires_at,
|
|
53
|
+
scope=" ".join(server.scopes) if server.scopes else None
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
async def set_tokens(self, tokens: OAuthToken) -> None:
|
|
57
|
+
"""Store OAuth tokens for the server."""
|
|
58
|
+
if self.server_name not in self.mcp_manager.servers:
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
server = self.mcp_manager.servers[self.server_name]
|
|
62
|
+
|
|
63
|
+
# Update server with token information
|
|
64
|
+
server.access_token = tokens.access_token
|
|
65
|
+
server.refresh_token = tokens.refresh_token
|
|
66
|
+
|
|
67
|
+
if hasattr(tokens, 'expires_at') and tokens.expires_at:
|
|
68
|
+
server.token_expires_at = tokens.expires_at.isoformat()
|
|
69
|
+
elif hasattr(tokens, 'expires_in') and tokens.expires_in:
|
|
70
|
+
# Calculate expires_at from expires_in
|
|
71
|
+
from datetime import datetime, timedelta
|
|
72
|
+
expires_at = datetime.now() + timedelta(seconds=tokens.expires_in)
|
|
73
|
+
server.token_expires_at = expires_at.isoformat()
|
|
74
|
+
else:
|
|
75
|
+
server.token_expires_at = None
|
|
76
|
+
|
|
77
|
+
if tokens.scope:
|
|
78
|
+
server.scopes = tokens.scope.split(" ")
|
|
79
|
+
|
|
80
|
+
# Save configuration
|
|
81
|
+
self.mcp_manager.save_config()
|
|
82
|
+
|
|
83
|
+
async def get_client_info(self) -> Optional[OAuthClientInformationFull]:
|
|
84
|
+
"""Get stored OAuth client information."""
|
|
85
|
+
if OAuthClientInformationFull is None:
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
if self.server_name not in self.mcp_manager.servers:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
server = self.mcp_manager.servers[self.server_name]
|
|
92
|
+
|
|
93
|
+
if not server.client_id:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
# Create client info from server configuration
|
|
97
|
+
client_info_data = {
|
|
98
|
+
"client_id": server.client_id,
|
|
99
|
+
"client_name": f"MindRoot MCP Client - {server.name}",
|
|
100
|
+
"redirect_uris": [server.redirect_uri] if server.redirect_uri else [],
|
|
101
|
+
"grant_types": ["authorization_code", "refresh_token"],
|
|
102
|
+
"response_types": ["code"],
|
|
103
|
+
"scope": " ".join(server.scopes) if server.scopes else "user"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if server.client_secret:
|
|
107
|
+
client_info_data["client_secret"] = server.client_secret
|
|
108
|
+
|
|
109
|
+
return OAuthClientInformationFull(**client_info_data)
|
|
110
|
+
|
|
111
|
+
async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
|
|
112
|
+
"""Store OAuth client information."""
|
|
113
|
+
if self.server_name not in self.mcp_manager.servers:
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
server = self.mcp_manager.servers[self.server_name]
|
|
117
|
+
|
|
118
|
+
# Update server with client information
|
|
119
|
+
server.client_id = client_info.client_id
|
|
120
|
+
|
|
121
|
+
if hasattr(client_info, 'client_secret') and client_info.client_secret:
|
|
122
|
+
server.client_secret = client_info.client_secret
|
|
123
|
+
|
|
124
|
+
if client_info.redirect_uris:
|
|
125
|
+
server.redirect_uri = client_info.redirect_uris[0]
|
|
126
|
+
|
|
127
|
+
if client_info.scope:
|
|
128
|
+
server.scopes = client_info.scope.split(" ")
|
|
129
|
+
|
|
130
|
+
# Save configuration
|
|
131
|
+
self.mcp_manager.save_config()
|
|
132
|
+
|
|
133
|
+
def clear_tokens(self):
|
|
134
|
+
"""Clear stored tokens for the server."""
|
|
135
|
+
if self.server_name not in self.mcp_manager.servers:
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
server = self.mcp_manager.servers[self.server_name]
|
|
139
|
+
server.access_token = None
|
|
140
|
+
server.refresh_token = None
|
|
141
|
+
server.token_expires_at = None
|
|
142
|
+
|
|
143
|
+
# Save configuration
|
|
144
|
+
self.mcp_manager.save_config()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import asyncio
|
|
3
|
+
from typing import Dict, Optional
|
|
4
|
+
|
|
5
|
+
class MCPServerInstaller:
|
|
6
|
+
"""Handles installation of MCP servers via different methods"""
|
|
7
|
+
|
|
8
|
+
@staticmethod
|
|
9
|
+
async def check_tools() -> Dict[str, bool]:
|
|
10
|
+
"""Check if installation tools are available"""
|
|
11
|
+
tools = {}
|
|
12
|
+
|
|
13
|
+
for tool in ['uvx', 'npx', 'npm', 'pip']:
|
|
14
|
+
try:
|
|
15
|
+
result = subprocess.run([tool, '--version'],
|
|
16
|
+
capture_output=True, text=True, timeout=5)
|
|
17
|
+
tools[tool] = result.returncode == 0
|
|
18
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
19
|
+
tools[tool] = False
|
|
20
|
+
|
|
21
|
+
return tools
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
async def install_with_uvx(package: str, args: list = None) -> bool:
|
|
25
|
+
"""Install package with uvx"""
|
|
26
|
+
if args is None:
|
|
27
|
+
args = []
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# For uvx, we don't need to install, just check if it can run
|
|
31
|
+
cmd = ['uvx', '--help']
|
|
32
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
33
|
+
return result.returncode == 0
|
|
34
|
+
except Exception as e:
|
|
35
|
+
print(f"Error with uvx: {e}")
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
async def install_with_npx(package: str, args: list = None) -> bool:
|
|
40
|
+
"""Install package with npx (similar to uvx, runs without global install)"""
|
|
41
|
+
if args is None:
|
|
42
|
+
args = []
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
# For npx, we don't need to install, just check if it can run
|
|
46
|
+
cmd = ['npx', '--help']
|
|
47
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
|
|
48
|
+
return result.returncode == 0
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"Error with npx: {e}")
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
async def install_with_pip(package: str, args: list = None) -> bool:
|
|
55
|
+
"""Install package with pip"""
|
|
56
|
+
if args is None:
|
|
57
|
+
args = []
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
cmd = ['pip', 'install'] + args + [package]
|
|
61
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
|
62
|
+
return result.returncode == 0
|
|
63
|
+
except Exception as e:
|
|
64
|
+
print(f"Error installing with pip: {e}")
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
async def install_with_npm(package: str, args: list = None) -> bool:
|
|
69
|
+
"""Install package with npm"""
|
|
70
|
+
if args is None:
|
|
71
|
+
args = []
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
cmd = ['npm', 'install', '-g'] + args + [package]
|
|
75
|
+
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
|
76
|
+
return result.returncode == 0
|
|
77
|
+
except Exception as e:
|
|
78
|
+
print(f"Error installing with npm: {e}")
|
|
79
|
+
return False
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
setup(
|
|
4
|
+
name="mr-mcp",
|
|
5
|
+
version="1.0.0",
|
|
6
|
+
description="Model Context Protocol integration for MindRoot",
|
|
7
|
+
packages=find_packages(where="src"),
|
|
8
|
+
package_dir={"": "src"},
|
|
9
|
+
package_data={
|
|
10
|
+
"mr_mcp": [
|
|
11
|
+
"templates/*.jinja2",
|
|
12
|
+
"inject/*.jinja2",
|
|
13
|
+
"static/js/*.js",
|
|
14
|
+
"static/css/*.css",
|
|
15
|
+
"data/*.json"
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
install_requires=[
|
|
19
|
+
"mcp>=1.9.0",
|
|
20
|
+
"httpx>=0.24.0",
|
|
21
|
+
"pydantic>=2.0.0",
|
|
22
|
+
"aiofiles>=23.0.0",
|
|
23
|
+
"uv>=0.7.0"
|
|
24
|
+
],
|
|
25
|
+
python_requires=">=3.8",
|
|
26
|
+
)
|