mcphub-server 3.0.4__tar.gz → 3.3.0__tar.gz
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.
- {mcphub_server-3.0.4/mcphub_server.egg-info → mcphub_server-3.3.0}/PKG-INFO +24 -5
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/README.md +21 -4
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/__init__.py +0 -7
- mcphub_server-3.3.0/core/admin_utils.py +27 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/api_keys.py +16 -10
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/auth.py +5 -2
- mcphub_server-3.3.0/core/config_snippets.py +128 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/dashboard/auth.py +102 -10
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/dashboard/routes.py +1617 -214
- mcphub_server-3.3.0/core/database.py +771 -0
- mcphub_server-3.3.0/core/encryption.py +212 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/health.py +111 -6
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/oauth/client_registry.py +3 -1
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/oauth/schemas.py +6 -2
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/oauth/server.py +6 -1
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/oauth/token_manager.py +13 -3
- mcphub_server-3.3.0/core/plugin_visibility.py +59 -0
- mcphub_server-3.3.0/core/settings.py +162 -0
- mcphub_server-3.3.0/core/site_api.py +674 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/site_manager.py +2 -0
- mcphub_server-3.3.0/core/templates/dashboard/404.html +59 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/templates/dashboard/api-keys/list.html +62 -62
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/templates/dashboard/audit-logs/list.html +41 -41
- mcphub_server-3.3.0/core/templates/dashboard/auth-login.html +209 -0
- mcphub_server-3.3.0/core/templates/dashboard/base.html +324 -0
- mcphub_server-3.3.0/core/templates/dashboard/connect.html +299 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/templates/dashboard/health/index.html +47 -47
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/templates/dashboard/health/projects-partial.html +5 -5
- mcphub_server-3.3.0/core/templates/dashboard/index.html +439 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/templates/dashboard/login.html +56 -39
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/templates/dashboard/oauth-clients/list.html +47 -47
- mcphub_server-3.3.0/core/templates/dashboard/partials/head_assets.html +105 -0
- mcphub_server-3.3.0/core/templates/dashboard/profile.html +90 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/templates/dashboard/projects/detail.html +58 -58
- mcphub_server-3.3.0/core/templates/dashboard/projects/list.html +285 -0
- mcphub_server-3.3.0/core/templates/dashboard/service.html +185 -0
- mcphub_server-3.3.0/core/templates/dashboard/services_list.html +57 -0
- mcphub_server-3.3.0/core/templates/dashboard/settings/index.html +323 -0
- mcphub_server-3.3.0/core/templates/dashboard/sites/add.html +174 -0
- mcphub_server-3.3.0/core/templates/dashboard/sites/edit.html +172 -0
- mcphub_server-3.3.0/core/templates/dashboard/sites/list.html +193 -0
- mcphub_server-3.3.0/core/templates/dashboard/user-oauth-clients.html +241 -0
- mcphub_server-3.3.0/core/templates/oauth/authorize.html +268 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/tool_generator.py +9 -4
- mcphub_server-3.3.0/core/user_auth.py +483 -0
- mcphub_server-3.3.0/core/user_endpoints.py +429 -0
- mcphub_server-3.3.0/core/user_keys.py +246 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0/mcphub_server.egg-info}/PKG-INFO +24 -5
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/mcphub_server.egg-info/SOURCES.txt +38 -2
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/mcphub_server.egg-info/requires.txt +2 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/handlers/system.py +2 -2
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/handlers/variables.py +1 -1
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/profiles.py +2 -2
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/client.py +245 -96
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/handlers/auth.py +0 -18
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/handlers/database.py +29 -12
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/handlers/system.py +8 -8
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/plugin.py +15 -6
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/woocommerce/plugin.py +20 -6
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/client.py +233 -37
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/menus.py +3 -3
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/seo.py +3 -3
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/wp_cli.py +70 -29
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/handlers/system.py +3 -3
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/plugin.py +14 -1
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/pyproject.toml +3 -1
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/server.py +578 -81
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/server_multi.py +8 -13
- mcphub_server-3.3.0/tests/test_admin_system.py +188 -0
- mcphub_server-3.3.0/tests/test_config_snippets.py +159 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_dashboard.py +37 -0
- mcphub_server-3.3.0/tests/test_database.py +577 -0
- mcphub_server-3.3.0/tests/test_dynamic_endpoints_integration.py +115 -0
- mcphub_server-3.3.0/tests/test_encryption.py +315 -0
- mcphub_server-3.3.0/tests/test_f3_admin_stats.py +17 -0
- mcphub_server-3.3.0/tests/test_f3_last_tested.py +77 -0
- mcphub_server-3.3.0/tests/test_f3_service_pages.py +35 -0
- mcphub_server-3.3.0/tests/test_oauth_basic_auth.py +165 -0
- mcphub_server-3.3.0/tests/test_oauth_resource.py +333 -0
- mcphub_server-3.3.0/tests/test_oauth_revoke.py +227 -0
- mcphub_server-3.3.0/tests/test_plugin_visibility.py +148 -0
- mcphub_server-3.3.0/tests/test_site_api.py +353 -0
- mcphub_server-3.3.0/tests/test_tenant_isolation.py +40 -0
- mcphub_server-3.3.0/tests/test_user_auth.py +444 -0
- mcphub_server-3.3.0/tests/test_user_endpoints.py +366 -0
- mcphub_server-3.3.0/tests/test_user_keys.py +294 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_woocommerce_plugin.py +6 -5
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_wordpress_plugin.py +283 -1
- mcphub_server-3.0.4/core/site_registry.py +0 -371
- mcphub_server-3.0.4/core/templates/dashboard/base.html +0 -227
- mcphub_server-3.0.4/core/templates/dashboard/index.html +0 -292
- mcphub_server-3.0.4/core/templates/dashboard/projects/list.html +0 -259
- mcphub_server-3.0.4/core/templates/dashboard/settings/index.html +0 -210
- mcphub_server-3.0.4/core/templates/oauth/authorize.html +0 -170
- mcphub_server-3.0.4/core/unified_tools.py +0 -363
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/LICENSE +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/audit_log.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/context.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/dashboard/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/endpoints/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/endpoints/config.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/endpoints/factory.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/endpoints/middleware.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/endpoints/registry.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/i18n.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/middleware/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/oauth/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/oauth/csrf.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/oauth/pkce.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/oauth/storage.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/project_manager.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/rate_limiter.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/templates/base.html +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/templates/oauth/error.html +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/core/tool_registry.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/mcphub_server.egg-info/dependency_links.txt +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/mcphub_server.egg-info/entry_points.txt +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/mcphub_server.egg-info/top_level.txt +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/client.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/handlers/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/handlers/databases.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/handlers/documents.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/handlers/functions.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/handlers/messaging.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/handlers/storage.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/handlers/system.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/handlers/teams.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/handlers/users.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/appwrite/plugin.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/base.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/client.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/access.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/automation.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/collections.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/content.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/dashboards.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/files.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/items.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/system.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/handlers/users.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/directus/plugin.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/client.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/handlers/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/handlers/issues.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/handlers/pull_requests.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/handlers/repositories.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/handlers/users.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/handlers/webhooks.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/plugin.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/schemas/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/schemas/common.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/schemas/issue.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/schemas/pull_request.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/schemas/repository.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/schemas/user.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/gitea/schemas/webhook.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/client.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/handlers/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/handlers/credentials.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/handlers/executions.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/handlers/projects.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/handlers/tags.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/handlers/users.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/handlers/workflows.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/n8n/plugin.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/client.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/clients.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/dashboards.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/events.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/export.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/funnels.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/projects.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/reports.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/system.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/handlers/utils.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/openpanel/plugin.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/handlers/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/handlers/admin.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/handlers/functions.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/supabase/handlers/storage.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/woocommerce/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/comments.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/coupons.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/customers.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/media.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/orders.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/posts.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/products.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/reports.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/site.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/taxonomy.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/users.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/handlers/wp_cli.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/plugin.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/plugin_old_backup.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/schemas/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/schemas/common.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/schemas/media.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/schemas/order.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/schemas/post.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/schemas/product.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress/schemas/seo.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/handlers/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/handlers/bulk.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/handlers/database.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/schemas/__init__.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/schemas/bulk.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/schemas/database.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/plugins/wordpress_advanced/schemas/system.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/setup.cfg +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_api_keys.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_auth.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_auth_logic.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_oauth_client_registry.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_oauth_integration.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_oauth_pkce.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_oauth_schemas.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_oauth_storage.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_oauth_token_manager.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_rate_limiter.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_site_manager.py +0 -0
- {mcphub_server-3.0.4 → mcphub_server-3.3.0}/tests/test_tool_registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcphub-server
|
|
3
|
-
Version: 3.0
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: AI-native management hub for WordPress, WooCommerce, and self-hosted services via Model Context Protocol (MCP)
|
|
5
5
|
Author-email: MCP Hub <contact@mcphub.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -33,7 +33,9 @@ Requires-Dist: docker>=7.0.0
|
|
|
33
33
|
Requires-Dist: authlib>=1.5.0
|
|
34
34
|
Requires-Dist: PyJWT>=2.8.0
|
|
35
35
|
Requires-Dist: cryptography>=46.0.0
|
|
36
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
36
37
|
Requires-Dist: jinja2>=3.1.2
|
|
38
|
+
Requires-Dist: bcrypt>=4.0.0
|
|
37
39
|
Provides-Extra: dev
|
|
38
40
|
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
39
41
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -45,6 +47,8 @@ Requires-Dist: mypy>=1.7.0; extra == "dev"
|
|
|
45
47
|
Requires-Dist: pre-commit>=3.5.0; extra == "dev"
|
|
46
48
|
Dynamic: license-file
|
|
47
49
|
|
|
50
|
+
<!-- mcp-name: io.github.airano-ir/mcphub -->
|
|
51
|
+
|
|
48
52
|
# MCP Hub
|
|
49
53
|
|
|
50
54
|
<div align="center">
|
|
@@ -58,7 +62,7 @@ Connect your sites, stores, repos, and databases — manage them all through Cla
|
|
|
58
62
|
[](https://www.python.org/)
|
|
59
63
|
[](https://pypi.org/project/mcphub-server/)
|
|
60
64
|
[](https://hub.docker.com/r/airano/mcphub)
|
|
61
|
-
[]()
|
|
62
66
|
[]()
|
|
63
67
|
[](https://github.com/airano-ir/mcphub/actions/workflows/ci.yml)
|
|
64
68
|
|
|
@@ -153,7 +157,20 @@ curl http://localhost:8000/health
|
|
|
153
157
|
|
|
154
158
|
Open the **web dashboard** in your browser: **http://localhost:8000/dashboard**
|
|
155
159
|
|
|
156
|
-
You should see the login page.
|
|
160
|
+
You should see the login page. Log in with your `MASTER_API_KEY` or via **GitHub/Google OAuth** (if configured).
|
|
161
|
+
|
|
162
|
+
### Try It Now (No Setup Required)
|
|
163
|
+
|
|
164
|
+
**Don't want to self-host?** Try the demo instance at **[mcp.palebluedot.live](https://mcp.palebluedot.live)**:
|
|
165
|
+
|
|
166
|
+
1. Log in with **GitHub** or **Google**
|
|
167
|
+
2. Add your sites via the dashboard (My Sites → Add Service)
|
|
168
|
+
3. Go to **Connect** page — generate config for your AI client
|
|
169
|
+
4. Copy-paste the config into Claude Desktop, VS Code, or Claude Code
|
|
170
|
+
|
|
171
|
+
Your personal MCP endpoint: `https://mcp.palebluedot.live/u/{your-user-id}/{alias}/mcp`
|
|
172
|
+
|
|
173
|
+
---
|
|
157
174
|
|
|
158
175
|
### Configure Your Sites
|
|
159
176
|
|
|
@@ -342,12 +359,14 @@ MCP Hub supports **Open Dynamic Client Registration** (RFC 7591). ChatGPT can au
|
|
|
342
359
|
/appwrite/mcp → Appwrite tools (100 tools)
|
|
343
360
|
/directus/mcp → Directus tools (100 tools)
|
|
344
361
|
/project/{alias}/mcp → Per-project endpoint (auto-injects site)
|
|
362
|
+
/u/{user_id}/{alias}/mcp → Per-user endpoint (hosted/OAuth users)
|
|
345
363
|
```
|
|
346
364
|
|
|
347
365
|
**Recommendation**: Use plugin-specific endpoints instead of `/mcp` (596 tools) to minimize token usage.
|
|
348
366
|
|
|
349
367
|
| Endpoint | Use Case | Tools |
|
|
350
368
|
|----------|----------|------:|
|
|
369
|
+
| `/u/{user_id}/{alias}/mcp` | Hosted users (OAuth login) | 22-100 |
|
|
351
370
|
| `/project/{alias}/mcp` | Single-site workflow (recommended) | 22-100 |
|
|
352
371
|
| `/{plugin}/mcp` | Multi-site management | 23-101 |
|
|
353
372
|
| `/mcp` | Admin & discovery only | 596 |
|
|
@@ -368,7 +387,7 @@ Some MCP Hub tools require companion WordPress plugins:
|
|
|
368
387
|
|
|
369
388
|
| Tools | Requirement |
|
|
370
389
|
|-------|-------------|
|
|
371
|
-
| SEO tools (`wordpress_get_post_seo`, etc.) | [SEO
|
|
390
|
+
| SEO tools (`wordpress_get_post_seo`, etc.) | [Airano MCP SEO Bridge](https://wordpress.org/plugins/airano-mcp-seo-bridge/) ([GitHub](wordpress-plugin/airano-mcp-seo-bridge/)) + Rank Math or Yoast SEO |
|
|
372
391
|
| WP-CLI tools (15 tools: `wp_cache_*`, `wp_db_*`, etc.) | Docker socket + `CONTAINER` env var |
|
|
373
392
|
| WordPress Advanced database/system tools | Docker socket + `CONTAINER` env var |
|
|
374
393
|
| OpenPanel analytics integration | [OpenPanel Self-Hosted](wordpress-plugin/openpanel-self-hosted/) ([Download ZIP](wordpress-plugin/openpanel-self-hosted.zip)) |
|
|
@@ -408,7 +427,7 @@ Without Docker socket, WP-CLI tools return "not available" but all REST API tool
|
|
|
408
427
|
# Install with dev dependencies
|
|
409
428
|
pip install -e ".[dev]"
|
|
410
429
|
|
|
411
|
-
# Run tests (
|
|
430
|
+
# Run tests (481 tests)
|
|
412
431
|
pytest
|
|
413
432
|
|
|
414
433
|
# Format and lint
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<!-- mcp-name: io.github.airano-ir/mcphub -->
|
|
2
|
+
|
|
1
3
|
# MCP Hub
|
|
2
4
|
|
|
3
5
|
<div align="center">
|
|
@@ -11,7 +13,7 @@ Connect your sites, stores, repos, and databases — manage them all through Cla
|
|
|
11
13
|
[](https://www.python.org/)
|
|
12
14
|
[](https://pypi.org/project/mcphub-server/)
|
|
13
15
|
[](https://hub.docker.com/r/airano/mcphub)
|
|
14
|
-
[]()
|
|
15
17
|
[]()
|
|
16
18
|
[](https://github.com/airano-ir/mcphub/actions/workflows/ci.yml)
|
|
17
19
|
|
|
@@ -106,7 +108,20 @@ curl http://localhost:8000/health
|
|
|
106
108
|
|
|
107
109
|
Open the **web dashboard** in your browser: **http://localhost:8000/dashboard**
|
|
108
110
|
|
|
109
|
-
You should see the login page.
|
|
111
|
+
You should see the login page. Log in with your `MASTER_API_KEY` or via **GitHub/Google OAuth** (if configured).
|
|
112
|
+
|
|
113
|
+
### Try It Now (No Setup Required)
|
|
114
|
+
|
|
115
|
+
**Don't want to self-host?** Try the demo instance at **[mcp.palebluedot.live](https://mcp.palebluedot.live)**:
|
|
116
|
+
|
|
117
|
+
1. Log in with **GitHub** or **Google**
|
|
118
|
+
2. Add your sites via the dashboard (My Sites → Add Service)
|
|
119
|
+
3. Go to **Connect** page — generate config for your AI client
|
|
120
|
+
4. Copy-paste the config into Claude Desktop, VS Code, or Claude Code
|
|
121
|
+
|
|
122
|
+
Your personal MCP endpoint: `https://mcp.palebluedot.live/u/{your-user-id}/{alias}/mcp`
|
|
123
|
+
|
|
124
|
+
---
|
|
110
125
|
|
|
111
126
|
### Configure Your Sites
|
|
112
127
|
|
|
@@ -295,12 +310,14 @@ MCP Hub supports **Open Dynamic Client Registration** (RFC 7591). ChatGPT can au
|
|
|
295
310
|
/appwrite/mcp → Appwrite tools (100 tools)
|
|
296
311
|
/directus/mcp → Directus tools (100 tools)
|
|
297
312
|
/project/{alias}/mcp → Per-project endpoint (auto-injects site)
|
|
313
|
+
/u/{user_id}/{alias}/mcp → Per-user endpoint (hosted/OAuth users)
|
|
298
314
|
```
|
|
299
315
|
|
|
300
316
|
**Recommendation**: Use plugin-specific endpoints instead of `/mcp` (596 tools) to minimize token usage.
|
|
301
317
|
|
|
302
318
|
| Endpoint | Use Case | Tools |
|
|
303
319
|
|----------|----------|------:|
|
|
320
|
+
| `/u/{user_id}/{alias}/mcp` | Hosted users (OAuth login) | 22-100 |
|
|
304
321
|
| `/project/{alias}/mcp` | Single-site workflow (recommended) | 22-100 |
|
|
305
322
|
| `/{plugin}/mcp` | Multi-site management | 23-101 |
|
|
306
323
|
| `/mcp` | Admin & discovery only | 596 |
|
|
@@ -321,7 +338,7 @@ Some MCP Hub tools require companion WordPress plugins:
|
|
|
321
338
|
|
|
322
339
|
| Tools | Requirement |
|
|
323
340
|
|-------|-------------|
|
|
324
|
-
| SEO tools (`wordpress_get_post_seo`, etc.) | [SEO
|
|
341
|
+
| SEO tools (`wordpress_get_post_seo`, etc.) | [Airano MCP SEO Bridge](https://wordpress.org/plugins/airano-mcp-seo-bridge/) ([GitHub](wordpress-plugin/airano-mcp-seo-bridge/)) + Rank Math or Yoast SEO |
|
|
325
342
|
| WP-CLI tools (15 tools: `wp_cache_*`, `wp_db_*`, etc.) | Docker socket + `CONTAINER` env var |
|
|
326
343
|
| WordPress Advanced database/system tools | Docker socket + `CONTAINER` env var |
|
|
327
344
|
| OpenPanel analytics integration | [OpenPanel Self-Hosted](wordpress-plugin/openpanel-self-hosted/) ([Download ZIP](wordpress-plugin/openpanel-self-hosted.zip)) |
|
|
@@ -361,7 +378,7 @@ Without Docker socket, WP-CLI tools return "not available" but all REST API tool
|
|
|
361
378
|
# Install with dev dependencies
|
|
362
379
|
pip install -e ".[dev]"
|
|
363
380
|
|
|
364
|
-
# Run tests (
|
|
381
|
+
# Run tests (481 tests)
|
|
365
382
|
pytest
|
|
366
383
|
|
|
367
384
|
# Format and lint
|
|
@@ -45,12 +45,10 @@ from core.rate_limiter import RateLimitConfig, RateLimiter, get_rate_limiter
|
|
|
45
45
|
from core.site_manager import SiteConfig, SiteManager, get_site_manager
|
|
46
46
|
|
|
47
47
|
# Legacy (kept for backward compatibility, will be removed in v2.0)
|
|
48
|
-
from core.site_registry import SiteInfo, SiteRegistry, get_site_registry
|
|
49
48
|
from core.tool_generator import ToolGenerator
|
|
50
49
|
|
|
51
50
|
# Tool Management (Option B architecture)
|
|
52
51
|
from core.tool_registry import ToolDefinition, ToolRegistry, get_tool_registry
|
|
53
|
-
from core.unified_tools import UnifiedToolGenerator
|
|
54
52
|
|
|
55
53
|
__all__ = [
|
|
56
54
|
# Authentication
|
|
@@ -64,11 +62,6 @@ __all__ = [
|
|
|
64
62
|
"SiteManager",
|
|
65
63
|
"SiteConfig",
|
|
66
64
|
"get_site_manager",
|
|
67
|
-
# Legacy (deprecated)
|
|
68
|
-
"SiteRegistry",
|
|
69
|
-
"SiteInfo",
|
|
70
|
-
"get_site_registry",
|
|
71
|
-
"UnifiedToolGenerator",
|
|
72
65
|
# Tool Management
|
|
73
66
|
"ToolRegistry",
|
|
74
67
|
"ToolDefinition",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Admin utility functions for role determination.
|
|
2
|
+
|
|
3
|
+
Centralizes admin email checking so it can be used in OAuth callback,
|
|
4
|
+
dashboard auth, and future admin/user panel unification (F.5+).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_admin_email(email: str | None) -> bool:
|
|
11
|
+
"""Check if an email is in the ADMIN_EMAILS env var list.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
email: Email address to check.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
True if email matches an admin email (case-insensitive).
|
|
18
|
+
"""
|
|
19
|
+
if not email:
|
|
20
|
+
return False
|
|
21
|
+
|
|
22
|
+
admin_emails_raw = os.environ.get("ADMIN_EMAILS", "")
|
|
23
|
+
if not admin_emails_raw.strip():
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
admin_emails = {e.strip().lower() for e in admin_emails_raw.split(",") if e.strip()}
|
|
27
|
+
return email.strip().lower() in admin_emails
|
|
@@ -14,6 +14,8 @@ from dataclasses import asdict, dataclass
|
|
|
14
14
|
from datetime import datetime, timedelta
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
|
|
17
|
+
import bcrypt
|
|
18
|
+
|
|
17
19
|
logger = logging.getLogger(__name__)
|
|
18
20
|
|
|
19
21
|
|
|
@@ -79,7 +81,7 @@ class APIKey:
|
|
|
79
81
|
"""
|
|
80
82
|
|
|
81
83
|
key_id: str
|
|
82
|
-
key_hash: str
|
|
84
|
+
key_hash: str # bcrypt hash (new keys) or SHA-256 hex (legacy)
|
|
83
85
|
project_id: str
|
|
84
86
|
scope: Scope
|
|
85
87
|
created_at: str
|
|
@@ -180,8 +182,16 @@ class APIKeyManager:
|
|
|
180
182
|
logger.error(f"Failed to save keys: {e}")
|
|
181
183
|
|
|
182
184
|
def _hash_key(self, api_key: str) -> str:
|
|
183
|
-
"""Hash API key for storage."""
|
|
184
|
-
return
|
|
185
|
+
"""Hash API key for storage using bcrypt."""
|
|
186
|
+
return bcrypt.hashpw(api_key.encode(), bcrypt.gensalt()).decode()
|
|
187
|
+
|
|
188
|
+
def _verify_key(self, api_key: str, key_hash: str) -> bool:
|
|
189
|
+
"""Verify API key against stored hash (supports bcrypt and legacy SHA-256)."""
|
|
190
|
+
if key_hash.startswith("$2"):
|
|
191
|
+
# bcrypt hash
|
|
192
|
+
return bcrypt.checkpw(api_key.encode(), key_hash.encode())
|
|
193
|
+
# Legacy SHA-256 fallback
|
|
194
|
+
return hashlib.sha256(api_key.encode()).hexdigest() == key_hash
|
|
185
195
|
|
|
186
196
|
def create_key(
|
|
187
197
|
self,
|
|
@@ -269,11 +279,9 @@ class APIKeyManager:
|
|
|
269
279
|
Returns:
|
|
270
280
|
Optional[str]: key_id if valid, None otherwise
|
|
271
281
|
"""
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
# Find key by hash
|
|
282
|
+
# Find key by verifying against stored hash (bcrypt or legacy SHA-256)
|
|
275
283
|
for key_id, key in self.keys.items():
|
|
276
|
-
if key.key_hash
|
|
284
|
+
if not self._verify_key(api_key, key.key_hash):
|
|
277
285
|
continue
|
|
278
286
|
|
|
279
287
|
# Check if valid (not revoked, not expired)
|
|
@@ -341,10 +349,8 @@ class APIKeyManager:
|
|
|
341
349
|
Returns:
|
|
342
350
|
Optional[APIKey]: The APIKey object if found, None otherwise
|
|
343
351
|
"""
|
|
344
|
-
key_hash = self._hash_key(api_key)
|
|
345
|
-
|
|
346
352
|
for key_id, key in self.keys.items():
|
|
347
|
-
if key.key_hash
|
|
353
|
+
if self._verify_key(api_key, key.key_hash):
|
|
348
354
|
logger.debug(f"Found API key {key_id} by token")
|
|
349
355
|
return key
|
|
350
356
|
|
|
@@ -51,10 +51,13 @@ class AuthManager:
|
|
|
51
51
|
Returns:
|
|
52
52
|
bool: True if valid
|
|
53
53
|
"""
|
|
54
|
-
|
|
54
|
+
if not self.master_api_key:
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
is_valid = secrets.compare_digest(api_key.strip(), self.master_api_key.strip())
|
|
55
58
|
|
|
56
59
|
if not is_valid:
|
|
57
|
-
logger.
|
|
60
|
+
logger.debug("Master key validation failed for provided token")
|
|
58
61
|
|
|
59
62
|
return is_valid
|
|
60
63
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""MCP client configuration snippet generation (Track E.3).
|
|
2
|
+
|
|
3
|
+
Generates copy-paste configuration snippets for connecting AI clients
|
|
4
|
+
(Claude Desktop, Claude Code, Cursor, VS Code, ChatGPT) to per-user
|
|
5
|
+
MCP endpoints.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from core.config_snippets import generate_config, get_supported_clients
|
|
9
|
+
|
|
10
|
+
snippet = generate_config(
|
|
11
|
+
base_url="https://mcp.example.com",
|
|
12
|
+
user_id="abc123",
|
|
13
|
+
alias="myblog",
|
|
14
|
+
api_key="mhu_...",
|
|
15
|
+
client_type="claude_desktop",
|
|
16
|
+
)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
|
|
21
|
+
# Supported MCP client types
|
|
22
|
+
SUPPORTED_CLIENTS = [
|
|
23
|
+
{
|
|
24
|
+
"id": "claude_connectors",
|
|
25
|
+
"label": "Claude.ai Connectors",
|
|
26
|
+
"description": "claude.ai/customize/connectors",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "claude_desktop",
|
|
30
|
+
"label": "Claude Desktop",
|
|
31
|
+
"description": "Anthropic's desktop app for Claude",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "claude_code",
|
|
35
|
+
"label": "Claude Code",
|
|
36
|
+
"description": "Anthropic's CLI for Claude",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "cursor",
|
|
40
|
+
"label": "Cursor",
|
|
41
|
+
"description": "AI-first code editor",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "vscode",
|
|
45
|
+
"label": "VS Code",
|
|
46
|
+
"description": "Visual Studio Code with MCP extension",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "chatgpt",
|
|
50
|
+
"label": "ChatGPT",
|
|
51
|
+
"description": "OpenAI ChatGPT (URL-based)",
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
# Clients that only need a URL (no JSON config snippet, no transport Note)
|
|
56
|
+
WEB_CLIENTS = {"claude_connectors", "chatgpt"}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_supported_clients() -> list[dict[str, str]]:
|
|
60
|
+
"""Return the list of supported MCP client types."""
|
|
61
|
+
return SUPPORTED_CLIENTS
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def generate_config(
|
|
65
|
+
base_url: str,
|
|
66
|
+
user_id: str,
|
|
67
|
+
alias: str,
|
|
68
|
+
api_key: str,
|
|
69
|
+
client_type: str,
|
|
70
|
+
) -> str:
|
|
71
|
+
"""Generate a configuration snippet for the given MCP client.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
base_url: Public URL of the MCP Hub instance (no trailing slash).
|
|
75
|
+
user_id: User UUID.
|
|
76
|
+
alias: Site alias.
|
|
77
|
+
api_key: User API key (``mhu_...``).
|
|
78
|
+
client_type: One of the supported client type IDs.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
JSON configuration string ready for copy-paste.
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
ValueError: If client_type is not supported.
|
|
85
|
+
"""
|
|
86
|
+
base_url = base_url.rstrip("/")
|
|
87
|
+
endpoint_url = f"{base_url}/u/{user_id}/{alias}/mcp"
|
|
88
|
+
server_name = f"mcphub-{alias}"
|
|
89
|
+
|
|
90
|
+
# Claude Desktop uses streamableHttp; Claude Code, VS Code, Cursor use http
|
|
91
|
+
transport_type = "streamableHttp" if client_type == "claude_desktop" else "http"
|
|
92
|
+
|
|
93
|
+
if client_type in ("claude_desktop", "claude_code"):
|
|
94
|
+
config = {
|
|
95
|
+
"mcpServers": {
|
|
96
|
+
server_name: {
|
|
97
|
+
"type": transport_type,
|
|
98
|
+
"url": endpoint_url,
|
|
99
|
+
"headers": {
|
|
100
|
+
"Authorization": f"Bearer {api_key}",
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return json.dumps(config, indent=2)
|
|
106
|
+
|
|
107
|
+
elif client_type in ("cursor", "vscode"):
|
|
108
|
+
config = {
|
|
109
|
+
"mcp": {
|
|
110
|
+
"servers": {
|
|
111
|
+
server_name: {
|
|
112
|
+
"type": transport_type,
|
|
113
|
+
"url": endpoint_url,
|
|
114
|
+
"headers": {
|
|
115
|
+
"Authorization": f"Bearer {api_key}",
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return json.dumps(config, indent=2)
|
|
122
|
+
|
|
123
|
+
elif client_type in ("claude_connectors", "chatgpt"):
|
|
124
|
+
return endpoint_url
|
|
125
|
+
|
|
126
|
+
else:
|
|
127
|
+
valid = [c["id"] for c in SUPPORTED_CLIENTS]
|
|
128
|
+
raise ValueError(f"Unsupported client type '{client_type}'. Valid: {valid}")
|
|
@@ -89,19 +89,30 @@ class DashboardAuth:
|
|
|
89
89
|
if not api_key:
|
|
90
90
|
return False, "", None
|
|
91
91
|
|
|
92
|
+
api_key_clean = api_key.strip()
|
|
93
|
+
# Check if master key dashboard login is disabled
|
|
94
|
+
master_login_disabled = (
|
|
95
|
+
os.environ.get("DISABLE_MASTER_KEY_LOGIN", "false").lower() == "true"
|
|
96
|
+
)
|
|
97
|
+
|
|
92
98
|
# Check master API key (from env var)
|
|
93
|
-
if
|
|
99
|
+
if (
|
|
100
|
+
not master_login_disabled
|
|
101
|
+
and self.master_api_key
|
|
102
|
+
and secrets.compare_digest(api_key_clean, self.master_api_key.strip())
|
|
103
|
+
):
|
|
94
104
|
return True, "master", None
|
|
95
105
|
|
|
96
106
|
# Check AuthManager's master key (covers auto-generated temp keys)
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
if not master_login_disabled:
|
|
108
|
+
try:
|
|
109
|
+
from core.auth import get_auth_manager
|
|
99
110
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
111
|
+
auth_mgr = get_auth_manager()
|
|
112
|
+
if auth_mgr.validate_master_key(api_key):
|
|
113
|
+
return True, "master", None
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.debug(f"AuthManager check skipped: {e}")
|
|
105
116
|
|
|
106
117
|
# Check project API keys with admin scope
|
|
107
118
|
try:
|
|
@@ -202,6 +213,9 @@ class DashboardAuth:
|
|
|
202
213
|
user_type=payload["type"],
|
|
203
214
|
key_id=payload.get("kid"),
|
|
204
215
|
)
|
|
216
|
+
except KeyError as e:
|
|
217
|
+
logger.debug(f"Invalid dashboard session payload (missing key): {e}")
|
|
218
|
+
return None
|
|
205
219
|
except jwt.ExpiredSignatureError:
|
|
206
220
|
logger.debug("Dashboard session expired")
|
|
207
221
|
return None
|
|
@@ -224,6 +238,27 @@ class DashboardAuth:
|
|
|
224
238
|
return None
|
|
225
239
|
return self.validate_session(token)
|
|
226
240
|
|
|
241
|
+
def get_user_session_from_request(self, request: Request) -> dict | None:
|
|
242
|
+
"""Extract and validate an OAuth user session from request.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
request: Starlette request object.
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
User session dict (user_id, email, name, role, type)
|
|
249
|
+
or None.
|
|
250
|
+
"""
|
|
251
|
+
token = request.cookies.get(self.COOKIE_NAME)
|
|
252
|
+
if not token:
|
|
253
|
+
return None
|
|
254
|
+
try:
|
|
255
|
+
from core.user_auth import get_user_auth
|
|
256
|
+
|
|
257
|
+
user_auth = get_user_auth()
|
|
258
|
+
return user_auth.validate_user_session(token)
|
|
259
|
+
except (RuntimeError, Exception):
|
|
260
|
+
return None
|
|
261
|
+
|
|
227
262
|
def set_session_cookie(self, response: Response, token: str) -> Response:
|
|
228
263
|
"""
|
|
229
264
|
Set session cookie on response.
|
|
@@ -273,13 +308,15 @@ class DashboardAuth:
|
|
|
273
308
|
RedirectResponse to login page if not authenticated, None if OK.
|
|
274
309
|
"""
|
|
275
310
|
session = self.get_session_from_request(request)
|
|
276
|
-
|
|
311
|
+
user_session = self.get_user_session_from_request(request)
|
|
312
|
+
|
|
313
|
+
if not session and not user_session:
|
|
277
314
|
# Store original URL for redirect after login
|
|
278
315
|
next_url = str(request.url.path)
|
|
279
316
|
if request.url.query:
|
|
280
317
|
next_url += f"?{request.url.query}"
|
|
281
318
|
return RedirectResponse(
|
|
282
|
-
url=f"/
|
|
319
|
+
url=f"/auth/login?next={next_url}",
|
|
283
320
|
status_code=303,
|
|
284
321
|
)
|
|
285
322
|
return None
|
|
@@ -291,3 +328,58 @@ def get_dashboard_auth() -> DashboardAuth:
|
|
|
291
328
|
if _dashboard_auth is None:
|
|
292
329
|
_dashboard_auth = DashboardAuth()
|
|
293
330
|
return _dashboard_auth
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
# ── Role-checking helpers ──────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def is_admin_session(session) -> bool:
|
|
337
|
+
"""Check if session is admin (master key or API key with admin scope).
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
session: DashboardSession or OAuth user dict.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
True if admin session.
|
|
344
|
+
"""
|
|
345
|
+
if isinstance(session, DashboardSession):
|
|
346
|
+
return session.user_type in ("master", "api_key")
|
|
347
|
+
if isinstance(session, dict):
|
|
348
|
+
return session.get("type") == "master" or session.get("role") == "admin"
|
|
349
|
+
return False
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def get_session_display_info(session) -> dict:
|
|
353
|
+
"""Get display info for header/UI.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
session: DashboardSession or OAuth user dict.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Dict with name, type, email, avatar keys.
|
|
360
|
+
"""
|
|
361
|
+
if isinstance(session, DashboardSession):
|
|
362
|
+
return {"name": "Admin", "type": "admin", "email": None, "avatar": None}
|
|
363
|
+
if isinstance(session, dict):
|
|
364
|
+
session_type = "admin" if session.get("role") == "admin" else "user"
|
|
365
|
+
return {
|
|
366
|
+
"name": session.get("name") or session.get("email", "User"),
|
|
367
|
+
"type": session_type,
|
|
368
|
+
"email": session.get("email"),
|
|
369
|
+
"avatar": None,
|
|
370
|
+
}
|
|
371
|
+
return {"name": "Unknown", "type": "unknown", "email": None, "avatar": None}
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def get_session_user_id(session) -> str | None:
|
|
375
|
+
"""Get user_id for OAuth sessions, None for admin.
|
|
376
|
+
|
|
377
|
+
Args:
|
|
378
|
+
session: DashboardSession or OAuth user dict.
|
|
379
|
+
|
|
380
|
+
Returns:
|
|
381
|
+
User UUID string or None.
|
|
382
|
+
"""
|
|
383
|
+
if isinstance(session, dict):
|
|
384
|
+
return session.get("user_id")
|
|
385
|
+
return None
|