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,134 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Test script to verify dynamic command registration for both uvx and npx MCP servers
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
# Add the mindroot lib to path
|
|
11
|
+
sys.path.insert(0, '/files/mindroot/src/mindroot')
|
|
12
|
+
|
|
13
|
+
from .mod import mcp_manager, MCPServer
|
|
14
|
+
from src.mr_mcp.dynamic_commands import MCPDynamicCommands
|
|
15
|
+
from lib.providers.commands import command_manager
|
|
16
|
+
|
|
17
|
+
async def test_dynamic_command_registration():
|
|
18
|
+
print("Testing dynamic command registration for MCP servers...")
|
|
19
|
+
|
|
20
|
+
# Check current registered commands
|
|
21
|
+
print(f"\nCurrently registered commands: {len(command_manager.functions)}")
|
|
22
|
+
mcp_commands = [name for name in command_manager.functions.keys() if name.startswith('mcp_')]
|
|
23
|
+
print(f"Current MCP commands: {mcp_commands}")
|
|
24
|
+
|
|
25
|
+
# Test creating both uvx and npx servers
|
|
26
|
+
print("\n=== Testing UVX Server (Calculator) ===")
|
|
27
|
+
|
|
28
|
+
uvx_server = EnhancedMCPServer(
|
|
29
|
+
name="test_calculator",
|
|
30
|
+
description="Test calculator server via uvx",
|
|
31
|
+
command="uvx",
|
|
32
|
+
args=["mcp-server-calculator"],
|
|
33
|
+
install_method="uvx",
|
|
34
|
+
install_package="mcp-server-calculator",
|
|
35
|
+
auto_install=True
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
enhanced_mcp_manager.add_server("test_calculator", uvx_server)
|
|
39
|
+
print(f"Added uvx server: {uvx_server.name}")
|
|
40
|
+
print(f"Command: {uvx_server.command} {' '.join(uvx_server.args)}")
|
|
41
|
+
|
|
42
|
+
print("\n=== Testing NPX Server (GitHub) ===")
|
|
43
|
+
|
|
44
|
+
npx_server = EnhancedMCPServer(
|
|
45
|
+
name="test_github",
|
|
46
|
+
description="Test GitHub server via npx",
|
|
47
|
+
command="npx",
|
|
48
|
+
args=["-y", "@modelcontextprotocol/server-github"],
|
|
49
|
+
install_method="npx",
|
|
50
|
+
install_package="@modelcontextprotocol/server-github",
|
|
51
|
+
auto_install=True
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
enhanced_mcp_manager.add_server("test_github", npx_server)
|
|
55
|
+
print(f"Added npx server: {npx_server.name}")
|
|
56
|
+
print(f"Command: {npx_server.command} {' '.join(npx_server.args)}")
|
|
57
|
+
|
|
58
|
+
# Check if dynamic commands object is properly initialized
|
|
59
|
+
print(f"\n=== Dynamic Commands System ===")
|
|
60
|
+
print(f"Dynamic commands object: {enhanced_mcp_manager.dynamic_commands}")
|
|
61
|
+
print(f"Sessions reference set: {bool(enhanced_mcp_manager.dynamic_commands.sessions)}")
|
|
62
|
+
print(f"Registered dynamic commands: {enhanced_mcp_manager.dynamic_commands.get_registered_commands()}")
|
|
63
|
+
|
|
64
|
+
# Test the registration process manually
|
|
65
|
+
print("\n=== Manual Tool Registration Test ===")
|
|
66
|
+
|
|
67
|
+
# Create a mock tool object
|
|
68
|
+
class MockTool:
|
|
69
|
+
def __init__(self, name, description):
|
|
70
|
+
self.name = name
|
|
71
|
+
self.description = description
|
|
72
|
+
self.inputSchema = {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"properties": {
|
|
75
|
+
"expression": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"description": "Mathematical expression to evaluate"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"required": ["expression"]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
mock_tools = [MockTool("calculate", "Perform mathematical calculations")]
|
|
84
|
+
|
|
85
|
+
print("Testing tool registration for mock calculator...")
|
|
86
|
+
await enhanced_mcp_manager.dynamic_commands.register_tools("test_calculator", mock_tools)
|
|
87
|
+
|
|
88
|
+
# Check if command was registered
|
|
89
|
+
expected_cmd = "mcp_test_calculator_calculate"
|
|
90
|
+
if expected_cmd in command_manager.functions:
|
|
91
|
+
print(f"✅ SUCCESS: Command {expected_cmd} was registered!")
|
|
92
|
+
cmd_info = command_manager.functions[expected_cmd]
|
|
93
|
+
if hasattr(cmd_info, 'docstring'):
|
|
94
|
+
print(f" Docstring: {cmd_info.docstring[:100]}...")
|
|
95
|
+
else:
|
|
96
|
+
print(f" Command info: {type(cmd_info)}")
|
|
97
|
+
else:
|
|
98
|
+
print(f"❌ FAILED: Command {expected_cmd} was not registered")
|
|
99
|
+
print(f" Available commands: {list(command_manager.functions.keys())[-5:]}")
|
|
100
|
+
|
|
101
|
+
# Test with npx server
|
|
102
|
+
print("\nTesting tool registration for mock GitHub server...")
|
|
103
|
+
|
|
104
|
+
github_tools = [MockTool("create_repository", "Create a new GitHub repository")]
|
|
105
|
+
await enhanced_mcp_manager.dynamic_commands.register_tools("test_github", github_tools)
|
|
106
|
+
|
|
107
|
+
expected_cmd2 = "mcp_test_github_create_repository"
|
|
108
|
+
if expected_cmd2 in command_manager.functions:
|
|
109
|
+
print(f"✅ SUCCESS: Command {expected_cmd2} was registered!")
|
|
110
|
+
else:
|
|
111
|
+
print(f"❌ FAILED: Command {expected_cmd2} was not registered")
|
|
112
|
+
|
|
113
|
+
# Final summary
|
|
114
|
+
print(f"\n=== Final Summary ===")
|
|
115
|
+
final_mcp_commands = [name for name in command_manager.functions.keys() if name.startswith('mcp_')]
|
|
116
|
+
print(f"Total MCP commands now: {len(final_mcp_commands)}")
|
|
117
|
+
print(f"Dynamic commands: {[cmd for cmd in final_mcp_commands if 'test_' in cmd]}")
|
|
118
|
+
|
|
119
|
+
print("\n=== Conclusion ===")
|
|
120
|
+
print("The dynamic command registration system works the same for both uvx and npx servers.")
|
|
121
|
+
print("The issue you're experiencing might be:")
|
|
122
|
+
print("1. The MCP server isn't actually connecting successfully")
|
|
123
|
+
print("2. The server doesn't expose tools (some servers only have resources/prompts)")
|
|
124
|
+
print("3. There's an error during the connection process")
|
|
125
|
+
print("4. The admin interface cache needs to be refreshed")
|
|
126
|
+
|
|
127
|
+
print("\nTo debug further:")
|
|
128
|
+
print("1. Check the server logs when connecting")
|
|
129
|
+
print("2. Verify the server actually starts and responds")
|
|
130
|
+
print("3. Check if the server exposes tools vs just resources")
|
|
131
|
+
print("4. Try refreshing the admin interface after connection")
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
asyncio.run(test_dynamic_command_registration())
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Before running, specify running MCP RS server URL.
|
|
3
|
+
To spin up RS server locally, see
|
|
4
|
+
examples/servers/simple-auth/README.md
|
|
5
|
+
|
|
6
|
+
cd to the `examples/snippets` directory and run:
|
|
7
|
+
uv run oauth-client
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
from urllib.parse import parse_qs, urlparse
|
|
12
|
+
|
|
13
|
+
from pydantic import AnyUrl
|
|
14
|
+
|
|
15
|
+
from mcp import ClientSession
|
|
16
|
+
from mcp.client.auth import OAuthClientProvider, TokenStorage
|
|
17
|
+
from mcp.client.streamable_http import streamablehttp_client
|
|
18
|
+
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InMemoryTokenStorage(TokenStorage):
|
|
22
|
+
"""Demo In-memory token storage implementation."""
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
self.tokens: OAuthToken | None = None
|
|
26
|
+
self.client_info: OAuthClientInformationFull | None = None
|
|
27
|
+
|
|
28
|
+
async def get_tokens(self) -> OAuthToken | None:
|
|
29
|
+
"""Get stored tokens."""
|
|
30
|
+
return self.tokens
|
|
31
|
+
|
|
32
|
+
async def set_tokens(self, tokens: OAuthToken) -> None:
|
|
33
|
+
"""Store tokens."""
|
|
34
|
+
self.tokens = tokens
|
|
35
|
+
|
|
36
|
+
async def get_client_info(self) -> OAuthClientInformationFull | None:
|
|
37
|
+
"""Get stored client information."""
|
|
38
|
+
return self.client_info
|
|
39
|
+
|
|
40
|
+
async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
|
|
41
|
+
"""Store client information."""
|
|
42
|
+
self.client_info = client_info
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
async def handle_redirect(auth_url: str) -> None:
|
|
46
|
+
print(f"Visit: {auth_url}")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def handle_callback() -> tuple[str, str | None]:
|
|
50
|
+
callback_url = input("Paste callback URL: ")
|
|
51
|
+
params = parse_qs(urlparse(callback_url).query)
|
|
52
|
+
print("Callback URL parameters:")
|
|
53
|
+
print(params)
|
|
54
|
+
return params["code"][0], params.get("state", [None])[0]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def main():
|
|
58
|
+
"""Run the OAuth client example."""
|
|
59
|
+
oauth_auth = OAuthClientProvider(
|
|
60
|
+
server_url="https://mcp.notion.com",
|
|
61
|
+
client_metadata=OAuthClientMetadata(
|
|
62
|
+
client_name="Example MCP Client",
|
|
63
|
+
redirect_uris=[AnyUrl("http://localhost:3000/callback")],
|
|
64
|
+
grant_types=["authorization_code", "refresh_token"],
|
|
65
|
+
response_types=["code"],
|
|
66
|
+
scope="user",
|
|
67
|
+
),
|
|
68
|
+
storage=InMemoryTokenStorage(),
|
|
69
|
+
redirect_handler=handle_redirect,
|
|
70
|
+
callback_handler=handle_callback,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
print("Starting.")
|
|
74
|
+
async with streamablehttp_client("https://mcp.notion.com/sse", auth=oauth_auth) as (read, write, _):
|
|
75
|
+
print("1")
|
|
76
|
+
async with ClientSession(read, write) as session:
|
|
77
|
+
print("2")
|
|
78
|
+
await session.initialize()
|
|
79
|
+
print("3")
|
|
80
|
+
tools = await session.list_tools()
|
|
81
|
+
print(f"Available tools: {[tool.name for tool in tools.tools]}")
|
|
82
|
+
|
|
83
|
+
resources = await session.list_resources()
|
|
84
|
+
print(f"Available resources: {[r.uri for r in resources.resources]}")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def run():
|
|
88
|
+
asyncio.run(main())
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
run()
|
|
@@ -12,15 +12,20 @@ from .init_persona import *
|
|
|
12
12
|
@service()
|
|
13
13
|
async def get_persona_data(persona_name, context=None):
|
|
14
14
|
print("persona name is", persona_name, file=sys.stderr)
|
|
15
|
-
# use pwd as base dir
|
|
16
|
-
# current working dir of process
|
|
17
15
|
pwd = os.getcwd()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
|
|
17
|
+
# Handle registry personas: registry/owner/name
|
|
18
|
+
if persona_name.startswith('registry/'):
|
|
19
|
+
persona_path = os.path.join(pwd, 'personas', persona_name)
|
|
20
|
+
else:
|
|
21
|
+
# Legacy support: check local first, then shared
|
|
22
|
+
persona_path = os.path.join(pwd, 'personas', 'local', persona_name)
|
|
21
23
|
if not os.path.exists(persona_path):
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
persona_path = os.path.join(pwd, 'personas', 'shared', persona_name)
|
|
25
|
+
|
|
26
|
+
if not os.path.exists(persona_path):
|
|
27
|
+
# need to raise an error here
|
|
28
|
+
raise Exception(f"Persona {persona_name} not found in {persona_path}")
|
|
24
29
|
|
|
25
30
|
# read the persona data
|
|
26
31
|
# use blue background and yellow text
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
<body>
|
|
131
131
|
<div class="signup-container">
|
|
132
132
|
<div class="logo-container">
|
|
133
|
-
<img src="/
|
|
133
|
+
<img src="/imgs/logo.png" alt="MindRoot Logo" class="logo">
|
|
134
134
|
<h1>Create Your MindRoot Account</h1>
|
|
135
135
|
</div>
|
|
136
136
|
|
|
@@ -266,7 +266,6 @@ async def get_available_features(active_only: bool = True, context=None) -> List
|
|
|
266
266
|
|
|
267
267
|
features = await _subscription_manager.storage.get_all_features(active_only)
|
|
268
268
|
return [feature.to_dict() for feature in features]
|
|
269
|
-
|
|
270
269
|
@service()
|
|
271
270
|
async def create_plan_feature(feature_data: Dict[str, Any], context=None) -> Dict[str, Any]:
|
|
272
271
|
"""Create a new plan feature
|
|
@@ -549,6 +548,19 @@ async def process_subscription_event(event_data: Dict[str, Any], context=None) -
|
|
|
549
548
|
}
|
|
550
549
|
)
|
|
551
550
|
|
|
551
|
+
# Notify any plugins that handle subscription creation
|
|
552
|
+
try:
|
|
553
|
+
from lib.providers.services import service_manager
|
|
554
|
+
await service_manager.handle_subscription_created(
|
|
555
|
+
user_id=username,
|
|
556
|
+
subscription_id=subscription.subscription_id,
|
|
557
|
+
plan_id=plan_id,
|
|
558
|
+
metadata=metadata
|
|
559
|
+
)
|
|
560
|
+
logger.info(f"Notified subscription handlers for {username}")
|
|
561
|
+
except Exception as e:
|
|
562
|
+
logger.error(f"Failed to notify subscription handlers: {e}")
|
|
563
|
+
|
|
552
564
|
return {
|
|
553
565
|
'status': 'success',
|
|
554
566
|
'event_type': event_type,
|
|
@@ -558,7 +570,6 @@ async def process_subscription_event(event_data: Dict[str, Any], context=None) -
|
|
|
558
570
|
else:
|
|
559
571
|
logger.error(f"Plan not found: {plan_id}")
|
|
560
572
|
return {'status': 'error', 'message': f"Plan not found: {plan_id}"}
|
|
561
|
-
|
|
562
573
|
elif event_type == 'subscription_renewed':
|
|
563
574
|
# Handle subscription renewal
|
|
564
575
|
provider_subscription_id = normalized_event.get('subscription_id')
|
|
@@ -840,4 +851,4 @@ async def get_my_subscriptions(params, context=None):
|
|
|
840
851
|
|
|
841
852
|
result += "\n"
|
|
842
853
|
|
|
843
|
-
return result
|
|
854
|
+
return result
|
|
@@ -235,7 +235,9 @@ async def api_create_checkout(plan_id: str,
|
|
|
235
235
|
provider: str = Query('stripe')):
|
|
236
236
|
"""Create checkout session for subscription"""
|
|
237
237
|
try:
|
|
238
|
+
print("checkout")
|
|
238
239
|
if not request.state.user:
|
|
240
|
+
print("Unauthorized")
|
|
239
241
|
raise UNAUTHORIZED
|
|
240
242
|
|
|
241
243
|
username = request.state.user.username
|
|
@@ -243,6 +245,7 @@ async def api_create_checkout(plan_id: str,
|
|
|
243
245
|
# Get plan details
|
|
244
246
|
plan = await get_subscription_plan(plan_id, context=request)
|
|
245
247
|
if not plan:
|
|
248
|
+
print("could not find plan",plan_id)
|
|
246
249
|
raise NOT_FOUND
|
|
247
250
|
|
|
248
251
|
# Call provider-specific checkout service
|
|
@@ -93,6 +93,7 @@ async def initialize_admin(user_data_root: str, app) -> Tuple[Optional[str], Opt
|
|
|
93
93
|
|
|
94
94
|
if not (username and password):
|
|
95
95
|
username, password = generate_random_credentials()
|
|
96
|
+
username = 'admin'
|
|
96
97
|
print("\n" + "="*50)
|
|
97
98
|
print("INITIAL ADMIN CREDENTIALS GENERATED:")
|
|
98
99
|
print(f"Username: {username}")
|
|
@@ -1,33 +1,88 @@
|
|
|
1
|
-
#from mindroot.coreplugins.email.mod import EmailMessage
|
|
2
1
|
from lib.providers.services import service_manager
|
|
3
2
|
from datetime import datetime, timedelta
|
|
4
3
|
import secrets
|
|
5
4
|
import os
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
6
8
|
|
|
7
9
|
REQUIRE_EMAIL_VERIFY = os.environ.get('REQUIRE_EMAIL_VERIFY', '').lower() == 'true'
|
|
10
|
+
BASE_URL = os.environ.get('BASE_URL', 'http://localhost:8011')
|
|
8
11
|
|
|
9
12
|
async def send_verification_email(email: str, verification_token: str):
|
|
10
13
|
"""Send email verification link to user."""
|
|
11
|
-
verification_url = f"
|
|
14
|
+
verification_url = f"{BASE_URL}/verify-email?token={verification_token}"
|
|
15
|
+
|
|
12
16
|
email_html = f"""
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
<html>
|
|
18
|
+
<body>
|
|
19
|
+
<h1>Welcome to MindRoot!</h1>
|
|
20
|
+
<p>Please verify your email address by clicking the link below:</p>
|
|
21
|
+
<p><a href="{verification_url}" style="background-color: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px;">Verify Email</a></p>
|
|
22
|
+
<p>Or copy and paste this link into your browser:</p>
|
|
23
|
+
<p><code>{verification_url}</code></p>
|
|
24
|
+
<p>This link will expire in 24 hours.</p>
|
|
25
|
+
<br>
|
|
26
|
+
<p><small>If you did not create this account, please ignore this email.</small></p>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
19
29
|
"""
|
|
20
|
-
|
|
21
|
-
return False
|
|
30
|
+
|
|
22
31
|
try:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
result = await service_manager.send_email(
|
|
33
|
+
to=email,
|
|
34
|
+
subject="Verify Your MindRoot Account",
|
|
35
|
+
body=email_html # HTML content will be auto-detected
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
if result.get('success'):
|
|
39
|
+
logger.info(f"Verification email sent successfully to {email}")
|
|
40
|
+
return True
|
|
41
|
+
else:
|
|
42
|
+
logger.error(f"Failed to send verification email to {email}: {result.get('error')}")
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
except Exception as e:
|
|
46
|
+
logger.error(f"Exception sending verification email to {email}: {e}")
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
async def send_password_reset_email(email: str, username: str, reset_token: str):
|
|
50
|
+
"""Send password reset email to user."""
|
|
51
|
+
reset_url = f"{BASE_URL}/reset-password?token={reset_token}"
|
|
52
|
+
|
|
53
|
+
email_html = f"""
|
|
54
|
+
<html>
|
|
55
|
+
<body>
|
|
56
|
+
<h1>Password Reset Request</h1>
|
|
57
|
+
<p>Hello {username},</p>
|
|
58
|
+
<p>You have requested to reset your password for your MindRoot account.</p>
|
|
59
|
+
<p>Click the link below to reset your password:</p>
|
|
60
|
+
<p><a href="{reset_url}" style="background-color: #f44336; color: white; padding: 10px 20px; text-decoration: none; border-radius: 4px;">Reset Password</a></p>
|
|
61
|
+
<p>Or copy and paste this link into your browser:</p>
|
|
62
|
+
<p><code>{reset_url}</code></p>
|
|
63
|
+
<p>This link will expire in 1 hour.</p>
|
|
64
|
+
<br>
|
|
65
|
+
<p><small>If you did not request this password reset, please ignore this email.</small></p>
|
|
66
|
+
</body>
|
|
67
|
+
</html>
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
result = await service_manager.send_email(
|
|
72
|
+
to=email,
|
|
73
|
+
subject="Password Reset Request - MindRoot",
|
|
74
|
+
body=email_html # HTML content will be auto-detected
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if result.get('success'):
|
|
78
|
+
logger.info(f"Password reset email sent successfully to {email}")
|
|
79
|
+
return True
|
|
80
|
+
else:
|
|
81
|
+
logger.error(f"Failed to send password reset email to {email}: {result.get('error')}")
|
|
82
|
+
return False
|
|
83
|
+
|
|
29
84
|
except Exception as e:
|
|
30
|
-
|
|
85
|
+
logger.error(f"Exception sending password reset email to {email}: {e}")
|
|
31
86
|
return False
|
|
32
87
|
|
|
33
88
|
def setup_verification() -> tuple[str, str, bool]:
|
|
@@ -70,20 +70,28 @@ async def create_user(user_data: UserCreate, roles: List[str] = None, skip_verif
|
|
|
70
70
|
async def verify_user(username: str, password: str, context=None) -> bool:
|
|
71
71
|
"""Verify user credentials and update last login"""
|
|
72
72
|
auth_file = os.path.join(USER_DATA_ROOT, username, "auth.json")
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
if not os.path.exists(auth_file):
|
|
75
|
+
print("user not found")
|
|
76
|
+
print("path", auth_file)
|
|
77
|
+
print("working dir", os.getcwd())
|
|
75
78
|
return False
|
|
76
79
|
|
|
77
80
|
with open(auth_file, 'r') as f:
|
|
78
81
|
auth_data = UserAuth(**json.load(f))
|
|
79
82
|
|
|
80
83
|
if bcrypt.checkpw(password.encode(), auth_data.password_hash.encode()):
|
|
84
|
+
print("check pw passed")
|
|
81
85
|
# Update last login
|
|
82
86
|
auth_data.last_login = datetime.utcnow().isoformat()
|
|
83
87
|
with open(auth_file, 'w') as f:
|
|
84
88
|
json.dump(auth_data.dict(), f, indent=2, default=str)
|
|
85
89
|
return True
|
|
86
|
-
|
|
90
|
+
else:
|
|
91
|
+
print("checkpw failed,")
|
|
92
|
+
print("auth_data", auth_data)
|
|
93
|
+
print("password:", password.encode())
|
|
94
|
+
return False
|
|
87
95
|
|
|
88
96
|
|
|
89
97
|
@service()
|