feat(F.7+F.17): v3.11.0 — Coolify plugin (67 tools) + tool access overhaul
Catch-up sync spanning v3.7.0 → v3.11.0 of the internal repo. Platform - Total tools: 565 → 633 (+68) across 10 plugins (Coolify added) - Tests: 481 → 828 passing New plugin: Coolify (67 tools, Track F.17) - Applications (17): CRUD, lifecycle, logs, env vars - Deployments (5): list/get/cancel/deploy, app history - Servers (8): CRUD, resources, domains, validation - Projects (8), Databases (16, 6 DB types + backups), Services (13) Tool access system (Track F.7 → F.7d) - Scope → category mapping with per-tool `category` + `sensitivity` - Schema v7: `site_tool_toggles(site_id)` + `sites.tool_scope` column - Schema v8: per-site API keys (`api_keys.site_id`) - Plugin-specific access-level presets (WP / WC / Gitea / OpenPanel / Coolify 5-tier) - Credential-requirement notice tailored per plugin and tier - Admin Tools count card on service page - Dropped redundant `write` tier on WP / WP Advanced / WooCommerce (admin-scope tool count = 0 → identical to admin tier) Dashboard - Unified site manage page (Connection / Tool Access / Connect) - /dashboard/keys unified (was /api-keys and /connect) - CSRF interceptor via meta-tag; removed conflicting cookie reader - Tailwind: pre-built CSS (scripts/build-css.sh) replaces CDN Docs - README / DOCKER_README / CLAUDE updated to 633 tools / 10 plugins - CHANGELOG entries for v3.7.0 → v3.11.0 - FastMCP compatibility note updated to 3.x (post-v3.5 upgrade) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -240,6 +240,28 @@ async def user_mcp_handler(request: Request) -> Response:
|
||||
_jsonrpc_error(None, -32600, "API key does not match user"),
|
||||
status_code=403,
|
||||
)
|
||||
|
||||
# Check site-scoped key: if key is scoped to a site, it can only
|
||||
# be used for that specific site's alias.
|
||||
key_site_id = key_info.get("site_id")
|
||||
if key_site_id:
|
||||
try:
|
||||
from core.database import get_database
|
||||
|
||||
db = get_database()
|
||||
site = await db.get_site(key_site_id, user_id)
|
||||
if site is None or site.get("alias") != alias:
|
||||
return JSONResponse(
|
||||
_jsonrpc_error(
|
||||
None,
|
||||
-32600,
|
||||
"API key is scoped to a different site",
|
||||
),
|
||||
status_code=403,
|
||||
)
|
||||
except RuntimeError:
|
||||
pass # DB unavailable — allow through
|
||||
|
||||
key_scopes = key_info.get("scopes", "read").split()
|
||||
else:
|
||||
# Try OAuth JWT token (issued after consent flow via GitHub/Google login)
|
||||
|
||||
Reference in New Issue
Block a user