From 5f7fc55d0945229f4277b953db1cbd4cdc772bd6 Mon Sep 17 00:00:00 2001 From: smoido Date: Sun, 27 Jul 2025 15:36:30 +0300 Subject: [PATCH] added general attribute creation tool, bulk attribute creatin, bulk attribute removal, user authentication --- README.md | 33 +++-- src/index.ts | 391 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 278 insertions(+), 146 deletions(-) diff --git a/README.md b/README.md index 67b6e67..64ce21d 100644 --- a/README.md +++ b/README.md @@ -197,15 +197,11 @@ Once integrated, you should be able to use commands like:
-🏷️ Attribute Management (7 tools) +🏷️ Attribute Management (3 tools) | Tool | Description | |------|-------------| -| `create_string_attribute` | Create a string attribute in a collection | -| `create_integer_attribute` | Create an integer attribute in a collection | -| `create_boolean_attribute` | Create a boolean attribute in a collection | -| `create_email_attribute` | Create an email attribute in a collection | -| `create_datetime_attribute` | Create a datetime attribute in a collection | +| `create_attribute` | ✨ Create any attribute type (string, integer, float, boolean, datetime, email, IP, URL, enum, relationship) | | `list_attributes` | List all attributes in a collection | | `delete_attribute` | Delete an attribute from a collection | @@ -236,17 +232,19 @@ Once integrated, you should be able to use commands like:
-👥 User Management (7 tools) +👥 User Management (9 tools) | Tool | Description | |------|-------------| | `create_user` | Create a new user | -| `list_users` | List all users | +| `list_users` | List all users ✨ *with verification status* | | `get_user` | Get a user by ID | | `update_user_email` | Update user email | | `update_user_name` | Update user name | | `update_user_password` | Update user password | | `delete_user` | Delete a user | +| `verify_user_email` | ✨ Verify a user's email address | +| `unverify_user_email` | ✨ Unverify a user's email address |
@@ -352,10 +350,12 @@ Once integrated, you should be able to use commands like:
-⚡ Bulk Operations (6 tools) +⚡ Bulk Operations (8 tools) | Tool | Description | |------|-------------| +| `bulk_create_attributes` | ✨ Create multiple attributes at once in a collection | +| `bulk_delete_attributes` | ✨ Delete multiple attributes at once from a collection | | `bulk_create_users` | ✨ Create multiple users at once | | `bulk_update_users` | ✨ Update multiple users at once | | `bulk_delete_users` | ✨ Delete multiple users at once | @@ -412,11 +412,14 @@ Once integrated, you can use natural language commands with Claude to interact w 🏷️ Schema & Attributes ``` -✨ "Add a string attribute called 'name' to the products collection with max size 255" -✨ "Create a boolean attribute 'in_stock' in the products collection" +✨ "Create a string attribute called 'name' in the products collection with max size 255" +✨ "Add a boolean attribute 'in_stock' to the products collection" +✨ "Create an enum attribute 'status' with values active, inactive, pending" +✨ "Add a relationship attribute linking products to categories" +✨ "Bulk create attributes: name (string), price (float), in_stock (boolean)" +✨ "Bulk delete attributes: old_field1, deprecated_field2, unused_field3" ✨ "List all attributes in the products collection" ✨ "Create a unique index on the 'sku' attribute" -✨ "Add an email attribute for 'contact_email'" ```
@@ -441,6 +444,8 @@ Once integrated, you can use natural language commands with Claude to interact w ✨ "Create a new user with email john@example.com and password SecurePass123" ✨ "List all users in the project" ✨ "Update user xyz123's name to 'John Doe'" +✨ "Verify user abc123's email address" +✨ "Unverify user xyz456's email verification" ✨ "Create a session for user john@example.com" ✨ "Bulk create 10 test users for demo purposes" ``` @@ -451,6 +456,10 @@ Once integrated, you can use natural language commands with Claude to interact w ⚡ Advanced Features (Exclusive) ``` +🚀 "Create a complete product schema with string, enum, float, and relationship attributes" +🚀 "Bulk create 10 attributes at once for my e-commerce collection" +🚀 "Add a relationship attribute linking orders to customers with two-way binding" +🚀 "Create an enum attribute for user roles with admin, user, moderator values" 🚀 "Analyze this sample data and create a collection schema automatically" 🚀 "Suggest optimal indexes for my users collection based on usage" 🚀 "Validate this document data before creating it" diff --git a/src/index.ts b/src/index.ts index a84e299..ef69159 100644 --- a/src/index.ts +++ b/src/index.ts @@ -176,81 +176,102 @@ class AppwriteMCPServer { // Attribute Operations { - name: "create_string_attribute", - description: "Create a string attribute in a collection", + name: "create_attribute", + description: "Create any type of attribute in a collection (string, integer, float, boolean, datetime, email, IP, URL, enum, relationship)", inputSchema: { type: "object", properties: { databaseId: { type: "string", description: "ID of the database" }, collectionId: { type: "string", description: "ID of the collection" }, key: { type: "string", description: "Attribute key" }, - size: { type: "number", description: "Maximum size of the 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)" } + default: { type: "string", description: "Default value (optional)" }, + 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)" }, + elements: { + type: "array", + items: { type: "string" }, + description: "Array of allowed values for enum attributes (required for enum type)" + }, + relatedCollection: { type: "string", description: "Related collection ID for relationship attributes (required for relationship type)" }, + relationType: { + type: "string", + description: "Relationship type (required for relationship type)", + enum: ["oneToOne", "oneToMany", "manyToOne", "manyToMany"] + }, + twoWay: { type: "boolean", description: "Is relationship two-way (optional for relationship type)" }, + twoWayKey: { type: "string", description: "Two-way relationship key (optional for relationship type)" } }, - required: ["databaseId", "collectionId", "key", "size", "required"] + required: ["databaseId", "collectionId", "key", "type", "required"] } }, { - name: "create_integer_attribute", - description: "Create an integer attribute in a collection", + name: "bulk_create_attributes", + description: "Create multiple attributes at once in a collection", inputSchema: { type: "object", properties: { databaseId: { type: "string", description: "ID of the database" }, collectionId: { type: "string", description: "ID of the collection" }, - key: { type: "string", description: "Attribute key" }, - required: { type: "boolean", description: "Is attribute required" }, - min: { type: "number", description: "Minimum value (optional)" }, - max: { type: "number", description: "Maximum value (optional)" }, - default: { type: "number", description: "Default value (optional)" } + attributes: { + type: "array", + description: "Array of attribute definitions", + items: { + type: "object", + properties: { + key: { type: "string", description: "Attribute key" }, + 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)" }, + 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)" }, + elements: { + type: "array", + items: { type: "string" }, + description: "Array of allowed values for enum attributes (required for enum type)" + }, + relatedCollection: { type: "string", description: "Related collection ID for relationship attributes (required for relationship type)" }, + relationType: { + type: "string", + description: "Relationship type (required for relationship type)", + enum: ["oneToOne", "oneToMany", "manyToOne", "manyToMany"] + }, + twoWay: { type: "boolean", description: "Is relationship two-way (optional for relationship type)" }, + twoWayKey: { type: "string", description: "Two-way relationship key (optional for relationship type)" } + }, + required: ["key", "type", "required"] + } + } }, - required: ["databaseId", "collectionId", "key", "required"] + required: ["databaseId", "collectionId", "attributes"] } }, { - name: "create_boolean_attribute", - description: "Create a boolean attribute in a collection", + name: "bulk_delete_attributes", + description: "Delete multiple attributes at once from a collection", inputSchema: { type: "object", properties: { databaseId: { type: "string", description: "ID of the database" }, collectionId: { type: "string", description: "ID of the collection" }, - key: { type: "string", description: "Attribute key" }, - required: { type: "boolean", description: "Is attribute required" }, - default: { type: "boolean", description: "Default value (optional)" } + attributeKeys: { + type: "array", + description: "Array of attribute keys to delete", + items: { type: "string" } + } }, - required: ["databaseId", "collectionId", "key", "required"] - } - }, - { - name: "create_email_attribute", - description: "Create an email attribute in a collection", - inputSchema: { - type: "object", - properties: { - databaseId: { type: "string", description: "ID of the database" }, - collectionId: { type: "string", description: "ID of the collection" }, - key: { type: "string", description: "Attribute key" }, - required: { type: "boolean", description: "Is attribute required" }, - default: { type: "string", description: "Default value (optional)" } - }, - required: ["databaseId", "collectionId", "key", "required"] - } - }, - { - name: "create_datetime_attribute", - description: "Create a datetime attribute in a collection", - inputSchema: { - type: "object", - properties: { - databaseId: { type: "string", description: "ID of the database" }, - collectionId: { type: "string", description: "ID of the collection" }, - key: { type: "string", description: "Attribute key" }, - required: { type: "boolean", description: "Is attribute required" }, - default: { type: "string", description: "Default value (optional)" } - }, - required: ["databaseId", "collectionId", "key", "required"] + required: ["databaseId", "collectionId", "attributeKeys"] } }, { @@ -476,6 +497,28 @@ class AppwriteMCPServer { required: ["userId"] } }, + { + name: "verify_user_email", + description: "Verify a user's email address", + inputSchema: { + type: "object", + properties: { + userId: { type: "string", description: "User ID" } + }, + required: ["userId"] + } + }, + { + name: "unverify_user_email", + description: "Unverify a user's email address", + inputSchema: { + type: "object", + properties: { + userId: { type: "string", description: "User ID" } + }, + required: ["userId"] + } + }, // Storage Operations { @@ -1040,16 +1083,12 @@ class AppwriteMCPServer { return await this.deleteCollection(request.params.arguments); // Attribute Operations - case "create_string_attribute": - return await this.createStringAttribute(request.params.arguments); - case "create_integer_attribute": - return await this.createIntegerAttribute(request.params.arguments); - case "create_boolean_attribute": - return await this.createBooleanAttribute(request.params.arguments); - case "create_email_attribute": - return await this.createEmailAttribute(request.params.arguments); - case "create_datetime_attribute": - return await this.createDatetimeAttribute(request.params.arguments); + case "create_attribute": + return await this.createAttribute(request.params.arguments); + case "bulk_create_attributes": + return await this.bulkCreateAttributes(request.params.arguments); + case "bulk_delete_attributes": + return await this.bulkDeleteAttributes(request.params.arguments); case "list_attributes": return await this.listAttributes(request.params.arguments); case "delete_attribute": @@ -1090,6 +1129,10 @@ class AppwriteMCPServer { return await this.updateUserPassword(request.params.arguments); case "delete_user": return await this.deleteUser(request.params.arguments); + case "verify_user_email": + return await this.verifyUserEmail(request.params.arguments); + case "unverify_user_email": + return await this.unverifyUserEmail(request.params.arguments); // Storage Operations case "create_bucket": @@ -1316,119 +1359,165 @@ class AppwriteMCPServer { } // Attribute Operations - private async createStringAttribute(args: any) { + + private async createAttribute(args: any) { if (!this.databases) throw new Error("Databases not initialized"); - const { databaseId, collectionId, key, size, required, default: defaultValue } = args; - - const attribute = await this.databases.createStringAttribute( + const { databaseId, collectionId, key, - size, + type, required, - defaultValue - ); + default: defaultValue, + size, + min, + max, + elements, + relatedCollection, + relationType, + twoWay, + twoWayKey + } = args; + + let attribute; + + switch (type) { + case 'string': + attribute = await this.databases.createStringAttribute( + databaseId, collectionId, key, size || 255, required, defaultValue + ); + break; + case 'integer': + attribute = await this.databases.createIntegerAttribute( + databaseId, collectionId, key, required, min, max, defaultValue + ); + break; + case 'float': + attribute = await this.databases.createFloatAttribute( + databaseId, collectionId, key, required, min, max, defaultValue + ); + break; + case 'boolean': + attribute = await this.databases.createBooleanAttribute( + databaseId, collectionId, key, required, defaultValue + ); + break; + case 'datetime': + attribute = await this.databases.createDatetimeAttribute( + databaseId, collectionId, key, required, defaultValue + ); + break; + case 'email': + attribute = await this.databases.createEmailAttribute( + databaseId, collectionId, key, required, defaultValue + ); + break; + case 'ip': + attribute = await this.databases.createIpAttribute( + databaseId, collectionId, key, required, defaultValue + ); + break; + case 'url': + attribute = await this.databases.createUrlAttribute( + databaseId, collectionId, key, required, defaultValue + ); + break; + case 'enum': + if (!elements || elements.length === 0) { + throw new Error("Enum attributes require 'elements' array with allowed values"); + } + attribute = await this.databases.createEnumAttribute( + databaseId, collectionId, key, elements, required, defaultValue + ); + break; + case 'relationship': + if (!relatedCollection || !relationType) { + throw new Error("Relationship attributes require 'relatedCollection' and 'relationType'"); + } + attribute = await this.databases.createRelationshipAttribute( + databaseId, collectionId, relatedCollection, relationType, twoWay, key, twoWayKey + ); + break; + default: + throw new Error(`Unsupported attribute type: ${type}`); + } return { content: [ { type: "text", - text: `String attribute '${key}' created successfully in collection ${collectionId}` + text: `${type.charAt(0).toUpperCase() + type.slice(1)} attribute '${key}' created successfully in collection ${collectionId}` } ] }; } - private async createIntegerAttribute(args: any) { + private async bulkCreateAttributes(args: any) { if (!this.databases) throw new Error("Databases not initialized"); - const { databaseId, collectionId, key, required, min, max, default: defaultValue } = args; + const { databaseId, collectionId, attributes } = args; - const attribute = await this.databases.createIntegerAttribute( - databaseId, - collectionId, - key, - required, - min, - max, - defaultValue - ); + const results = []; + const errors = []; + + for (const attr of attributes) { + try { + await this.createAttribute({ + databaseId, + collectionId, + ...attr + }); + results.push(`✅ ${attr.key} (${attr.type})`); + } catch (error) { + errors.push(`❌ ${attr.key} (${attr.type}): ${error}`); + } + } return { content: [ { type: "text", - text: `Integer attribute '${key}' created successfully in collection ${collectionId}` + text: `Bulk Attribute Creation Results: + +Successful (${results.length}): +${results.join('\n')} + +${errors.length > 0 ? `Failed (${errors.length}): +${errors.join('\n')}` : ''}` } ] }; } - private async createBooleanAttribute(args: any) { + private async bulkDeleteAttributes(args: any) { if (!this.databases) throw new Error("Databases not initialized"); - const { databaseId, collectionId, key, required, default: defaultValue } = args; + const { databaseId, collectionId, attributeKeys } = args; - const attribute = await this.databases.createBooleanAttribute( - databaseId, - collectionId, - key, - required, - defaultValue - ); + const results = []; + const errors = []; + + for (const key of attributeKeys) { + try { + await this.databases.deleteAttribute(databaseId, collectionId, key); + results.push(`✅ ${key}`); + } catch (error) { + errors.push(`❌ ${key}: ${error}`); + } + } return { content: [ { type: "text", - text: `Boolean attribute '${key}' created successfully in collection ${collectionId}` - } - ] - }; - } + text: `Bulk Attribute Deletion Results: - private async createEmailAttribute(args: any) { - if (!this.databases) throw new Error("Databases not initialized"); - - const { databaseId, collectionId, key, required, default: defaultValue } = args; - - const attribute = await this.databases.createEmailAttribute( - databaseId, - collectionId, - key, - required, - defaultValue - ); - - return { - content: [ - { - type: "text", - text: `Email attribute '${key}' created successfully in collection ${collectionId}` - } - ] - }; - } +Successful (${results.length}): +${results.join('\n')} - private async createDatetimeAttribute(args: any) { - if (!this.databases) throw new Error("Databases not initialized"); - - const { databaseId, collectionId, key, required, default: defaultValue } = args; - - const attribute = await this.databases.createDatetimeAttribute( - databaseId, - collectionId, - key, - required, - defaultValue - ); - - return { - content: [ - { - type: "text", - text: `Datetime attribute '${key}' created successfully in collection ${collectionId}` +${errors.length > 0 ? `Failed (${errors.length}): +${errors.join('\n')}` : ''}` } ] }; @@ -1665,7 +1754,7 @@ class AppwriteMCPServer { const users = await this.users.list(queries || []); const userList = users.users.map((user: any) => - `- ${user.name || 'No name'} (${user.email}) - ID: ${user.$id}` + `- ${user.name || 'No name'} (${user.email}) - ID: ${user.$id} - ${user.emailVerification ? '✅ Verified' : '❌ Unverified'}` ).join('\n'); return { @@ -1763,6 +1852,40 @@ class AppwriteMCPServer { }; } + private async verifyUserEmail(args: any) { + if (!this.users) throw new Error("Users service not initialized"); + + const { userId } = args; + + await this.users.updateEmailVerification(userId, true); + + return { + content: [ + { + type: "text", + text: `User ${userId} email verification status updated to verified ✅` + } + ] + }; + } + + private async unverifyUserEmail(args: any) { + if (!this.users) throw new Error("Users service not initialized"); + + const { userId } = args; + + await this.users.updateEmailVerification(userId, false); + + return { + content: [ + { + type: "text", + text: `User ${userId} email verification status updated to unverified ❌` + } + ] + }; + } + // Storage Operations private async createBucket(args: any) { if (!this.storage) throw new Error("Storage service not initialized");