mcp-server-appwrite 2.2.0__tar.gz → 2.2.2__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.
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/PKG-INFO +1 -1
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/apps.md +18 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/protocol.py +32 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/views.py +26 -1
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/assets/view.html +3 -1
- mcp_server_appwrite-2.2.2/src/mcp_server_appwrite/data/docs_index.npz +4 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/server.py +1 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/telemetry.py +38 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/support.py +2 -1
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/test_apps.py +68 -4
- mcp_server_appwrite-2.2.0/src/mcp_server_appwrite/data/docs_index.npz +0 -4
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.dockerignore +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.env.example +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.github/workflows/ci.yml +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.github/workflows/production.yml +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.github/workflows/publish.yml +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.github/workflows/staging.yml +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.github/workflows/update-docs-index.yml +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.gitignore +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.python-version +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/AGENTS.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/CLAUDE.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/Dockerfile +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/LICENSE +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/README.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/compose.yaml +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/appwrite-mcp-flow.png +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/appwrite-mcp-flow.svg +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/authentication.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/development.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/documentation-search.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/events.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/flags.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/images/claude-desktop-local-mcp-servers.png +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/images/codex-mcp.png +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/images/cursor-mcp.png +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/self-hosted.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/docs/tool-surface.md +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/glama.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/pyproject.toml +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/scripts/build_docs_index.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/scripts/docs_release_notes.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/scripts/render_server_json.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/server.template.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/__init__.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/__main__.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/__init__.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/executions.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/files.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/resource.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/usage.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/users.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/assets/__init__.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/assets/favicon.svg +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/assets/instructions.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/auth.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/catalog_policy.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/constants.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/context.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/docs.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/docs_index.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/docs_search.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/docs_source.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/error_classification.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/error_monitoring.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/__init__.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/bus.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/catalog.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/delivery.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/egress.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/envelope.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/errors.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/ingress.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/projection.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/protocol.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/streams.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/subscriptions.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/webhooks.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/flags.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/http_app.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/instructions.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/legacy.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/operator.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/service.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/tool_manager.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/deployment_function_duplicate.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/deployment_function_failed.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/deployment_function_ready.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/deployment_site_ready.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/execution_async_failed.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/execution_async_waiting.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/execution_sync_failed.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/file_created.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/function_activation.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/row_created.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/user_created.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/test_events_ingress.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/test_events_protocol.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/test_events_stream.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/test_events_subscribe.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/test_stdio.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/test_tools.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/support.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_avatars.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_console_sdk_oauth.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_docs_index.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_docs_source.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_functions.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_locale.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_messaging.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_oauth_discovery.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_operator.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_sites.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_storage.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_tables_db.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_teams.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_users.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/test_all.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/fixtures/jwe-v1.json +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_auth.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_context.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_docs.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_docs_search.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_error_classification.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_error_monitoring.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_events_delivery.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_events_egress.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_events_envelope.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_events_protocol.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_http_app.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_instructions.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_operator.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_release_metadata.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_server.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_service.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_telemetry.py +0 -0
- {mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mcp-server-appwrite
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: Build and deploy application backends. Add authentication, PostgreSQL/MySQL databases, vector databases, storage, serverless functions, realtime, messaging, hosting, domains, CDN and firewall.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -48,6 +48,24 @@ text summary. 2026-07-28 clients send `clientCapabilities` with every request,
|
|
|
48
48
|
and stdio has the handshake, so both are gated exactly. `tools/call` is not
|
|
49
49
|
gated, because the view's own calls reach the server through the host.
|
|
50
50
|
|
|
51
|
+
## Telemetry
|
|
52
|
+
|
|
53
|
+
On the hosted transport, two counters show which views people use:
|
|
54
|
+
|
|
55
|
+
- `mcp.apps.views` counts `appwrite_show` calls by `view` (`invalid` for a name
|
|
56
|
+
that is not one), `status` (`success` or `error`), `client_id`, and `origin`:
|
|
57
|
+
`model` when the model opens a view, `view` when the view refreshes, pages,
|
|
58
|
+
polls a build or opens a row on its own. Views opened per view type is
|
|
59
|
+
`origin="model"`; how much people then use each view is `origin="view"`.
|
|
60
|
+
- `mcp.apps.writes` counts `appwrite_view_write` calls by `action` (the
|
|
61
|
+
allowlisted write, or `invalid`), `status` and `client_id`.
|
|
62
|
+
|
|
63
|
+
The view marks its own calls with `"io.appwrite/origin": "view"` in the
|
|
64
|
+
request's `_meta`. Hosts forward a view's `tools/call` params as sent; a host
|
|
65
|
+
that drops `_meta` makes those calls count as `origin="model"`. Both counters
|
|
66
|
+
feed the MCP Apps row of the MCP dashboard (appwrite-labs/monitoring
|
|
67
|
+
`dashboards/Cloud/mcp.json`).
|
|
68
|
+
|
|
51
69
|
## Security
|
|
52
70
|
|
|
53
71
|
- The resource declares no CSP domains, so hosts apply the spec's restrictive
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/protocol.py
RENAMED
|
@@ -19,6 +19,8 @@ Apps are offered behind the ``apps`` flag (``MCP_APPS``); see :func:`enabled`.
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
21
|
from collections.abc import Mapping
|
|
22
|
+
from contextvars import ContextVar
|
|
23
|
+
from enum import StrEnum
|
|
22
24
|
from typing import Any
|
|
23
25
|
|
|
24
26
|
from mcp.server import Server, ServerRequestContext
|
|
@@ -34,6 +36,22 @@ MIME_TYPE = "text/html;profile=mcp-app"
|
|
|
34
36
|
|
|
35
37
|
SETTINGS: dict[str, Any] = {"mimeTypes": [MIME_TYPE]}
|
|
36
38
|
|
|
39
|
+
ORIGIN_META_KEY = "io.appwrite/origin"
|
|
40
|
+
"""The request ``_meta`` key the view sets to ``"view"`` on its own calls, so
|
|
41
|
+
telemetry can tell a view refreshing and paging itself from the model opening
|
|
42
|
+
one. Hosts forward a view's ``tools/call`` params as sent; a host that drops
|
|
43
|
+
``_meta`` makes those calls count as the model's."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Origin(StrEnum):
|
|
47
|
+
MODEL = "model"
|
|
48
|
+
VIEW = "view"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
_origin: ContextVar[Origin] = ContextVar(
|
|
52
|
+
"appwrite_mcp_apps_origin", default=Origin.MODEL
|
|
53
|
+
)
|
|
54
|
+
|
|
37
55
|
|
|
38
56
|
def enabled() -> bool:
|
|
39
57
|
"""Whether this server offers MCP Apps."""
|
|
@@ -56,6 +74,20 @@ def supported(context: ServerRequestContext[Any, Any]) -> bool:
|
|
|
56
74
|
return not isinstance(mime_types, list) or MIME_TYPE in mime_types
|
|
57
75
|
|
|
58
76
|
|
|
77
|
+
def bind_origin(context: ServerRequestContext[Any, Any]) -> None:
|
|
78
|
+
"""Remember who made this ``tools/call``: the view, when its ``_meta``
|
|
79
|
+
says so, else the model. Contextvars reach the worker thread that runs the
|
|
80
|
+
tool."""
|
|
81
|
+
meta = context.meta
|
|
82
|
+
from_view = isinstance(meta, Mapping) and meta.get(ORIGIN_META_KEY) == Origin.VIEW
|
|
83
|
+
_origin.set(Origin.VIEW if from_view else Origin.MODEL)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def origin() -> Origin:
|
|
87
|
+
"""Who made the ``tools/call`` being served."""
|
|
88
|
+
return _origin.get()
|
|
89
|
+
|
|
90
|
+
|
|
59
91
|
def register(server: Server[Any]) -> None:
|
|
60
92
|
"""Advertise the extension in ``server``'s capabilities."""
|
|
61
93
|
server.extensions[EXTENSION] = dict(SETTINGS)
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/views.py
RENAMED
|
@@ -29,8 +29,9 @@ from typing import Any
|
|
|
29
29
|
import mcp.types as types
|
|
30
30
|
from appwrite_console.query import Query
|
|
31
31
|
|
|
32
|
+
from .. import telemetry
|
|
32
33
|
from ..constants import SHOW_TOOL_NAME, VIEW_WRITE_TOOL_NAME
|
|
33
|
-
from . import executions, files, resource, usage, users
|
|
34
|
+
from . import executions, files, protocol, resource, usage, users
|
|
34
35
|
|
|
35
36
|
Run = Callable[[dict[str, Any], bool], list[Any]]
|
|
36
37
|
"""Runs a hidden catalog tool from raw call arguments (``tool_name``,
|
|
@@ -86,6 +87,18 @@ class Views:
|
|
|
86
87
|
return [(_show_tool(), self.show), (_write_tool(), self.write)]
|
|
87
88
|
|
|
88
89
|
def show(self, arguments: dict[str, Any]) -> types.CallToolResult:
|
|
90
|
+
view = arguments.get("view")
|
|
91
|
+
status = "error"
|
|
92
|
+
try:
|
|
93
|
+
result = self._show(arguments)
|
|
94
|
+
status = "success"
|
|
95
|
+
return result
|
|
96
|
+
finally:
|
|
97
|
+
telemetry.record_view(
|
|
98
|
+
str(view) if view in View else "invalid", protocol.origin(), status
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def _show(self, arguments: dict[str, Any]) -> types.CallToolResult:
|
|
89
102
|
view = arguments.get("view")
|
|
90
103
|
if view == View.TABLE:
|
|
91
104
|
data = self._table(arguments)
|
|
@@ -113,6 +126,18 @@ class Views:
|
|
|
113
126
|
)
|
|
114
127
|
|
|
115
128
|
def write(self, arguments: dict[str, Any]) -> types.CallToolResult:
|
|
129
|
+
tool_name = arguments.get("tool_name")
|
|
130
|
+
status = "error"
|
|
131
|
+
try:
|
|
132
|
+
result = self._write(arguments)
|
|
133
|
+
status = "success"
|
|
134
|
+
return result
|
|
135
|
+
finally:
|
|
136
|
+
telemetry.record_view_write(
|
|
137
|
+
tool_name if tool_name in WRITES else "invalid", status
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def _write(self, arguments: dict[str, Any]) -> types.CallToolResult:
|
|
116
141
|
tool_name = arguments.get("tool_name")
|
|
117
142
|
if tool_name not in WRITES:
|
|
118
143
|
raise ValueError(
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/assets/view.html
RENAMED
|
@@ -304,6 +304,8 @@ details.headers td { height: 32px; max-width: none; white-space: normal; overflo
|
|
|
304
304
|
const PROTOCOL_VERSION = "2026-01-26";
|
|
305
305
|
const SHOW_TOOL = "appwrite_show";
|
|
306
306
|
const WRITE_TOOL = "appwrite_view_write";
|
|
307
|
+
// Tells the server's telemetry this call is the view's, not the model's.
|
|
308
|
+
const ORIGIN = { "io.appwrite/origin": "view" };
|
|
307
309
|
const POLL_MS = 3000;
|
|
308
310
|
const PAGE_SIZE = 25;
|
|
309
311
|
const FILES_PAGE = 24;
|
|
@@ -352,7 +354,7 @@ details.headers td { height: 32px; max-width: none; white-space: normal; overflo
|
|
|
352
354
|
}
|
|
353
355
|
|
|
354
356
|
async function callTool(name, args) {
|
|
355
|
-
const result = await request("tools/call", { name, arguments: args });
|
|
357
|
+
const result = await request("tools/call", { name, arguments: args, _meta: ORIGIN });
|
|
356
358
|
if (result && result.isError) {
|
|
357
359
|
throw new Error(textOf(result) || "The Appwrite call failed.");
|
|
358
360
|
}
|
|
@@ -1379,6 +1379,7 @@ def build_mcp_server(
|
|
|
1379
1379
|
ctx: ServerRequestContext, params: types.CallToolRequestParams
|
|
1380
1380
|
) -> types.CallToolResult:
|
|
1381
1381
|
_emit_initialize(ctx)
|
|
1382
|
+
apps.bind_origin(ctx)
|
|
1382
1383
|
start = time.monotonic()
|
|
1383
1384
|
name = params.name
|
|
1384
1385
|
arguments = params.arguments or {}
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/telemetry.py
RENAMED
|
@@ -311,6 +311,20 @@ def _build_instruments(meter: Any, transport: str, version: str) -> None:
|
|
|
311
311
|
),
|
|
312
312
|
)
|
|
313
313
|
|
|
314
|
+
_instruments["apps_views"] = meter.create_counter(
|
|
315
|
+
"mcp.apps.views",
|
|
316
|
+
unit="{call}",
|
|
317
|
+
description=(
|
|
318
|
+
"MCP Apps appwrite_show calls by view, origin (the model opening a "
|
|
319
|
+
"view, or the view refreshing and paging itself) and status."
|
|
320
|
+
),
|
|
321
|
+
)
|
|
322
|
+
_instruments["apps_writes"] = meter.create_counter(
|
|
323
|
+
"mcp.apps.writes",
|
|
324
|
+
unit="{call}",
|
|
325
|
+
description="MCP Apps appwrite_view_write calls by action and status.",
|
|
326
|
+
)
|
|
327
|
+
|
|
314
328
|
_instruments["token_usage"] = meter.create_counter(
|
|
315
329
|
"mcp.token.usage",
|
|
316
330
|
unit="{token}",
|
|
@@ -711,6 +725,30 @@ def record_tool_call(
|
|
|
711
725
|
)
|
|
712
726
|
|
|
713
727
|
|
|
728
|
+
def record_view(view: str, origin: str, status: str) -> None:
|
|
729
|
+
"""One ``appwrite_show`` call. ``view`` is a view name, or ``invalid``."""
|
|
730
|
+
_safe_add(
|
|
731
|
+
"apps_views",
|
|
732
|
+
1,
|
|
733
|
+
{
|
|
734
|
+
"view": view,
|
|
735
|
+
"origin": origin,
|
|
736
|
+
"status": status,
|
|
737
|
+
"client_id": current_client_id(),
|
|
738
|
+
},
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def record_view_write(action: str, status: str) -> None:
|
|
743
|
+
"""One ``appwrite_view_write`` call. ``action`` is an allowlisted catalog
|
|
744
|
+
write, or ``invalid``."""
|
|
745
|
+
_safe_add(
|
|
746
|
+
"apps_writes",
|
|
747
|
+
1,
|
|
748
|
+
{"action": action, "status": status, "client_id": current_client_id()},
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
|
|
714
752
|
def record_hallucination(attempted_tool: Any) -> None:
|
|
715
753
|
_safe_add(
|
|
716
754
|
"tool_hallucination",
|
|
@@ -302,7 +302,7 @@ class Client:
|
|
|
302
302
|
self, method: str, params: Mapping[str, Any] | None = None, *, path: str = "/"
|
|
303
303
|
) -> tuple[int, dict[str, Any]]:
|
|
304
304
|
"""One 2026-07-28 request: protocol version and method in the headers,
|
|
305
|
-
client identity in ``params._meta``."""
|
|
305
|
+
client identity in ``params._meta`` next to any ``_meta`` in ``params``."""
|
|
306
306
|
body = {
|
|
307
307
|
"jsonrpc": "2.0",
|
|
308
308
|
"id": next(self._ids),
|
|
@@ -316,6 +316,7 @@ class Client:
|
|
|
316
316
|
"version": "1",
|
|
317
317
|
},
|
|
318
318
|
"io.modelcontextprotocol/clientCapabilities": self._capabilities,
|
|
319
|
+
**(params or {}).get("_meta", {}),
|
|
319
320
|
},
|
|
320
321
|
},
|
|
321
322
|
}
|
|
@@ -25,6 +25,7 @@ from support import (
|
|
|
25
25
|
Cloud,
|
|
26
26
|
Project,
|
|
27
27
|
Server,
|
|
28
|
+
collector,
|
|
28
29
|
column,
|
|
29
30
|
deployment,
|
|
30
31
|
execution,
|
|
@@ -42,6 +43,10 @@ EXTENSION = "io.modelcontextprotocol/ui"
|
|
|
42
43
|
MIME_TYPE = "text/html;profile=mcp-app"
|
|
43
44
|
VIEW_URI = "ui://appwrite/view"
|
|
44
45
|
UI = {"extensions": {EXTENSION: {"mimeTypes": [MIME_TYPE]}}}
|
|
46
|
+
FROM_VIEW = {"io.appwrite/origin": "view"}
|
|
47
|
+
"""The ``_meta`` the view sends on its own calls."""
|
|
48
|
+
VIEWS = "mcp.apps.views"
|
|
49
|
+
WRITES = "mcp.apps.writes"
|
|
45
50
|
ROWS = 30
|
|
46
51
|
PAGE = 25
|
|
47
52
|
LOG_LINES = 250
|
|
@@ -225,10 +230,16 @@ class AppsFlow(unittest.TestCase):
|
|
|
225
230
|
cls.host.__exit__(None, None, None)
|
|
226
231
|
cls.server.__exit__(None, None, None)
|
|
227
232
|
|
|
228
|
-
def call(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
233
|
+
def call(
|
|
234
|
+
self,
|
|
235
|
+
name: str,
|
|
236
|
+
arguments: dict[str, Any],
|
|
237
|
+
meta: dict[str, Any] | None = None,
|
|
238
|
+
) -> dict[str, Any]:
|
|
239
|
+
params: dict[str, Any] = {"name": name, "arguments": arguments}
|
|
240
|
+
if meta is not None:
|
|
241
|
+
params["_meta"] = meta
|
|
242
|
+
status, response = self.host.call("tools/call", params)
|
|
232
243
|
self.assertEqual(status, 200, response)
|
|
233
244
|
return response["result"]
|
|
234
245
|
|
|
@@ -290,6 +301,9 @@ class AppsFlow(unittest.TestCase):
|
|
|
290
301
|
# Appwrite data reaches the DOM as text only.
|
|
291
302
|
self.assertNotIn("innerHTML", html)
|
|
292
303
|
self.assertNotIn("insertAdjacentHTML", html)
|
|
304
|
+
# The view tags its own calls so telemetry can tell them apart.
|
|
305
|
+
self.assertIn('"io.appwrite/origin": "view"', html)
|
|
306
|
+
self.assertIn("_meta: ORIGIN", html)
|
|
293
307
|
digest = hashlib.sha256(html.encode("utf-8")).hexdigest()[:12]
|
|
294
308
|
self.assertEqual(uri, f"{VIEW_URI}/{digest}")
|
|
295
309
|
|
|
@@ -316,6 +330,8 @@ class AppsFlow(unittest.TestCase):
|
|
|
316
330
|
|
|
317
331
|
def test_table_view_pages_through_rows(self):
|
|
318
332
|
listed = cloud.calls("GET", rf"/v1/tablesdb/{DATABASE}/tables/{TABLE}/rows")
|
|
333
|
+
opened = collector().count(VIEWS, view="table", origin="model")
|
|
334
|
+
paged = collector().count(VIEWS, view="table", origin="view")
|
|
319
335
|
result = self.show_table()
|
|
320
336
|
data = result["structuredContent"]
|
|
321
337
|
self.assertEqual(data["view"], "table")
|
|
@@ -353,9 +369,37 @@ class AppsFlow(unittest.TestCase):
|
|
|
353
369
|
listed + 3,
|
|
354
370
|
)
|
|
355
371
|
|
|
372
|
+
# The model opened the view; the view pages itself under its own origin.
|
|
373
|
+
self.call(
|
|
374
|
+
"appwrite_show",
|
|
375
|
+
{
|
|
376
|
+
"view": "table",
|
|
377
|
+
"project_id": PROJECT,
|
|
378
|
+
"database_id": DATABASE,
|
|
379
|
+
"table_id": TABLE,
|
|
380
|
+
"cursor": "ord_002",
|
|
381
|
+
},
|
|
382
|
+
meta=FROM_VIEW,
|
|
383
|
+
)
|
|
384
|
+
self.assertEqual(
|
|
385
|
+
collector().count(VIEWS, view="table", origin="view", status="success"),
|
|
386
|
+
paged + 1,
|
|
387
|
+
)
|
|
388
|
+
self.assertGreater(
|
|
389
|
+
collector().count(VIEWS, view="table", origin="model", status="success"),
|
|
390
|
+
opened,
|
|
391
|
+
)
|
|
392
|
+
self.assertEqual(
|
|
393
|
+
collector().count(VIEWS, view="table", origin="view"), paged + 1
|
|
394
|
+
)
|
|
395
|
+
|
|
356
396
|
def test_view_writes_only_its_allowlist_and_only_when_confirmed(self):
|
|
357
397
|
row_path = rf"/v1/tablesdb/{DATABASE}/tables/{TABLE}/rows/ord_001"
|
|
358
398
|
writes = cloud.calls("PATCH", row_path)
|
|
399
|
+
action = "tables_db_update_row"
|
|
400
|
+
refused = collector().count(WRITES, action=action, status="error")
|
|
401
|
+
succeeded = collector().count(WRITES, action=action, status="success")
|
|
402
|
+
invalid = collector().count(WRITES, action="invalid")
|
|
359
403
|
update = {
|
|
360
404
|
"tool_name": "tables_db_update_row",
|
|
361
405
|
"project_id": PROJECT,
|
|
@@ -396,6 +440,16 @@ class AppsFlow(unittest.TestCase):
|
|
|
396
440
|
self.assertFalse(result.get("isError"), result)
|
|
397
441
|
self.assertEqual(result["structuredContent"]["status"], "refunded")
|
|
398
442
|
self.assertEqual(cloud.calls("PATCH", row_path), writes + 1)
|
|
443
|
+
# Every refusal and the write itself are counted by action; a tool off
|
|
444
|
+
# the allowlist is never named.
|
|
445
|
+
self.assertEqual(
|
|
446
|
+
collector().count(WRITES, action=action, status="error"), refused + 4
|
|
447
|
+
)
|
|
448
|
+
self.assertEqual(
|
|
449
|
+
collector().count(WRITES, action=action, status="success"), succeeded + 1
|
|
450
|
+
)
|
|
451
|
+
self.assertEqual(collector().count(WRITES, action="invalid"), invalid + 1)
|
|
452
|
+
self.assertEqual(collector().count(WRITES, action="tables_db_delete_row"), 0)
|
|
399
453
|
last = self.show_table(cursor="ord_002")["structuredContent"]["rows"]
|
|
400
454
|
self.assertEqual(last[0]["$id"], "ord_001")
|
|
401
455
|
self.assertEqual(last[0]["status"], "refunded")
|
|
@@ -733,6 +787,8 @@ class AppsFlow(unittest.TestCase):
|
|
|
733
787
|
self.assertIn("limit_history_exceeded", refused["content"][0]["text"])
|
|
734
788
|
|
|
735
789
|
def test_bad_requests_are_tool_errors(self):
|
|
790
|
+
invalid = collector().count(VIEWS, view="invalid", status="error")
|
|
791
|
+
files = collector().count(VIEWS, view="files", status="error")
|
|
736
792
|
cases = [
|
|
737
793
|
(
|
|
738
794
|
{"view": "chart"},
|
|
@@ -773,6 +829,14 @@ class AppsFlow(unittest.TestCase):
|
|
|
773
829
|
result = self.call("appwrite_show", arguments)
|
|
774
830
|
self.assertTrue(result["isError"])
|
|
775
831
|
self.assertIn(error, result["content"][0]["text"])
|
|
832
|
+
# An unknown view name is not a label; a known one fails under its name.
|
|
833
|
+
self.assertEqual(
|
|
834
|
+
collector().count(VIEWS, view="invalid", status="error"), invalid + 1
|
|
835
|
+
)
|
|
836
|
+
self.assertEqual(collector().count(VIEWS, view="chart"), 0)
|
|
837
|
+
self.assertEqual(
|
|
838
|
+
collector().count(VIEWS, view="files", status="error"), files + 1
|
|
839
|
+
)
|
|
776
840
|
|
|
777
841
|
|
|
778
842
|
if __name__ == "__main__":
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/.github/workflows/update-docs-index.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/__init__.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/executions.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/files.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/resource.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/usage.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/apps/users.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/assets/__init__.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/assets/favicon.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/catalog_policy.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/constants.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/docs_index.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/docs_search.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/docs_source.py
RENAMED
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/error_monitoring.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/__init__.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/bus.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/catalog.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/delivery.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/egress.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/envelope.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/errors.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/ingress.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/projection.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/protocol.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/streams.py
RENAMED
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/events/webhooks.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/instructions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/src/mcp_server_appwrite/tool_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/e2e/fixtures/appwrite/row_created.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_console_sdk_oauth.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_docs_index.py
RENAMED
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_docs_source.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/integration/test_oauth_discovery.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mcp_server_appwrite-2.2.0 → mcp_server_appwrite-2.2.2}/tests/unit/test_error_classification.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|