feat(F.7+F.17): v3.11.0 — Coolify plugin (67 tools) + tool access overhaul
Some checks failed
Release / Test before release (push) Has been cancelled
Release / Publish to PyPI (push) Has been cancelled
Release / Publish to Docker Hub (push) Has been cancelled
Release / Create GitHub Release (push) Has been cancelled

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:
2026-04-14 21:05:07 +02:00
parent d8a0805412
commit 788439e377
31 changed files with 1911 additions and 254 deletions

View File

@@ -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)