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

@@ -181,24 +181,41 @@ class TestScopesToCategories:
class TestScopeFilter:
"""F.7c: Coolify uses legacy category filter (plugin_type='coolify'),
other plugins use universal required_scope filter."""
def test_read_scope_drops_lifecycle_crud_system(self, access_mgr):
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["read"])}
# Coolify tools filtered by category
coolify_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "coolify"]
out = {
t.name
for t in access_mgr.apply_scope_filter(coolify_tools, ["read"], plugin_type="coolify")
}
assert "coolify_list_applications" in out
assert "wordpress_list_posts" in out # legacy default category
assert "coolify_start_application" not in out
assert "coolify_create_application_public" not in out
assert "coolify_delete_server" not in out
assert "coolify_get_application_logs" not in out
def test_read_sensitive_includes_logs_and_backups(self, access_mgr):
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["read:sensitive"])}
coolify_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "coolify"]
out = {
t.name
for t in access_mgr.apply_scope_filter(
coolify_tools, ["read:sensitive"], plugin_type="coolify"
)
}
assert "coolify_get_application_logs" in out
assert "coolify_get_database_backups" in out
assert "coolify_start_application" not in out
assert "coolify_create_application_public" not in out
def test_deploy_scope_includes_lifecycle_only(self, access_mgr):
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["deploy"])}
coolify_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "coolify"]
out = {
t.name
for t in access_mgr.apply_scope_filter(coolify_tools, ["deploy"], plugin_type="coolify")
}
assert "coolify_start_application" in out
assert "coolify_stop_application" in out
assert "coolify_list_applications" in out
@@ -206,7 +223,11 @@ class TestScopeFilter:
assert "coolify_delete_server" not in out
def test_write_scope_excludes_system(self, access_mgr):
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["write"])}
coolify_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "coolify"]
out = {
t.name
for t in access_mgr.apply_scope_filter(coolify_tools, ["write"], plugin_type="coolify")
}
assert "coolify_create_application_public" in out
assert "coolify_start_application" in out
assert "coolify_create_application_env" in out
@@ -214,9 +235,22 @@ class TestScopeFilter:
assert "coolify_get_application_logs" not in out
def test_admin_keeps_everything(self, access_mgr):
out = {t.name for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["admin"])}
out = {
t.name
for t in access_mgr.apply_scope_filter(_SAMPLE_TOOLS, ["admin"], plugin_type="coolify")
}
assert out == {t.name for t in _SAMPLE_TOOLS}
def test_universal_read_filters_by_required_scope(self, access_mgr):
"""Non-Coolify plugins use universal required_scope filter."""
wp_tools = [t for t in _SAMPLE_TOOLS if t.plugin_type == "wordpress"]
out = {
t.name
for t in access_mgr.apply_scope_filter(wp_tools, ["read"], plugin_type="wordpress")
}
# All wordpress sample tools have required_scope="read"
assert "wordpress_list_posts" in out
# ---------------------------------------------------------------------------
# Per-site toggles