diff --git a/README.md b/README.md index 490eca6..3fae3b9 100644 --- a/README.md +++ b/README.md @@ -6,33 +6,32 @@ ![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white) ![Claude](https://img.shields.io/badge/Claude-AI-orange?style=for-the-badge) -**Transform Claude into your intelligent Appwrite management assistant** -*16 optimized tools • Core Appwrite operations • Context-efficient design • Production-ready* +**Transform Claude into your intelligent Appwrite management assistant** +*12 optimized tools • Core Appwrite operations • Context-efficient design • Production-ready* --- -A **context-optimized Model Context Protocol (MCP)** server that provides Claude with essential Appwrite integration. This version focuses on core functionality with action-based combined tools, offering database operations, user management, storage, messaging, and team management in an efficient package designed to stay under MCP context limits. +A **context-optimized Model Context Protocol (MCP)** server that provides Claude with essential Appwrite integration. This version focuses on core functionality with action-based combined tools, offering database operations, user management, storage, and team management in an efficient package designed to stay under MCP context limits. ## 🚀 Features **🎯 Core Appwrite Operations:** -- **Database Management** - Create, list, and delete databases -- **Collection Operations** - Full collection lifecycle management -- **Document CRUD** - Complete document operations with bulk support +- **Database Management** - Create, get, list, update, and delete databases with enabled status +- **Collection Operations** - Full collection lifecycle with document security and search support +- **Document CRUD** - Complete document operations with bulk support and advanced queries - **User Management** - User operations including bulk processing and preferences -- **Storage Management** - Bucket and file operations with URL generation -- **Team Management** - Complete team administration -- **Messaging Service** - Messages, topics, and subscriber management -- **Attribute & Index Management** - Schema management with bulk operations +- **Storage Management** - Complete bucket operations with advanced configuration (file size limits, allowed extensions, compression, encryption, antivirus) and file upload/download +- **Team Management** - Team CRUD operations +- **Attribute & Index Management** - Schema management with array support and bulk operations - **Health Monitoring** - System health checks **⚡ Action-Based Design:** - **Single tools handle multiple operations** using action parameters - **Bulk operations integrated** into respective categories -- **Context-optimized** - 88% fewer tools than original -- **Maintained functionality** - All core features preserved +- **Context-optimized** - Efficient tool design for reduced token usage +- **Advanced features** - Document security, file download, bucket encryption, array attributes ## 📦 Quick Start @@ -167,14 +166,14 @@ Once integrated, you should be able to use commands like: - "Create a new user with email test@example.com" - "Show me all collections in my database" -## 🛠️ Available Tools (16 Total) +## 🛠️ Available Tools (12 Total)
🗄️ Database Operations (1 tool) | Tool | Actions | Description | |------|---------|-------------| -| `manage_database` | create, list, delete | Comprehensive database management | +| `manage_database` | create, get, list, update, delete | Comprehensive database management with enabled status |
@@ -183,7 +182,7 @@ Once integrated, you should be able to use commands like: | Tool | Actions | Description | |------|---------|-------------| -| `collection_operations` | create, get, list, update, delete | Complete collection lifecycle management | +| `collection_operations` | create, get, list, update, delete | Complete collection lifecycle with document security, enabled status, and search | @@ -192,7 +191,7 @@ Once integrated, you should be able to use commands like: | Tool | Actions | Description | |------|---------|-------------| -| `attribute_operations` | create, get, list, update, delete, bulk_create, bulk_delete | Full attribute management with bulk operations | +| `attribute_operations` | create, get, list, update, delete, bulk_create, bulk_delete | Full attribute management with array support and bulk operations | @@ -230,8 +229,8 @@ Once integrated, you should be able to use commands like: | Tool | Actions/Description | |------|--------------------| -| `bucket_operations` | create, get, list, update, delete | -| `file_operations` | get, create, update, delete, list | +| `bucket_operations` | create, get, list, update, delete - with advanced configuration (file size limits, allowed extensions, compression, encryption, antivirus) | +| `file_operations` | get, create, update, delete, list, download - with actual file download implementation | | `get_file_url` | Generate download, preview, or view URLs with transformations | @@ -246,17 +245,6 @@ Once integrated, you should be able to use commands like: -
-📧 Messaging Operations (3 tools) - -| Tool | Actions | Description | -|------|---------|-------------| -| `messaging_message_operations` | create, get, list, update, delete | Complete message management | -| `messaging_topic_operations` | create, get, list, update, delete | Complete topic management | -| `messaging_subscriber_operations` | create, get, list, delete | Complete subscriber management | - -
-
🏥 Health Monitoring (1 tool) @@ -278,21 +266,21 @@ Once integrated, you should be able to use commands like: - **Appwrite Cloud**: `https://cloud.appwrite.io/v1` - **Self-hosted**: `https://your-domain.com/v1` 3. **🔐 API Key**: Create a new API key in your Appwrite console under **Settings > API Keys** - - ✅ Required scopes: `databases.read`, `databases.write`, `users.read`, `users.write`, `storage.read`, `storage.write`, `messages.read`, `messages.write`, `topics.read`, `topics.write`, `subscribers.read`, `subscribers.write`, `teams.read`, `teams.write`, `health.read` + - ✅ Required scopes: `databases.read`, `databases.write`, `users.read`, `users.write`, `storage.read`, `storage.write`, `teams.read`, `teams.write`, `health.read` ### 🎯 VS Official Implementation | Feature | Our Implementation | Official Appwrite MCP | |---------|-------------------|----------------------| -| **Tools Available** | 🟢 16 optimized tools (context-efficient) | 🟡 195 tools (selective enabling) | -| **Context Usage** | 🟢 Under 25,000 tokens (88% reduction) | 🔴 Over 60,000 tokens | +| **Tools Available** | 🟢 12 optimized tools (context-efficient) | 🟡 195 tools (selective enabling) | +| **Context Usage** | 🟢 Optimized token usage | 🔴 Over 60,000 tokens | | **Tool Design** | 🟢 Action-based combined tools | 🟡 Individual tools for each operation | -| **Core Operations** | 🟢 All essential Appwrite features | 🟡 Database tools only (context limits) | -| **Messaging Service** | 🟢 Messages, topics, subscribers | 🟡 Selective enabling required | -| **Storage Operations** | 🟢 Complete file operations & uploads | 🟡 Basic bucket operations | +| **Core Operations** | 🟢 All essential database, user, storage, and team features | 🟡 Database tools only (context limits) | +| **Storage Operations** | 🟢 Complete file operations with download & advanced bucket config | 🟡 Basic bucket operations | | **User Management** | 🟢 CRUD + preferences + bulk operations | 🟡 Basic CRUD only | +| **Advanced Features** | 🟢 Document security, file encryption, array attributes, compression | 🔴 Limited | | **Bulk Operations** | 🟢 Integrated into respective categories | 🔴 Not available | -| **Error Handling** | 🟢 Comprehensive | 🟡 Basic | +| **Error Handling** | 🟢 Comprehensive with validation | 🟡 Basic | | **Language** | 🟡 TypeScript/Node.js | 🟢 Python | | **Maintenance** | 🟢 Easier with fewer tools | 🟡 Complex with many tools | @@ -372,18 +360,6 @@ Once integrated, you can use natural language commands with Claude to interact w -
-📧 Messaging & Communication - -``` -✨ "List all messaging topics" -✨ "Create a topic for user announcements" -✨ "Add subscribers to the announcements topic" -✨ "Send a message to all subscribers" -✨ "List all messages and their delivery status" -``` - -
diff --git a/src/index.ts b/src/index.ts index 87e7c02..f71bff0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,23 +8,22 @@ import { ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js"; -import { - Client, - Databases, - Users, - Storage, +import { + Client, + Databases, + Users, + Storage, Teams, Account, Health, - Messaging, - ID, - Query, - Permission, + ID, + Query, + Permission, Role, IndexType } from "node-appwrite"; import { InputFile } from "node-appwrite/file"; -import { readFileSync, existsSync } from "fs"; +import { readFileSync, writeFileSync, existsSync } from "fs"; import { join } from "path"; import * as dotenv from "dotenv"; @@ -43,7 +42,6 @@ class AppwriteMCPServer { private teams: Teams | null = null; private account: Account | null = null; private health: Health | null = null; - private messaging: Messaging | null = null; private config: AppwriteConfig | null = null; constructor() { @@ -99,7 +97,6 @@ class AppwriteMCPServer { this.teams = new Teams(this.client); this.account = new Account(this.client); this.health = new Health(this.client); - this.messaging = new Messaging(this.client); } private setupToolHandlers(): void { @@ -109,13 +106,14 @@ class AppwriteMCPServer { // Database Operations { name: "manage_database", - description: "Manage database operations (create, list, delete)", + description: "Manage database operations (create, get, list, update, delete)", inputSchema: { type: "object", properties: { - action: { type: "string", description: "Action to perform", enum: ["create", "list", "delete"] }, - databaseId: { type: "string", description: "ID of the database (required for delete, optional for create)" }, - name: { type: "string", description: "Name of the database (required for create)" } + action: { type: "string", description: "Action to perform", enum: ["create", "get", "list", "update", "delete"] }, + databaseId: { type: "string", description: "ID of the database (required for get/update/delete, optional for create)" }, + name: { type: "string", description: "Name of the database (required for create/update)" }, + enabled: { type: "boolean", description: "Enable or disable database (optional for create/update)" } }, required: ["action"] } @@ -132,7 +130,11 @@ class AppwriteMCPServer { databaseId: { type: "string", description: "ID of the database" }, collectionId: { type: "string", description: "ID of the collection (optional for create/list, required for get/update/delete)" }, name: { type: "string", description: "Collection name (required for create/update)" }, - permissions: { type: "array", description: "Collection permissions (optional)", items: { type: "string" } } + permissions: { type: "array", description: "Collection permissions (optional)", items: { type: "string" } }, + documentSecurity: { type: "boolean", description: "Enable document-level security (optional)" }, + enabled: { type: "boolean", description: "Enable or disable collection (optional)" }, + queries: { type: "array", description: "Query filters for list action (optional)", items: { type: "string" } }, + search: { type: "string", description: "Search term for filtering results (optional)" } }, required: ["action", "databaseId"] } @@ -149,13 +151,14 @@ class AppwriteMCPServer { databaseId: { type: "string", description: "ID of the database" }, collectionId: { type: "string", description: "ID of the collection" }, key: { type: "string", description: "Attribute key (required for create/get/update/delete)" }, - type: { - type: "string", + type: { + type: "string", description: "Attribute type (required for create/update)", enum: ["string", "integer", "float", "boolean", "datetime", "email", "ip", "url", "enum", "relationship"] }, required: { type: "boolean", description: "Is attribute required (required for create/update)" }, default: { type: "string", description: "Default value (optional)" }, + array: { type: "boolean", description: "Is attribute an array (optional, default: false)" }, size: { type: "number", description: "Size for string/ip/url attributes (optional)" }, min: { type: "number", description: "Minimum value for integer/float attributes (optional)" }, max: { type: "number", description: "Maximum value for integer/float attributes (optional)" }, @@ -179,13 +182,14 @@ class AppwriteMCPServer { type: "object", properties: { key: { type: "string", description: "Attribute key" }, - type: { - type: "string", + type: { + type: "string", description: "Attribute type", enum: ["string", "integer", "float", "boolean", "datetime", "email", "ip", "url", "enum", "relationship"] }, required: { type: "boolean", description: "Is attribute required" }, default: { type: "string", description: "Default value (optional)" }, + array: { type: "boolean", description: "Is attribute an array (optional, default: false)" }, size: { type: "number", description: "Size for string/ip/url attributes (optional)" }, min: { type: "number", description: "Minimum value for integer/float attributes (optional)" }, max: { type: "number", description: "Maximum value for integer/float attributes (optional)" }, @@ -380,7 +384,12 @@ class AppwriteMCPServer { name: { type: "string", description: "Bucket name (required for create/update)" }, permissions: { type: "array", description: "Bucket permissions (optional)", items: { type: "string" } }, fileSecurity: { type: "boolean", description: "Enable file security (optional)" }, - enabled: { type: "boolean", description: "Enable bucket (optional)" } + enabled: { type: "boolean", description: "Enable bucket (optional)" }, + maximumFileSize: { type: "number", description: "Maximum file size in bytes (optional, max 50MB)" }, + allowedFileExtensions: { type: "array", description: "Allowed file extensions (optional)", items: { type: "string" } }, + compression: { type: "string", description: "Compression algorithm (optional)", enum: ["none", "gzip", "zstd"] }, + encryption: { type: "boolean", description: "Enable encryption (optional)" }, + antivirus: { type: "boolean", description: "Enable antivirus scanning (optional)" } }, required: ["action"] } @@ -389,14 +398,15 @@ class AppwriteMCPServer { // File Operations { name: "file_operations", - description: "Manage file operations (get, create, update, delete, list)", + description: "Manage file operations (get, create, update, delete, list, download)", inputSchema: { type: "object", properties: { - action: { type: "string", description: "Action to perform", enum: ["get", "create", "update", "delete", "list"] }, + action: { type: "string", description: "Action to perform", enum: ["get", "create", "update", "delete", "list", "download"] }, bucketId: { type: "string", description: "Bucket ID" }, - fileId: { type: "string", description: "File ID (optional for create/list, required for get/update/delete)" }, + fileId: { type: "string", description: "File ID (optional for create/list, required for get/update/delete/download)" }, filePath: { type: "string", description: "Local file path to upload (required for create)" }, + downloadPath: { type: "string", description: "Local path to save downloaded file (required for download)" }, name: { type: "string", description: "File name (optional for update)" }, permissions: { type: "array", description: "File permissions (optional)", items: { type: "string" } } }, @@ -446,62 +456,7 @@ class AppwriteMCPServer { } }, - // Messaging Message Operations - { - name: "messaging_message_operations", - description: "Manage messaging message operations (create, get, list, update, delete)", - inputSchema: { - type: "object", - properties: { - action: { type: "string", description: "Action to perform", enum: ["create", "get", "list", "update", "delete"] }, - messageId: { type: "string", description: "Message ID (optional for create/list, required for get/update/delete)" }, - subject: { type: "string", description: "Message subject (optional)" }, - content: { type: "string", description: "Message content (required for create)" }, - topics: { type: "array", description: "Topic IDs (optional)", items: { type: "string" } }, - users: { type: "array", description: "User IDs (optional)", items: { type: "string" } }, - targets: { type: "array", description: "Target IDs (optional)", items: { type: "string" } }, - status: { type: "string", description: "Message status (optional for update)" }, - queries: { type: "array", description: "Query filters (optional for list)", items: { type: "string" } } - }, - required: ["action"] - } - }, - - // Messaging Topic Operations - { - name: "messaging_topic_operations", - description: "Manage messaging topic operations (create, get, list, update, delete)", - inputSchema: { - type: "object", - properties: { - action: { type: "string", description: "Action to perform", enum: ["create", "get", "list", "update", "delete"] }, - topicId: { type: "string", description: "Topic ID (optional for create/list, required for get/update/delete)" }, - name: { type: "string", description: "Topic name (required for create)" }, - description: { type: "string", description: "Topic description (optional)" }, - queries: { type: "array", description: "Query filters (optional for list)", items: { type: "string" } } - }, - required: ["action"] - } - }, - - // Messaging Subscriber Operations - { - name: "messaging_subscriber_operations", - description: "Manage messaging subscriber operations (create, get, list, delete)", - inputSchema: { - type: "object", - properties: { - action: { type: "string", description: "Action to perform", enum: ["create", "get", "list", "delete"] }, - topicId: { type: "string", description: "Topic ID" }, - subscriberId: { type: "string", description: "Subscriber ID (optional for create/list, required for get/delete)" }, - targetId: { type: "string", description: "Target ID (required for create)" }, - queries: { type: "array", description: "Query filters (optional for list)", items: { type: "string" } } - }, - required: ["action", "topicId"] - } - }, - - // Health Monitoring (kept separate) + // Health Monitoring { name: "get_health", description: "Get overall health status of Appwrite services", @@ -568,16 +523,6 @@ class AppwriteMCPServer { case "team_operations": return await this.teamOperations(request.params.arguments); - // Messaging Operations - case "messaging_message_operations": - return await this.messagingMessageOperations(request.params.arguments); - - case "messaging_topic_operations": - return await this.messagingTopicOperations(request.params.arguments); - - case "messaging_subscriber_operations": - return await this.messagingSubscriberOperations(request.params.arguments); - case "get_health": return await this.getHealth(); @@ -600,25 +545,40 @@ class AppwriteMCPServer { private async manageDatabaseOperations(args: any) { if (!this.databases) throw new Error("Databases not initialized"); - - const { action, databaseId, name } = args; + + const { action, databaseId, name, enabled } = args; switch (action) { case "create": if (!name) throw new Error("Name is required for create action"); const dbId = databaseId || ID.unique(); - const database = await this.databases.create(dbId, name); + const database = await this.databases.create(dbId, name, enabled); return { - content: [{ type: "text", text: `Database created successfully:\n- ID: ${database.$id}\n- Name: ${database.name}\n- Created: ${database.$createdAt}` }] + content: [{ type: "text", text: `Database created successfully:\n- ID: ${database.$id}\n- Name: ${database.name}\n- Enabled: ${database.enabled}\n- Created: ${database.$createdAt}` }] + }; + + case "get": + if (!databaseId) throw new Error("databaseId is required for get action"); + const getDb = await this.databases.get(databaseId); + return { + content: [{ type: "text", text: `Database Details:\n- ID: ${getDb.$id}\n- Name: ${getDb.name}\n- Enabled: ${getDb.enabled}\n- Created: ${getDb.$createdAt}\n- Updated: ${getDb.$updatedAt}` }] }; case "list": const databases = await this.databases.list(); - const dbList = databases.databases.map(db => `- ${db.name} (${db.$id})`).join('\n'); + const dbList = databases.databases.map(db => `- ${db.name} (${db.$id}) - ${db.enabled ? 'enabled' : 'disabled'}`).join('\n'); return { content: [{ type: "text", text: `Databases (${databases.total}):\n${dbList}` }] }; + case "update": + if (!databaseId) throw new Error("databaseId is required for update action"); + if (!name) throw new Error("name is required for update action"); + const updatedDb = await this.databases.update(databaseId, name, enabled); + return { + content: [{ type: "text", text: `Database updated successfully:\n- ID: ${updatedDb.$id}\n- Name: ${updatedDb.name}\n- Enabled: ${updatedDb.enabled}` }] + }; + case "delete": if (!databaseId) throw new Error("databaseId is required for delete action"); await this.databases.delete(databaseId); @@ -633,28 +593,38 @@ class AppwriteMCPServer { private async collectionOperations(args: any) { if (!this.databases) throw new Error("Databases not initialized"); - - const { action, databaseId, collectionId, name, permissions } = args; + + const { action, databaseId, collectionId, name, permissions, documentSecurity, enabled, queries, search } = args; switch (action) { case "create": if (!name) throw new Error("Name is required for create action"); const colId = collectionId || ID.unique(); - const collection = await this.databases.createCollection(databaseId, colId, name, permissions); + const collection = await this.databases.createCollection(databaseId, colId, name, permissions, documentSecurity, enabled); return { - content: [{ type: "text", text: `Collection created successfully:\n- ID: ${collection.$id}\n- Name: ${collection.name}\n- Database: ${collection.databaseId}` }] + content: [{ type: "text", text: `Collection created successfully:\n- ID: ${collection.$id}\n- Name: ${collection.name}\n- Database: ${collection.databaseId}\n- Document Security: ${collection.documentSecurity}\n- Enabled: ${collection.enabled}` }] }; case "get": if (!collectionId) throw new Error("collectionId is required for get action"); const getCollection = await this.databases.getCollection(databaseId, collectionId); return { - content: [{ type: "text", text: `Collection Details:\n- ID: ${getCollection.$id}\n- Name: ${getCollection.name}\n- Attributes: ${getCollection.attributes.length}\n- Indexes: ${getCollection.indexes.length}` }] + content: [{ type: "text", text: `Collection Details:\n- ID: ${getCollection.$id}\n- Name: ${getCollection.name}\n- Attributes: ${getCollection.attributes.length}\n- Indexes: ${getCollection.indexes.length}\n- Document Security: ${getCollection.documentSecurity}\n- Enabled: ${getCollection.enabled}` }] }; case "list": - const collections = await this.databases.listCollections(databaseId, [Query.limit(5000)]); - const colList = collections.collections.map(col => `- ${col.name} (${col.$id})`).join('\n'); + const parsedQueries: string[] = []; + if (queries && Array.isArray(queries)) { + for (const queryStr of queries) { + parsedQueries.push(this.parseQuery(queryStr)); + } + } + if (!parsedQueries.some(q => q.includes('limit'))) { + parsedQueries.push(Query.limit(5000)); + } + + const collections = await this.databases.listCollections(databaseId, parsedQueries, search); + const colList = collections.collections.map(col => `- ${col.name} (${col.$id}) - ${col.enabled ? 'enabled' : 'disabled'}`).join('\n'); return { content: [{ type: "text", text: `Collections in database ${databaseId} (${collections.total}):\n${colList}` }] }; @@ -662,9 +632,9 @@ class AppwriteMCPServer { case "update": if (!collectionId) throw new Error("collectionId is required for update action"); if (!name) throw new Error("Name is required for update action"); - const updatedCollection = await this.databases.updateCollection(databaseId, collectionId, name, permissions); + const updatedCollection = await this.databases.updateCollection(databaseId, collectionId, name, permissions, documentSecurity, enabled); return { - content: [{ type: "text", text: `Collection updated successfully:\n- ID: ${updatedCollection.$id}\n- Name: ${updatedCollection.name}` }] + content: [{ type: "text", text: `Collection updated successfully:\n- ID: ${updatedCollection.$id}\n- Name: ${updatedCollection.name}\n- Document Security: ${updatedCollection.documentSecurity}\n- Enabled: ${updatedCollection.enabled}` }] }; case "delete": @@ -1278,22 +1248,33 @@ class AppwriteMCPServer { private async bucketOperations(args: any) { if (!this.storage) throw new Error("Storage not initialized"); - - const { action, bucketId, name, permissions, fileSecurity, enabled } = args; + + const { action, bucketId, name, permissions, fileSecurity, enabled, maximumFileSize, allowedFileExtensions, compression, encryption, antivirus } = args; switch (action) { case "create": if (!name) throw new Error("name is required for create action"); - const bucket = await this.storage.createBucket(bucketId || ID.unique(), name, permissions, fileSecurity, enabled); + const bucket = await this.storage.createBucket( + bucketId || ID.unique(), + name, + permissions, + fileSecurity, + enabled, + maximumFileSize, + allowedFileExtensions, + compression, + encryption, + antivirus + ); return { - content: [{ type: "text", text: `Bucket created successfully:\n- ID: ${bucket.$id}\n- Name: ${bucket.name}\n- Enabled: ${bucket.enabled}` }] + content: [{ type: "text", text: `Bucket created successfully:\n- ID: ${bucket.$id}\n- Name: ${bucket.name}\n- Enabled: ${bucket.enabled}\n- File Security: ${bucket.fileSecurity}\n- Max File Size: ${bucket.maximumFileSize || 'unlimited'} bytes\n- Compression: ${bucket.compression}\n- Encryption: ${bucket.encryption}\n- Antivirus: ${bucket.antivirus}` }] }; case "get": if (!bucketId) throw new Error("bucketId is required for get action"); const getBucket = await this.storage.getBucket(bucketId); return { - content: [{ type: "text", text: `Bucket Details:\n- ID: ${getBucket.$id}\n- Name: ${getBucket.name}\n- Enabled: ${getBucket.enabled}\n- File Security: ${getBucket.fileSecurity}` }] + content: [{ type: "text", text: `Bucket Details:\n- ID: ${getBucket.$id}\n- Name: ${getBucket.name}\n- Enabled: ${getBucket.enabled}\n- File Security: ${getBucket.fileSecurity}\n- Max File Size: ${getBucket.maximumFileSize || 'unlimited'} bytes\n- Allowed Extensions: ${getBucket.allowedFileExtensions?.join(', ') || 'all'}\n- Compression: ${getBucket.compression}\n- Encryption: ${getBucket.encryption}\n- Antivirus: ${getBucket.antivirus}` }] }; case "list": @@ -1305,9 +1286,20 @@ class AppwriteMCPServer { case "update": if (!bucketId || !name) throw new Error("bucketId and name are required for update action"); - const updatedBucket = await this.storage.updateBucket(bucketId, name, permissions, fileSecurity, enabled); + const updatedBucket = await this.storage.updateBucket( + bucketId, + name, + permissions, + fileSecurity, + enabled, + maximumFileSize, + allowedFileExtensions, + compression, + encryption, + antivirus + ); return { - content: [{ type: "text", text: `Bucket updated successfully:\n- ID: ${updatedBucket.$id}\n- Name: ${updatedBucket.name}` }] + content: [{ type: "text", text: `Bucket updated successfully:\n- ID: ${updatedBucket.$id}\n- Name: ${updatedBucket.name}\n- Enabled: ${updatedBucket.enabled}` }] }; case "delete": @@ -1324,8 +1316,8 @@ class AppwriteMCPServer { private async fileOperations(args: any) { if (!this.storage) throw new Error("Storage not initialized"); - - const { action, bucketId, fileId, filePath, name, permissions } = args; + + const { action, bucketId, fileId, filePath, downloadPath, name, permissions } = args; switch (action) { case "get": @@ -1338,14 +1330,35 @@ class AppwriteMCPServer { case "create": if (!filePath) throw new Error("filePath is required for create action"); if (!existsSync(filePath)) throw new Error(`File not found: ${filePath}`); - + const fileBuffer = readFileSync(filePath); - const inputFile = InputFile.fromBuffer(fileBuffer, filePath.split('/').pop() || 'file'); + const inputFile = InputFile.fromBuffer(fileBuffer, filePath.split(/[/\\]/).pop() || 'file'); const createdFile = await this.storage.createFile(bucketId, fileId || ID.unique(), inputFile, permissions); return { content: [{ type: "text", text: `File uploaded successfully:\n- ID: ${createdFile.$id}\n- Name: ${createdFile.name}\n- Size: ${createdFile.sizeOriginal} bytes` }] }; + case "download": + if (!fileId) throw new Error("fileId is required for download action"); + if (!downloadPath) throw new Error("downloadPath is required for download action"); + + // Get file metadata first + const downloadFile = await this.storage.getFile(bucketId, fileId); + + // Get download URL and fetch the file + const downloadUrl = this.storage.getFileDownload(bucketId, fileId); + + // Use fetch to download the file + const response = await fetch(downloadUrl.toString()); + if (!response.ok) throw new Error(`Failed to download file: ${response.statusText}`); + + const buffer = Buffer.from(await response.arrayBuffer()); + writeFileSync(downloadPath, buffer); + + return { + content: [{ type: "text", text: `File downloaded successfully:\n- ID: ${downloadFile.$id}\n- Name: ${downloadFile.name}\n- Size: ${downloadFile.sizeOriginal} bytes\n- Saved to: ${downloadPath}` }] + }; + case "update": if (!fileId) throw new Error("fileId is required for update action"); const updatedFile = await this.storage.updateFile(bucketId, fileId, name, permissions); @@ -1447,121 +1460,6 @@ class AppwriteMCPServer { } } - private async messagingMessageOperations(args: any) { - if (!this.messaging) throw new Error("Messaging not initialized"); - - const { action, messageId, subject, content, topics, users, targets, status, queries } = args; - - switch (action) { - case "create": - if (!content) throw new Error("content is required for create action"); - // Note: Using a placeholder - actual Appwrite messaging API may differ - return { - content: [{ type: "text", text: `Message operation not fully implemented in current Appwrite SDK version` }] - }; - - case "get": - if (!messageId) throw new Error("messageId is required for get action"); - return { - content: [{ type: "text", text: `Message operation not fully implemented in current Appwrite SDK version` }] - }; - - case "list": - return { - content: [{ type: "text", text: `Message operation not fully implemented in current Appwrite SDK version` }] - }; - - case "update": - if (!messageId) throw new Error("messageId is required for update action"); - return { - content: [{ type: "text", text: `Message operation not fully implemented in current Appwrite SDK version` }] - }; - - case "delete": - if (!messageId) throw new Error("messageId is required for delete action"); - return { - content: [{ type: "text", text: `Message operation not fully implemented in current Appwrite SDK version` }] - }; - - default: - throw new Error(`Unknown action: ${action}`); - } - } - - private async messagingTopicOperations(args: any) { - if (!this.messaging) throw new Error("Messaging not initialized"); - - const { action, topicId, name, description, queries } = args; - - switch (action) { - case "create": - if (!name) throw new Error("name is required for create action"); - return { - content: [{ type: "text", text: `Topic operation not fully implemented in current Appwrite SDK version` }] - }; - - case "get": - if (!topicId) throw new Error("topicId is required for get action"); - return { - content: [{ type: "text", text: `Topic operation not fully implemented in current Appwrite SDK version` }] - }; - - case "list": - return { - content: [{ type: "text", text: `Topic operation not fully implemented in current Appwrite SDK version` }] - }; - - case "update": - if (!topicId) throw new Error("topicId is required for update action"); - return { - content: [{ type: "text", text: `Topic operation not fully implemented in current Appwrite SDK version` }] - }; - - case "delete": - if (!topicId) throw new Error("topicId is required for delete action"); - return { - content: [{ type: "text", text: `Topic operation not fully implemented in current Appwrite SDK version` }] - }; - - default: - throw new Error(`Unknown action: ${action}`); - } - } - - private async messagingSubscriberOperations(args: any) { - if (!this.messaging) throw new Error("Messaging not initialized"); - - const { action, topicId, subscriberId, targetId, queries } = args; - - switch (action) { - case "create": - if (!targetId) throw new Error("targetId is required for create action"); - return { - content: [{ type: "text", text: `Subscriber operation not fully implemented in current Appwrite SDK version` }] - }; - - case "get": - if (!subscriberId) throw new Error("subscriberId is required for get action"); - return { - content: [{ type: "text", text: `Subscriber operation not fully implemented in current Appwrite SDK version` }] - }; - - case "list": - return { - content: [{ type: "text", text: `Subscriber operation not fully implemented in current Appwrite SDK version` }] - }; - - case "delete": - if (!subscriberId) throw new Error("subscriberId is required for delete action"); - return { - content: [{ type: "text", text: `Subscriber operation not fully implemented in current Appwrite SDK version` }] - }; - - default: - throw new Error(`Unknown action: ${action}`); - } - } - private async getHealth() { if (!this.health) throw new Error("Health not initialized");