diff --git a/docs/appwrite-mcp-flow.svg b/docs/appwrite-mcp-flow.svg index 2f2e26f..bf83bf2 100644 --- a/docs/appwrite-mcp-flow.svg +++ b/docs/appwrite-mcp-flow.svg @@ -115,7 +115,7 @@ search_docs - hosted catalog · 40 services · 1007 tools + hosted catalog · 41 services · 1016 tools writes need confirm_write: true diff --git a/docs/tool-surface.md b/docs/tool-surface.md index a9e2a27..ec930e7 100644 --- a/docs/tool-surface.md +++ b/docs/tool-surface.md @@ -20,7 +20,7 @@ flowchart LR subgraph CAT[Internal catalog — authentication-aware] direction LR - K[OAuth: 40 services / 1007 tools
API key: 26 services / 663 tools] + K[OAuth: 41 services / 1016 tools
API key: 26 services / 663 tools] end CT -->|large output| R[(MCP resource
preview + URI)] @@ -47,8 +47,8 @@ flowchart LR `context=organization`, or `context=project`. Hosted calls enforce the required top-level `organization_id` or `project_id` before making a request. - **Access** is still gated per-route by the scopes granted to the OAuth token. -- **Hosted OAuth** registers all 40 services and 1007 methods shipped by - `appwrite-console` 0.7.0. This adds console control-plane services including +- **Hosted OAuth** registers all 41 services and 1016 methods shipped by + `appwrite-console` 0.8.1. This adds console control-plane services including projects, organizations, domains, migrations, dedicated databases, usage, VCS, vectors, WAF, notifications, and regions. - **API-key stdio** deliberately registers only the 663 project-key-compatible diff --git a/pyproject.toml b/pyproject.toml index f122dda..74e1f4a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "anyio>=4.0.0", - "appwrite-console>=0.7.1,<0.8", + "appwrite-console>=0.8.1,<0.9", "docstring-parser>=0.16", "mcp[cli]>=2,<3", "python-dotenv>=1.0.1", diff --git a/src/mcp_server_appwrite/catalog_policy.py b/src/mcp_server_appwrite/catalog_policy.py index bd4d222..2be5675 100644 --- a/src/mcp_server_appwrite/catalog_policy.py +++ b/src/mcp_server_appwrite/catalog_policy.py @@ -83,6 +83,7 @@ } ), "apps": frozenset({"delete_installation"}), + "avatars": frozenset({"delete_photo", "update_photo"}), "documents_db": frozenset( { "create_documents", diff --git a/tests/unit/test_server.py b/tests/unit/test_server.py index dceb504..f52d465 100644 --- a/tests/unit/test_server.py +++ b/tests/unit/test_server.py @@ -923,8 +923,8 @@ def test_register_services_returns_fresh_manager(self): {service.service_name for service in manager_a.services}, set(SERVICE_CLASSES), ) - self.assertEqual(len(manager_a.services), 40) - self.assertEqual(len(manager_a.get_all_tools()), 1007) + self.assertEqual(len(manager_a.services), 41) + self.assertEqual(len(manager_a.get_all_tools()), 1016) def test_console_sdk_0_7_surface(self): manager = register_services(object(), profile=OAUTH_PROFILE) @@ -935,6 +935,8 @@ def test_console_sdk_0_7_surface(self): self.assertIn("growth_create_conversation", tools) self.assertIn("organization_create_project_key", tools) self.assertIn("tables_db_create_cutover", tools) + self.assertIn("analytics_list_metrics", tools) + self.assertIn("avatars_update_photo", tools) for removed_tool in ( "project_get_usage", "projects_list_dev_keys", @@ -984,6 +986,35 @@ def test_console_sdk_0_7_surface(self): ] self.assertIn("bun-1.4", build_runtime["enum"]) + def test_console_sdk_accepts_null_billing_fields(self): + # Cloud returns null for these fields; earlier SDKs rejected the + # responses (Sentry MCP-1G/1S/1F/1P/1N/1W). + from appwrite_console.models.dedicated_database_restoration import ( + DedicatedDatabaseRestoration, + ) + from appwrite_console.models.invoice import Invoice + from appwrite_console.models.organization import Organization + from appwrite_console.models.payment_method import PaymentMethod + from pydantic import TypeAdapter + + cases = { + Organization: ("paymentMethodId",), + PaymentMethod: ("mandateId", "state", "lastError"), + Invoice: ("clientSecret", "lastError"), + DedicatedDatabaseRestoration: ("targetTime",), + } + for model, aliases in cases.items(): + fields = { + field.alias or name: field for name, field in model.model_fields.items() + } + for alias in aliases: + with self.subTest(model=model.__name__, field=alias): + field = fields[alias] + self.assertFalse(field.is_required()) + self.assertIsNone( + TypeAdapter(field.annotation).validate_python(None) + ) + def test_advisor_tools_are_project_scoped(self): manager = register_services(object(), profile=OAUTH_PROFILE) advisor_tools = { @@ -1013,6 +1044,9 @@ def test_api_key_profile_only_advertises_server_capabilities(self): self.assertNotIn("organizations", service_names) self.assertNotIn("documents_db_list_operations", tool_names) self.assertNotIn("tables_db_create_cutover", tool_names) + self.assertNotIn("avatars_update_photo", tool_names) + self.assertNotIn("avatars_delete_photo", tool_names) + self.assertNotIn("analytics", service_names) self.assertNotIn("growth", service_names) for method in ( "create_ephemeral_project_key", diff --git a/uv.lock b/uv.lock index 711d866..47498e6 100644 --- a/uv.lock +++ b/uv.lock @@ -39,15 +39,15 @@ wheels = [ [[package]] name = "appwrite-console" -version = "0.7.1" +version = "0.8.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/0a/d1f4506b3dc54ecb55382aee503d249ebe58e9cf99b44167d5f640b3ecfd/appwrite_console-0.7.1.tar.gz", hash = "sha256:b62706c1497a47070797ecfb70fb3f8910706db9c54ad770a1eda79a962ee287", size = 320527, upload-time = "2026-10-01T19:17:01.769Z" } +sdist = { url = "https://files.pythonhosted.org/packages/02/b2/76bbf397aca3e8caaf52bd254dac54b85214fffd6d1a1d4dd40d353247dd/appwrite_console-0.8.1.tar.gz", hash = "sha256:1fbb764bb0c9e802f079019e375fe96f5a28e51553a4bce94246e7f18bf8cce5", size = 326599, upload-time = "2026-10-06T10:55:59.571Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/99/23/ae465cbc2f7a3e22a42793e4a77a52c0f2b26543fdc1eb01f81411b92fc1/appwrite_console-0.7.1-py3-none-any.whl", hash = "sha256:5b8b53a25dbc8b97080f006d5f71436738b6f312f519b8bc3c030a445e63423c", size = 554926, upload-time = "2026-10-01T19:17:00.369Z" }, + { url = "https://files.pythonhosted.org/packages/65/c4/8001bfadd6f5af3fced6421a4b8651fe7b496cafd1fbe01777830d455c36/appwrite_console-0.8.1-py3-none-any.whl", hash = "sha256:c71ad412f932dffe0d244b78ed20368999be3ac6961719e8bd30069b56857768", size = 565106, upload-time = "2026-10-06T10:55:57.918Z" }, ] [[package]] @@ -694,7 +694,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "anyio", specifier = ">=4.0.0" }, - { name = "appwrite-console", specifier = ">=0.7.1,<0.8" }, + { name = "appwrite-console", specifier = ">=0.8.1,<0.9" }, { name = "argon2-cffi", marker = "extra == 'integration'", specifier = ">=23.1.0" }, { name = "bcrypt", marker = "extra == 'integration'", specifier = ">=4.1.2" }, { name = "docstring-parser", specifier = ">=0.16" },