From b0c37d6b7e1f4f92523a2221689fffcbaa7cf992 Mon Sep 17 00:00:00 2001 From: adityaoberai Date: Thu, 10 Sep 2026 19:15:55 +0530 Subject: [PATCH 1/3] chore: prepare ChatGPT app submission metadata --- chatgpt-app-submission.json | 112 +++++++++++++++++++++++++ src/mcp_server_appwrite/docs_search.py | 5 ++ src/mcp_server_appwrite/operator.py | 15 ++++ 3 files changed, 132 insertions(+) create mode 100644 chatgpt-app-submission.json diff --git a/chatgpt-app-submission.json b/chatgpt-app-submission.json new file mode 100644 index 0000000..e2cb10d --- /dev/null +++ b/chatgpt-app-submission.json @@ -0,0 +1,112 @@ +{ + "$schema": "https://developers.openai.com/apps-sdk/schemas/chatgpt-app-submission.v1.json", + "schema_version": 1, + "app_info": { + "display_name": "Appwrite", + "subtitle": "Manage Appwrite projects", + "description": "Appwrite helps users explore their projects, inspect databases and storage, and manage users, functions, messaging, and sites through ChatGPT. Discover available operations, review required parameters, and execute changes with write confirmation. Search official Appwrite documentation for concepts and implementation guidance when documentation search is enabled.", + "category": "DEVELOPER_TOOLS" + }, + "tools": { + "appwrite_get_context": { + "annotations": { "readOnlyHint": true, "openWorldHint": false, "destructiveHint": false }, + "justifications": { + "read_only_justification": "Retrieves accessible account, organization, project, and service summaries without modifying Appwrite resources.", + "open_world_justification": "Only retrieves workspace information and does not publish content or change external systems.", + "destructive_justification": "Does not delete resources, overwrite data, send messages, or revoke access." + } + }, + "appwrite_search_tools": { + "annotations": { "readOnlyHint": true, "openWorldHint": false, "destructiveHint": false }, + "justifications": { + "read_only_justification": "Searches the available operation catalog and returns parameter schemas without executing the matching operations.", + "open_world_justification": "Catalog searches do not publish content or modify Appwrite or other external systems.", + "destructive_justification": "Returns operation descriptions without performing any deletion, overwrite, or access change." + } + }, + "appwrite_call_tool": { + "annotations": { "readOnlyHint": false, "openWorldHint": true, "destructiveHint": true }, + "justifications": { + "read_only_justification": "Executes Appwrite operations that can create, update, or delete resources and run functions or messaging workflows.", + "open_world_justification": "Appwrite operations can send messages and deploy publicly accessible sites or functions, affecting external recipients and public content.", + "destructive_justification": "Can delete resources, overwrite data, or revoke access, with operations classified as mutating requiring confirm_write=true." + } + }, + "appwrite_search_docs": { + "annotations": { "readOnlyHint": true, "openWorldHint": false, "destructiveHint": false }, + "justifications": { + "read_only_justification": "Embeds the documentation query through OpenAI and retrieves matching indexed documentation without changing Appwrite resources.", + "open_world_justification": "Sends the query to OpenAI for embedding but does not publish content or change external application resources.", + "destructive_justification": "Retrieves documentation and cannot delete data, overwrite resources, or revoke access." + } + } + }, + "test_cases": [ + { + "description": "Discover accessible projects and their service summaries.", + "user_prompt": "Show my accessible Appwrite projects and summarize their services without sample records.", + "file_attachment_urls": null, + "tools_triggered": "appwrite_get_context", + "expected_output": "Lists accessible projects and available service totals, explaining permission limits or when a project must be selected to retrieve service summaries.", + "expected_output_url": null + }, + { + "description": "Find read-only storage operations without executing them.", + "user_prompt": "Find Appwrite operations for listing storage buckets, excluding operations that change data, and show their required parameters.", + "file_attachment_urls": null, + "tools_triggered": "appwrite_search_tools", + "expected_output": "Returns matching read operations, their exact operation names, parameter schemas, and target context without executing an API operation.", + "expected_output_url": null + }, + { + "description": "Read project resources through the operator workflow.", + "user_prompt": "Find my Appwrite project named Submission Review and list its storage buckets. If several projects have that name, let me choose first.", + "file_attachment_urls": null, + "tools_triggered": "appwrite_get_context, appwrite_search_tools, appwrite_call_tool", + "expected_output": "Resolves the project, discovers the bucket-list operation, and returns buckets or an empty result; requests clarification for ambiguous projects and reports access restrictions without changing resources.", + "expected_output_url": null + }, + { + "description": "Create a resource with explicit user authorization in a disposable review project.", + "user_prompt": "In my disposable Appwrite project named Submission Review, create a private storage bucket named submission-review-test with a unique ID and no public permissions. I authorize this creation; if the project is ambiguous, ask me to select it.", + "file_attachment_urls": null, + "tools_triggered": "appwrite_get_context, appwrite_search_tools, appwrite_call_tool", + "expected_output": "Selects the intended project, discovers creation parameters, and executes the authorized creation with confirm_write=true, reporting the created bucket and its private permissions or a clear API error.", + "expected_output_url": null + }, + { + "description": "Search documentation with the optional documentation tool enabled.", + "user_prompt": "Find the official Appwrite documentation explaining database relationships and return up to two relevant pages.", + "file_attachment_urls": null, + "tools_triggered": "appwrite_search_docs", + "expected_output": "Returns up to two relevant documentation pages with source links and content, or clearly reports that no matching documentation was found.", + "expected_output_url": null + } + ], + "negative_test_cases": [ + { + "description": "Do not invoke Appwrite for an unrelated calendar request.", + "user_prompt": "What meetings are on my Google Calendar tomorrow?", + "file_attachment_urls": null, + "tools_triggered": null, + "expected_output": "Does not invoke Appwrite because reading Google Calendar is outside its supported workflows.", + "expected_output_url": null + }, + { + "description": "Do not invoke Appwrite to administer a different backend provider.", + "user_prompt": "List the tables in my Supabase project.", + "file_attachment_urls": null, + "tools_triggered": null, + "expected_output": "Does not invoke Appwrite because the request targets a different backend service.", + "expected_output_url": null + }, + { + "description": "Do not invoke Appwrite for a general programming explanation.", + "user_prompt": "Explain Python list comprehensions with a simple example. Do not access any external services.", + "file_attachment_urls": null, + "tools_triggered": null, + "expected_output": "Answers the programming question without invoking Appwrite or accessing workspace data.", + "expected_output_url": null + } + ] +} diff --git a/src/mcp_server_appwrite/docs_search.py b/src/mcp_server_appwrite/docs_search.py index 465d575..76c0339 100644 --- a/src/mcp_server_appwrite/docs_search.py +++ b/src/mcp_server_appwrite/docs_search.py @@ -144,6 +144,11 @@ def _load_index(self) -> bool: def get_tool(self) -> types.Tool: return types.Tool( name=DOCS_TOOL_NAME, + annotations=types.ToolAnnotations( + read_only_hint=True, + open_world_hint=False, + destructive_hint=False, + ), description=( "Search the Appwrite documentation with a natural-language query and " "return the most relevant documentation pages with their full content. " diff --git a/src/mcp_server_appwrite/operator.py b/src/mcp_server_appwrite/operator.py index c4bd6db..2c6cc73 100644 --- a/src/mcp_server_appwrite/operator.py +++ b/src/mcp_server_appwrite/operator.py @@ -145,6 +145,11 @@ def get_public_tools(self) -> list[types.Tool]: tools = [ types.Tool( name="appwrite_get_context", + annotations=types.ToolAnnotations( + read_only_hint=True, + open_world_hint=False, + destructive_hint=False, + ), description=( "Get an adaptive Appwrite account/project context summary, including " "available projects and per-project service counts where the current " @@ -186,6 +191,11 @@ def get_public_tools(self) -> list[types.Tool]: ), types.Tool( name="appwrite_search_tools", + annotations=types.ToolAnnotations( + read_only_hint=True, + open_world_hint=False, + destructive_hint=False, + ), description=( "Search the hidden Appwrite tool catalog by natural language query. " "Matches include parameter schemas (name, type, required/optional, " @@ -227,6 +237,11 @@ def get_public_tools(self) -> list[types.Tool]: ), types.Tool( name="appwrite_call_tool", + annotations=types.ToolAnnotations( + read_only_hint=False, + open_world_hint=True, + destructive_hint=True, + ), description=( "Call a hidden Appwrite tool by name. Put Appwrite parameters inside `arguments`. " "Mutating tools require confirm_write=true. Hidden Appwrite parameters accept " From 4e2d45b52f1a6658c0f6393d3f876a667a664723 Mon Sep 17 00:00:00 2001 From: adityaoberai Date: Thu, 10 Sep 2026 19:20:20 +0530 Subject: [PATCH 2/3] chore: remove ChatGPT submission metadata --- chatgpt-app-submission.json | 112 ------------------------------------ 1 file changed, 112 deletions(-) delete mode 100644 chatgpt-app-submission.json diff --git a/chatgpt-app-submission.json b/chatgpt-app-submission.json deleted file mode 100644 index e2cb10d..0000000 --- a/chatgpt-app-submission.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "$schema": "https://developers.openai.com/apps-sdk/schemas/chatgpt-app-submission.v1.json", - "schema_version": 1, - "app_info": { - "display_name": "Appwrite", - "subtitle": "Manage Appwrite projects", - "description": "Appwrite helps users explore their projects, inspect databases and storage, and manage users, functions, messaging, and sites through ChatGPT. Discover available operations, review required parameters, and execute changes with write confirmation. Search official Appwrite documentation for concepts and implementation guidance when documentation search is enabled.", - "category": "DEVELOPER_TOOLS" - }, - "tools": { - "appwrite_get_context": { - "annotations": { "readOnlyHint": true, "openWorldHint": false, "destructiveHint": false }, - "justifications": { - "read_only_justification": "Retrieves accessible account, organization, project, and service summaries without modifying Appwrite resources.", - "open_world_justification": "Only retrieves workspace information and does not publish content or change external systems.", - "destructive_justification": "Does not delete resources, overwrite data, send messages, or revoke access." - } - }, - "appwrite_search_tools": { - "annotations": { "readOnlyHint": true, "openWorldHint": false, "destructiveHint": false }, - "justifications": { - "read_only_justification": "Searches the available operation catalog and returns parameter schemas without executing the matching operations.", - "open_world_justification": "Catalog searches do not publish content or modify Appwrite or other external systems.", - "destructive_justification": "Returns operation descriptions without performing any deletion, overwrite, or access change." - } - }, - "appwrite_call_tool": { - "annotations": { "readOnlyHint": false, "openWorldHint": true, "destructiveHint": true }, - "justifications": { - "read_only_justification": "Executes Appwrite operations that can create, update, or delete resources and run functions or messaging workflows.", - "open_world_justification": "Appwrite operations can send messages and deploy publicly accessible sites or functions, affecting external recipients and public content.", - "destructive_justification": "Can delete resources, overwrite data, or revoke access, with operations classified as mutating requiring confirm_write=true." - } - }, - "appwrite_search_docs": { - "annotations": { "readOnlyHint": true, "openWorldHint": false, "destructiveHint": false }, - "justifications": { - "read_only_justification": "Embeds the documentation query through OpenAI and retrieves matching indexed documentation without changing Appwrite resources.", - "open_world_justification": "Sends the query to OpenAI for embedding but does not publish content or change external application resources.", - "destructive_justification": "Retrieves documentation and cannot delete data, overwrite resources, or revoke access." - } - } - }, - "test_cases": [ - { - "description": "Discover accessible projects and their service summaries.", - "user_prompt": "Show my accessible Appwrite projects and summarize their services without sample records.", - "file_attachment_urls": null, - "tools_triggered": "appwrite_get_context", - "expected_output": "Lists accessible projects and available service totals, explaining permission limits or when a project must be selected to retrieve service summaries.", - "expected_output_url": null - }, - { - "description": "Find read-only storage operations without executing them.", - "user_prompt": "Find Appwrite operations for listing storage buckets, excluding operations that change data, and show their required parameters.", - "file_attachment_urls": null, - "tools_triggered": "appwrite_search_tools", - "expected_output": "Returns matching read operations, their exact operation names, parameter schemas, and target context without executing an API operation.", - "expected_output_url": null - }, - { - "description": "Read project resources through the operator workflow.", - "user_prompt": "Find my Appwrite project named Submission Review and list its storage buckets. If several projects have that name, let me choose first.", - "file_attachment_urls": null, - "tools_triggered": "appwrite_get_context, appwrite_search_tools, appwrite_call_tool", - "expected_output": "Resolves the project, discovers the bucket-list operation, and returns buckets or an empty result; requests clarification for ambiguous projects and reports access restrictions without changing resources.", - "expected_output_url": null - }, - { - "description": "Create a resource with explicit user authorization in a disposable review project.", - "user_prompt": "In my disposable Appwrite project named Submission Review, create a private storage bucket named submission-review-test with a unique ID and no public permissions. I authorize this creation; if the project is ambiguous, ask me to select it.", - "file_attachment_urls": null, - "tools_triggered": "appwrite_get_context, appwrite_search_tools, appwrite_call_tool", - "expected_output": "Selects the intended project, discovers creation parameters, and executes the authorized creation with confirm_write=true, reporting the created bucket and its private permissions or a clear API error.", - "expected_output_url": null - }, - { - "description": "Search documentation with the optional documentation tool enabled.", - "user_prompt": "Find the official Appwrite documentation explaining database relationships and return up to two relevant pages.", - "file_attachment_urls": null, - "tools_triggered": "appwrite_search_docs", - "expected_output": "Returns up to two relevant documentation pages with source links and content, or clearly reports that no matching documentation was found.", - "expected_output_url": null - } - ], - "negative_test_cases": [ - { - "description": "Do not invoke Appwrite for an unrelated calendar request.", - "user_prompt": "What meetings are on my Google Calendar tomorrow?", - "file_attachment_urls": null, - "tools_triggered": null, - "expected_output": "Does not invoke Appwrite because reading Google Calendar is outside its supported workflows.", - "expected_output_url": null - }, - { - "description": "Do not invoke Appwrite to administer a different backend provider.", - "user_prompt": "List the tables in my Supabase project.", - "file_attachment_urls": null, - "tools_triggered": null, - "expected_output": "Does not invoke Appwrite because the request targets a different backend service.", - "expected_output_url": null - }, - { - "description": "Do not invoke Appwrite for a general programming explanation.", - "user_prompt": "Explain Python list comprehensions with a simple example. Do not access any external services.", - "file_attachment_urls": null, - "tools_triggered": null, - "expected_output": "Answers the programming question without invoking Appwrite or accessing workspace data.", - "expected_output_url": null - } - ] -} From eddb555cb1e67878776178921bc0fd0551b0eaf3 Mon Sep 17 00:00:00 2001 From: adityaoberai Date: Sat, 12 Sep 2026 00:29:59 +0530 Subject: [PATCH 3/3] fix: mark docs search and get_context as open-world tools appwrite_search_docs sends each query to OpenAI's embeddings API and appwrite_get_context reads live account, organization, project, and service data from the Appwrite API. Both make outbound calls, so open_world_hint=False misrepresented them to clients that honour tool annotations. appwrite_search_tools stays closed-world since it only searches the in-process catalog. Addresses Greptile review feedback on #118. Co-Authored-By: Claude Fable 5.1 --- src/mcp_server_appwrite/docs_search.py | 2 +- src/mcp_server_appwrite/operator.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mcp_server_appwrite/docs_search.py b/src/mcp_server_appwrite/docs_search.py index 76c0339..a721263 100644 --- a/src/mcp_server_appwrite/docs_search.py +++ b/src/mcp_server_appwrite/docs_search.py @@ -146,7 +146,7 @@ def get_tool(self) -> types.Tool: name=DOCS_TOOL_NAME, annotations=types.ToolAnnotations( read_only_hint=True, - open_world_hint=False, + open_world_hint=True, destructive_hint=False, ), description=( diff --git a/src/mcp_server_appwrite/operator.py b/src/mcp_server_appwrite/operator.py index 2c6cc73..eb77628 100644 --- a/src/mcp_server_appwrite/operator.py +++ b/src/mcp_server_appwrite/operator.py @@ -147,7 +147,7 @@ def get_public_tools(self) -> list[types.Tool]: name="appwrite_get_context", annotations=types.ToolAnnotations( read_only_hint=True, - open_world_hint=False, + open_world_hint=True, destructive_hint=False, ), description=(