mcp-server-appwrite 1.0.28__tar.gz → 2.0.0__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.
Files changed (125) hide show
  1. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/AGENTS.md +11 -8
  2. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/PKG-INFO +1 -1
  3. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/tool-surface.md +15 -4
  4. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/assets/instructions.json +3 -3
  5. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/constants.py +4 -14
  6. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/data/docs_index.npz +1 -1
  7. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/docs_search.py +4 -1
  8. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/error_classification.py +11 -0
  9. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/error_monitoring.py +1 -1
  10. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/operator.py +276 -167
  11. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/telemetry.py +3 -3
  12. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/support.py +6 -0
  13. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/test_stdio.py +1 -0
  14. mcp_server_appwrite-2.0.0/tests/e2e/test_tools.py +208 -0
  15. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/support.py +6 -4
  16. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_operator.py +9 -4
  17. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_server.py +4 -4
  18. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_telemetry.py +1 -1
  19. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/.dockerignore +0 -0
  20. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/.env.example +0 -0
  21. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/.github/workflows/ci.yml +0 -0
  22. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/.github/workflows/production.yml +0 -0
  23. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/.github/workflows/publish.yml +0 -0
  24. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/.github/workflows/staging.yml +0 -0
  25. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/.github/workflows/update-docs-index.yml +0 -0
  26. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/.gitignore +0 -0
  27. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/.python-version +0 -0
  28. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/CLAUDE.md +0 -0
  29. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/Dockerfile +0 -0
  30. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/LICENSE +0 -0
  31. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/README.md +0 -0
  32. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/compose.yaml +0 -0
  33. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/appwrite-mcp-flow.png +0 -0
  34. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/appwrite-mcp-flow.svg +0 -0
  35. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/authentication.md +0 -0
  36. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/development.md +0 -0
  37. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/documentation-search.md +0 -0
  38. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/events.md +0 -0
  39. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/flags.md +0 -0
  40. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/images/claude-desktop-local-mcp-servers.png +0 -0
  41. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/images/codex-mcp.png +0 -0
  42. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/images/cursor-mcp.png +0 -0
  43. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/docs/self-hosted.md +0 -0
  44. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/glama.json +0 -0
  45. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/pyproject.toml +0 -0
  46. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/scripts/build_docs_index.py +0 -0
  47. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/scripts/docs_release_notes.py +0 -0
  48. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/scripts/render_server_json.py +0 -0
  49. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/server.template.json +0 -0
  50. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/__init__.py +0 -0
  51. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/__main__.py +0 -0
  52. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/assets/__init__.py +0 -0
  53. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/assets/favicon.svg +0 -0
  54. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/auth.py +0 -0
  55. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/catalog_policy.py +0 -0
  56. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/context.py +0 -0
  57. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/docs.py +0 -0
  58. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/docs_index.py +0 -0
  59. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/docs_source.py +0 -0
  60. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/__init__.py +0 -0
  61. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/bus.py +0 -0
  62. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/catalog.py +0 -0
  63. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/delivery.py +0 -0
  64. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/egress.py +0 -0
  65. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/envelope.py +0 -0
  66. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/errors.py +0 -0
  67. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/ingress.py +0 -0
  68. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/projection.py +0 -0
  69. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/protocol.py +0 -0
  70. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/streams.py +0 -0
  71. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/subscriptions.py +0 -0
  72. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/events/webhooks.py +0 -0
  73. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/flags.py +0 -0
  74. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/http_app.py +0 -0
  75. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/instructions.py +0 -0
  76. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/legacy.py +0 -0
  77. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/server.py +0 -0
  78. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/service.py +0 -0
  79. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/src/mcp_server_appwrite/tool_manager.py +0 -0
  80. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/deployment_function_duplicate.json +0 -0
  81. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/deployment_function_failed.json +0 -0
  82. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/deployment_function_ready.json +0 -0
  83. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/deployment_site_ready.json +0 -0
  84. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/execution_async_failed.json +0 -0
  85. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/execution_async_waiting.json +0 -0
  86. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/execution_sync_failed.json +0 -0
  87. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/file_created.json +0 -0
  88. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/function_activation.json +0 -0
  89. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/row_created.json +0 -0
  90. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/fixtures/appwrite/user_created.json +0 -0
  91. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/test_events_ingress.py +0 -0
  92. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/test_events_protocol.py +0 -0
  93. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/test_events_stream.py +0 -0
  94. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/e2e/test_events_subscribe.py +0 -0
  95. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_avatars.py +0 -0
  96. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_console_sdk_oauth.py +0 -0
  97. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_docs_index.py +0 -0
  98. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_docs_source.py +0 -0
  99. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_functions.py +0 -0
  100. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_locale.py +0 -0
  101. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_messaging.py +0 -0
  102. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_oauth_discovery.py +0 -0
  103. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_operator.py +0 -0
  104. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_sites.py +0 -0
  105. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_storage.py +0 -0
  106. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_tables_db.py +0 -0
  107. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_teams.py +0 -0
  108. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/integration/test_users.py +0 -0
  109. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/test_all.py +0 -0
  110. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/fixtures/jwe-v1.json +0 -0
  111. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_auth.py +0 -0
  112. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_context.py +0 -0
  113. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_docs.py +0 -0
  114. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_docs_search.py +0 -0
  115. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_error_classification.py +0 -0
  116. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_error_monitoring.py +0 -0
  117. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_events_delivery.py +0 -0
  118. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_events_egress.py +0 -0
  119. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_events_envelope.py +0 -0
  120. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_events_protocol.py +0 -0
  121. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_http_app.py +0 -0
  122. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_instructions.py +0 -0
  123. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_release_metadata.py +0 -0
  124. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/tests/unit/test_service.py +0 -0
  125. {mcp_server_appwrite-1.0.28 → mcp_server_appwrite-2.0.0}/uv.lock +0 -0
@@ -29,7 +29,7 @@ Source lives in `src/mcp_server_appwrite/`:
29
29
  | `flags.py` | Central registry of tester/testing flags (CLI arg + env var per flag). Enabling and testing them is documented in [docs/flags.md](docs/flags.md). |
30
30
  | `service.py` | `Service` base class: introspects an Appwrite SDK service and turns its methods into MCP tool definitions. |
31
31
  | `tool_manager.py` | Registry of all services and their generated tools. |
32
- | `operator.py` | The compact "operator" surface — `appwrite_search_tools`, `appwrite_call_tool`, result/resource storage, write confirmation. |
32
+ | `operator.py` | The compact "operator" surface — `appwrite_search_tools`, `appwrite_call_tool` / `appwrite_call_write_tool`, tool annotations, result/resource storage, write confirmation. |
33
33
  | `instructions.py` | `serverInfo.description` and the server instructions sent on initialize, rendered from `assets/instructions.json` (a description plus ordered, transport/docs-conditional Markdown sections). Edit the JSON to change the wording; keep the `pyproject.toml` description in sync (a test enforces it). |
34
34
  | `context.py` | `appwrite_get_context` — workspace summary (project, services, account/org for OAuth). |
35
35
  | `docs_search.py` | In-process semantic docs search (`appwrite_search_docs`) over a prebuilt index. |
@@ -47,17 +47,20 @@ Source lives in `src/mcp_server_appwrite/`:
47
47
  no-op unless the transport is `http` and `OTEL_EXPORTER_OTLP_ENDPOINT` is set, so
48
48
  `stdio` and unconfigured servers stay silent. In the cluster that endpoint is the
49
49
  Alloy collector, which adds the `deployment.*` labels and forwards upstream — the app
50
- needs no credentials. Dashboards live in the `dashboards` repo under `MCP/`.
50
+ needs no credentials. The MCP dashboard lives in `appwrite-labs/monitoring` at
51
+ `dashboards/Cloud/mcp.json`, synced to monitoring.appwrite.systems by Grafana Git Sync.
51
52
 
52
53
  ### Tool surface (key design point)
53
54
 
54
- The server boots in a compact workflow: the client sees up to 4 tools
55
- (`appwrite_get_context`, `appwrite_search_tools`, `appwrite_call_tool`, and
56
- optionally `appwrite_search_docs`), while the full OAuth Appwrite catalog (41
55
+ The server boots in a compact workflow: the client sees up to 5 tools
56
+ (`appwrite_get_context`, `appwrite_search_tools`, `appwrite_call_tool` for
57
+ reads, `appwrite_call_write_tool` for everything else, and optionally
58
+ `appwrite_search_docs`), while the full OAuth Appwrite catalog (41
57
59
  services) stays internal and is searched at runtime. API-key stdio uses a filtered
58
- 27-service catalog. Mutating hidden tools require
59
- `confirm_write=true`. Large outputs are stored as MCP resources and returned as a
60
- preview + resource URI.
60
+ 27-service catalog. Mutating hidden tools run only through
61
+ `appwrite_call_write_tool` and require `confirm_write=true`, so each exposed
62
+ tool's annotations match every call it accepts. Large outputs are stored as MCP
63
+ resources and returned as a preview + resource URI.
61
64
 
62
65
  ## Local development
63
66
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mcp-server-appwrite
3
- Version: 1.0.28
3
+ Version: 2.0.0
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
@@ -8,15 +8,17 @@ searched at runtime.
8
8
  flowchart LR
9
9
  M[Model] -->|sees| E
10
10
 
11
- subgraph E[Exposed surface — up to 4 tools]
11
+ subgraph E[Exposed surface — up to 5 tools]
12
12
  C[appwrite_get_context]
13
13
  ST[appwrite_search_tools]
14
14
  CT[appwrite_call_tool]
15
+ WT[appwrite_call_write_tool]
15
16
  SD[appwrite_search_docs *]
16
17
  end
17
18
 
18
19
  ST -.searches.-> CAT
19
- CT -.invokes.-> CAT
20
+ CT -.reads.-> CAT
21
+ WT -.mutates.-> CAT
20
22
 
21
23
  subgraph CAT[Internal catalog — authentication-aware]
22
24
  direction LR
@@ -35,14 +37,23 @@ flowchart LR
35
37
  | --- | --- |
36
38
  | `appwrite_get_context` | Workspace summary. API key → project + readable service totals/samples. OAuth → also account, organization, discovered projects. |
37
39
  | `appwrite_search_tools` | Searches the internal catalog at runtime. |
38
- | `appwrite_call_tool` | Invokes a catalog tool. Mutating calls require `confirm_write=true`. |
40
+ | `appwrite_call_tool` | Runs a read catalog tool (`list`, `get`). |
41
+ | `appwrite_call_write_tool` | Runs every other catalog tool (create, update, delete, upsert, unknown verbs). Requires `confirm_write=true`. |
39
42
  | `appwrite_search_docs` | Semantic search over Appwrite docs (conditional — see above). |
40
43
 
41
44
  ## Behavior
42
45
 
43
46
  - **Large outputs** are stored as an MCP resource and returned as preview text
44
47
  plus a resource URI.
45
- - **Writes** through hidden mutating tools require `confirm_write=true`.
48
+ - **Reads and writes** go through separate tools so each tool's annotations
49
+ match every call it accepts: `appwrite_call_tool` is read-only and
50
+ idempotent, `appwrite_call_write_tool` is destructive. Each search result
51
+ names its `call_tool`, and calling a tool through the other one returns an
52
+ error naming the right one.
53
+ - **Writes** require `confirm_write=true`.
54
+ - **Annotations**: every exposed tool sets a `title` and all four hints
55
+ (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`)
56
+ explicitly, as the Anthropic and OpenAI connector directories require.
46
57
  - **Target context** is included in search results as `context=console`,
47
58
  `context=organization`, or `context=project`. Hosted calls enforce the required
48
59
  top-level `organization_id` or `project_id` before making a request.
@@ -79,8 +79,8 @@
79
79
  {
80
80
  "lines": [
81
81
  "Using these tools",
82
- "- Use appwrite_get_context to understand the current connection and available project resources, then use appwrite_search_tools and appwrite_call_tool for specific operations.",
83
- "- Mutating hidden tools require confirm_write=true."
82
+ "- Use appwrite_get_context to understand the current connection and available project resources, then use appwrite_search_tools to find the operation you need.",
83
+ "- Run read operations (list, get) with appwrite_call_tool. Run every other operation (create, update, delete, upsert) with appwrite_call_write_tool and confirm_write=true; each search result names its call_tool."
84
84
  ]
85
85
  },
86
86
  {
@@ -101,7 +101,7 @@
101
101
  "transports": ["http"],
102
102
  "lines": [
103
103
  "- You authenticate against the Appwrite console, which can list your organizations and projects but stores no project data itself.",
104
- "- Project-scoped tools (every database type, users, teams, storage, functions, sites, messaging, firewall, advisor) need a target project: use appwrite_get_context first, then pass the selected project id as project_id to appwrite_call_tool.",
104
+ "- Project-scoped tools (every database type, users, teams, storage, functions, sites, messaging, firewall, advisor) need a target project: use appwrite_get_context first, then pass the selected project id as project_id to appwrite_call_tool or appwrite_call_write_tool.",
105
105
  "- Organization-scoped console tools (e.g. creating a project or managing domains) need organization_id.",
106
106
  "- File/image uploads: pass a public URL as the file argument (e.g. {\"url\": \"https://...\"}) so the server downloads it directly; for very small files you may pass inline base64 ({\"filename\": ..., \"content\": ..., \"encoding\": \"base64\"}).",
107
107
  "- Hosted HTTP returns tool results inline, including images and other binary payloads."
@@ -13,8 +13,6 @@ from appwrite_console.models.site import Site
13
13
  from appwrite_console.models.team import Team
14
14
  from appwrite_console.models.user import User
15
15
 
16
- # --- server ---------------------------------------------------------------
17
-
18
16
 
19
17
  def _resolve_server_version() -> str:
20
18
  try:
@@ -64,8 +62,6 @@ HOSTED_PATH_GUIDANCE = (
64
62
  '{{"filename": "...", "content": "<base64>", "encoding": "base64"}}.'
65
63
  )
66
64
 
67
- # --- auth -----------------------------------------------------------------
68
-
69
65
  DEFAULT_PROJECT_ID = "console"
70
66
 
71
67
  # Curated scope allowlist. Empty means "no curation": the MCP mirrors the
@@ -77,8 +73,6 @@ PREFERRED_SCOPES: list[str] = []
77
73
  # Shared TTL for cached upstream lookups (OAuth discovery, project regions).
78
74
  CACHE_TTL_SECONDS = 300.0
79
75
 
80
- # --- http_app -------------------------------------------------------------
81
-
82
76
  CORS_HEADERS = {
83
77
  "Access-Control-Allow-Origin": "*",
84
78
  "Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
@@ -90,8 +84,10 @@ CORS_HEADERS = {
90
84
  "Access-Control-Expose-Headers": "Mcp-Session-Id, WWW-Authenticate, Link",
91
85
  }
92
86
 
93
- # --- operator -------------------------------------------------------------
94
-
87
+ CONTEXT_TOOL_NAME = "appwrite_get_context"
88
+ SEARCH_TOOL_NAME = "appwrite_search_tools"
89
+ CALL_TOOL_NAME = "appwrite_call_tool"
90
+ WRITE_TOOL_NAME = "appwrite_call_write_tool"
95
91
  SEARCH_LIMIT = 8
96
92
  # A page of rows does not fit in 800 characters, so the model paid a
97
93
  # resources/read for every list call. Only stdio stores results at all.
@@ -106,8 +102,6 @@ UPDATE_HINTS = {"change", "edit", "modify", "rename", "set", "update"}
106
102
  DELETE_HINTS = {"delete", "destroy", "drop", "remove"}
107
103
  READ_HINTS = {"fetch", "find", "get", "list", "read", "search", "show", "view"}
108
104
 
109
- # --- docs_search ----------------------------------------------------------
110
-
111
105
  DOCS_TOOL_NAME = "appwrite_search_docs"
112
106
  EMBED_MODEL = "text-embedding-3-small"
113
107
  DOCS_DEFAULT_LIMIT = 5
@@ -119,8 +113,6 @@ DATA_DIR = Path(__file__).parent / "data"
119
113
  INDEX_FILE = "docs_index.npz"
120
114
  META_MEMBER = "meta.json"
121
115
 
122
- # --- context --------------------------------------------------------------
123
-
124
116
  SERVICE_PROBES = {
125
117
  "tablesdb": {
126
118
  "path": "/tablesdb",
@@ -161,8 +153,6 @@ SERVICE_PROBES = {
161
153
 
162
154
  REDACTED_KEYS = {"password", "secret", "key", "token", "otp", "cookie", "session"}
163
155
 
164
- # --- telemetry ------------------------------------------------------------
165
-
166
156
  ACTIVE_WINDOW_SECONDS = 300.0 # rolling window for "active users/clients" gauges
167
157
 
168
158
  # Known MCP clients (normalized: lowercase, whitespace -> "-"). Client names are
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
- name: mcp_server_appwrite-1.0.28/src/mcp_server_appwrite/data/docs_index.npz
2
+ name: mcp_server_appwrite-2.0.0/src/mcp_server_appwrite/data/docs_index.npz
3
3
  size: 35539301 bytes
4
4
  sha256: 5a6a4cef977b909050f1980567781c16720ec7eff17320fd7811a3c386b38a4e
@@ -144,10 +144,13 @@ class DocsSearch:
144
144
  def get_tool(self) -> types.Tool:
145
145
  return types.Tool(
146
146
  name=DOCS_TOOL_NAME,
147
+ title="Search Appwrite docs",
147
148
  annotations=types.ToolAnnotations(
149
+ title="Search Appwrite docs",
148
150
  read_only_hint=True,
149
- open_world_hint=True,
150
151
  destructive_hint=False,
152
+ idempotent_hint=True,
153
+ open_world_hint=True,
151
154
  ),
152
155
  description=(
153
156
  "Search the Appwrite documentation with a natural-language query and "
@@ -15,6 +15,7 @@ from appwrite_console.exception import AppwriteException
15
15
 
16
16
  ErrorCategory = Literal[
17
17
  "write_confirmation",
18
+ "wrong_call_tool",
18
19
  "appwrite_4xx",
19
20
  "appwrite_5xx",
20
21
  "sdk_validation",
@@ -25,6 +26,7 @@ ErrorCategory = Literal[
25
26
  ERROR_CATEGORIES: frozenset[str] = frozenset(
26
27
  {
27
28
  "write_confirmation",
29
+ "wrong_call_tool",
28
30
  "appwrite_4xx",
29
31
  "appwrite_5xx",
30
32
  "sdk_validation",
@@ -38,6 +40,12 @@ class WriteConfirmationRequired(RuntimeError):
38
40
  """A mutating hidden tool was called without explicit confirmation."""
39
41
 
40
42
 
43
+ class WrongCallTool(ValueError):
44
+ """A hidden tool was called through the public tool for the other
45
+ classification: a mutation through the read-only call tool, or a read
46
+ through the write call tool."""
47
+
48
+
41
49
  class HostedBinaryResponseTooLarge(ValueError):
42
50
  """A binary Appwrite response exceeded the hosted MCP memory-safe limit."""
43
51
 
@@ -72,6 +80,9 @@ def classify_tool_error(exc: BaseException) -> ErrorCategory:
72
80
  if any(isinstance(item, WriteConfirmationRequired) for item in chain):
73
81
  return "write_confirmation"
74
82
 
83
+ if any(isinstance(item, WrongCallTool) for item in chain):
84
+ return "wrong_call_tool"
85
+
75
86
  if any(isinstance(item, HostedBinaryResponseTooLarge) for item in chain):
76
87
  return "response_too_large"
77
88
 
@@ -171,7 +171,7 @@ def _should_capture(exc: BaseException) -> bool:
171
171
  return False
172
172
 
173
173
  category = classify_tool_error(exc)
174
- if category in {"write_confirmation", "appwrite_4xx"}:
174
+ if category in {"write_confirmation", "wrong_call_tool", "appwrite_4xx"}:
175
175
  return False
176
176
  # Pydantic validation errors are ValueError subclasses, but SDK response
177
177
  # validation is actionable model drift and must remain visible.