From 62f7f125d35a67bd9a8f419278c6a9f760c534a5 Mon Sep 17 00:00:00 2001 From: smoido Date: Fri, 25 Jul 2025 16:43:30 +0300 Subject: [PATCH] bulk and auth functions --- README.md | 10 + src/index.ts | 602 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 612 insertions(+) diff --git a/README.md b/README.md index e309906..5a4b9e2 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,16 @@ A comprehensive Model Context Protocol (MCP) server that transforms Claude Code - **Index optimization** suggestions - **Schema migrations** with safety checks +**🔐 Advanced Authentication & Sessions:** +- **Session management** (create, delete, list user sessions) +- **Function execution** with logging and monitoring +- **Health monitoring** for all Appwrite services + +**⚡ Bulk Operations:** +- **Bulk user management** (create, update, delete multiple users) +- **Bulk document operations** (create, update, delete multiple documents) +- **Efficient batch processing** with detailed success/error reporting + ## Installation 1. Clone this repository: diff --git a/src/index.ts b/src/index.ts index ec115e3..abefd1d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,8 @@ import { Storage, Functions, Teams, + Account, + Health, ID, Query, Permission, @@ -39,6 +41,8 @@ class AppwriteMCPServer { private storage: Storage | null = null; private functions: Functions | null = null; private teams: Teams | null = null; + private account: Account | null = null; + private health: Health | null = null; private config: AppwriteConfig | null = null; constructor() { @@ -93,6 +97,8 @@ class AppwriteMCPServer { this.storage = new Storage(this.client); this.functions = new Functions(this.client); this.teams = new Teams(this.client); + this.account = new Account(this.client); + this.health = new Health(this.client); } private setupToolHandlers(): void { @@ -767,6 +773,239 @@ class AppwriteMCPServer { }, required: ["databaseId"] } + }, + + // Session Management + { + name: "create_session", + description: "Create a new user session with email and password", + inputSchema: { + type: "object", + properties: { + email: { type: "string", description: "User email" }, + password: { type: "string", description: "User password" } + }, + required: ["email", "password"] + } + }, + { + name: "delete_session", + description: "Delete a user session", + inputSchema: { + type: "object", + properties: { + sessionId: { type: "string", description: "Session ID to delete" } + }, + required: ["sessionId"] + } + }, + { + name: "list_sessions", + description: "List all sessions for a user", + inputSchema: { + type: "object", + properties: { + userId: { type: "string", description: "User ID" } + }, + required: ["userId"] + } + }, + + // Function Execution + { + name: "execute_function", + description: "Execute a function with optional data", + inputSchema: { + type: "object", + properties: { + functionId: { type: "string", description: "Function ID" }, + data: { type: "string", description: "Data to pass to function (optional)" }, + async: { type: "boolean", description: "Execute asynchronously (optional)", default: false } + }, + required: ["functionId"] + } + }, + { + name: "list_executions", + description: "List function executions with logs", + inputSchema: { + type: "object", + properties: { + functionId: { type: "string", description: "Function ID" }, + limit: { type: "number", description: "Number of executions to return (optional, max 100)", default: 25 } + }, + required: ["functionId"] + } + }, + { + name: "get_execution", + description: "Get details of a specific function execution", + inputSchema: { + type: "object", + properties: { + functionId: { type: "string", description: "Function ID" }, + executionId: { type: "string", description: "Execution ID" } + }, + required: ["functionId", "executionId"] + } + }, + + // Health Monitoring + { + name: "get_health", + description: "Get overall health status of Appwrite services", + inputSchema: { + type: "object", + properties: {} + } + }, + { + name: "get_health_db", + description: "Get database health status", + inputSchema: { + type: "object", + properties: {} + } + }, + { + name: "get_health_storage", + description: "Get storage health status", + inputSchema: { + type: "object", + properties: {} + } + }, + + // Bulk Operations + { + name: "bulk_create_users", + description: "Create multiple users at once", + inputSchema: { + type: "object", + properties: { + users: { + type: "array", + description: "Array of user objects to create", + items: { + type: "object", + properties: { + userId: { type: "string", description: "User ID (optional)" }, + email: { type: "string", description: "User email" }, + password: { type: "string", description: "User password" }, + name: { type: "string", description: "User name (optional)" } + }, + required: ["email", "password"] + } + } + }, + required: ["users"] + } + }, + { + name: "bulk_update_users", + description: "Update multiple users at once", + inputSchema: { + type: "object", + properties: { + updates: { + type: "array", + description: "Array of user updates", + items: { + type: "object", + properties: { + userId: { type: "string", description: "User ID" }, + email: { type: "string", description: "New email (optional)" }, + name: { type: "string", description: "New name (optional)" }, + password: { type: "string", description: "New password (optional)" } + }, + required: ["userId"] + } + } + }, + required: ["updates"] + } + }, + { + name: "bulk_delete_users", + description: "Delete multiple users at once", + inputSchema: { + type: "object", + properties: { + userIds: { + type: "array", + description: "Array of user IDs to delete", + items: { type: "string" } + } + }, + required: ["userIds"] + } + }, + { + name: "bulk_create_documents", + description: "Create multiple documents at once", + inputSchema: { + type: "object", + properties: { + databaseId: { type: "string", description: "ID of the database" }, + collectionId: { type: "string", description: "ID of the collection" }, + documents: { + type: "array", + description: "Array of documents to create", + items: { + type: "object", + properties: { + documentId: { type: "string", description: "Document ID (optional)" }, + data: { type: "object", description: "Document data" }, + permissions: { type: "array", description: "Document permissions (optional)", items: { type: "string" } } + }, + required: ["data"] + } + } + }, + required: ["databaseId", "collectionId", "documents"] + } + }, + { + name: "bulk_update_documents", + description: "Update multiple documents at once", + inputSchema: { + type: "object", + properties: { + databaseId: { type: "string", description: "ID of the database" }, + collectionId: { type: "string", description: "ID of the collection" }, + updates: { + type: "array", + description: "Array of document updates", + items: { + type: "object", + properties: { + documentId: { type: "string", description: "Document ID" }, + data: { type: "object", description: "Updated document data" }, + permissions: { type: "array", description: "Document permissions (optional)", items: { type: "string" } } + }, + required: ["documentId", "data"] + } + } + }, + required: ["databaseId", "collectionId", "updates"] + } + }, + { + name: "bulk_delete_documents", + description: "Delete multiple documents at once", + inputSchema: { + type: "object", + properties: { + databaseId: { type: "string", description: "ID of the database" }, + collectionId: { type: "string", description: "ID of the collection" }, + documentIds: { + type: "array", + description: "Array of document IDs to delete", + items: { type: "string" } + } + }, + required: ["databaseId", "collectionId", "documentIds"] + } } ] }; @@ -909,6 +1148,44 @@ class AppwriteMCPServer { case "usage_stats": return await this.usageStats(request.params.arguments); + // Session Management + case "create_session": + return await this.createSession(request.params.arguments); + case "delete_session": + return await this.deleteSession(request.params.arguments); + case "list_sessions": + return await this.listSessions(request.params.arguments); + + // Function Execution + case "execute_function": + return await this.executeFunction(request.params.arguments); + case "list_executions": + return await this.listExecutions(request.params.arguments); + case "get_execution": + return await this.getExecution(request.params.arguments); + + // Health Monitoring + case "get_health": + return await this.getHealth(); + case "get_health_db": + return await this.getHealthDb(); + case "get_health_storage": + return await this.getHealthStorage(); + + // Bulk Operations + case "bulk_create_users": + return await this.bulkCreateUsers(request.params.arguments); + case "bulk_update_users": + return await this.bulkUpdateUsers(request.params.arguments); + case "bulk_delete_users": + return await this.bulkDeleteUsers(request.params.arguments); + case "bulk_create_documents": + return await this.bulkCreateDocuments(request.params.arguments); + case "bulk_update_documents": + return await this.bulkUpdateDocuments(request.params.arguments); + case "bulk_delete_documents": + return await this.bulkDeleteDocuments(request.params.arguments); + default: throw new McpError( ErrorCode.MethodNotFound, @@ -3163,6 +3440,331 @@ For accurate usage tracking, consider: (avgSize / 1024).toFixed(1) + ' KB'; } + // Session Management + private async createSession(args: any) { + if (!this.account) throw new Error("Account service not initialized"); + + const { email, password } = args; + + const session = await this.account.createEmailPasswordSession(email, password); + + return { + content: [ + { + type: "text", + text: `Session created successfully:\n- Session ID: ${session.$id}\n- User ID: ${session.userId}\n- Created: ${session.$createdAt}\n- Expires: ${session.expire}` + } + ] + }; + } + + private async deleteSession(args: any) { + if (!this.account) throw new Error("Account service not initialized"); + + const { sessionId } = args; + + await this.account.deleteSession(sessionId); + + return { + content: [ + { + type: "text", + text: `Session ${sessionId} deleted successfully` + } + ] + }; + } + + private async listSessions(args: any) { + if (!this.users) throw new Error("Users service not initialized"); + + const { userId } = args; + + const sessions = await this.users.listSessions(userId); + + const sessionList = sessions.sessions.map((session: any) => + `- ${session.$id} - Created: ${session.$createdAt} - Expires: ${session.expire}` + ).join('\n'); + + return { + content: [ + { + type: "text", + text: `Sessions for user ${userId} (${sessions.total}):\n${sessionList}` + } + ] + }; + } + + // Function Execution + private async executeFunction(args: any) { + if (!this.functions) throw new Error("Functions service not initialized"); + + const { functionId, data, async } = args; + + const execution = await this.functions.createExecution(functionId, data, async); + + return { + content: [ + { + type: "text", + text: `Function execution ${async ? 'started' : 'completed'}:\n- Execution ID: ${execution.$id}\n- Function ID: ${execution.functionId}\n- Status: ${execution.status}\n- Response: ${execution.responseBody || 'No response'}\n- Duration: ${execution.duration}ms` + } + ] + }; + } + + private async listExecutions(args: any) { + if (!this.functions) throw new Error("Functions service not initialized"); + + const { functionId, limit = 25 } = args; + + const executions = await this.functions.listExecutions(functionId, [Query.limit(limit)]); + + const executionList = executions.executions.map((exec: any) => + `- ${exec.$id} - Status: ${exec.status} - Duration: ${exec.duration}ms - Created: ${exec.$createdAt}` + ).join('\n'); + + return { + content: [ + { + type: "text", + text: `Function executions for ${functionId} (${executions.total}):\n${executionList}` + } + ] + }; + } + + private async getExecution(args: any) { + if (!this.functions) throw new Error("Functions service not initialized"); + + const { functionId, executionId } = args; + + const execution = await this.functions.getExecution(functionId, executionId); + + return { + content: [ + { + type: "text", + text: `Execution Details:\n${JSON.stringify(execution, null, 2)}` + } + ] + }; + } + + // Health Monitoring + private async getHealth() { + if (!this.health) throw new Error("Health service not initialized"); + + const health = await this.health.get(); + + return { + content: [ + { + type: "text", + text: `Health Status: ${health.status}\n- Ping: ${health.ping}ms` + } + ] + }; + } + + private async getHealthDb() { + if (!this.health) throw new Error("Health service not initialized"); + + const health = await this.health.getDB(); + + return { + content: [ + { + type: "text", + text: `Database Health: ${health.status}\n- Ping: ${health.ping}ms` + } + ] + }; + } + + private async getHealthStorage() { + if (!this.health) throw new Error("Health service not initialized"); + + const health = await this.health.getStorage(); + + return { + content: [ + { + type: "text", + text: `Storage Health: ${health.status}\n- Ping: ${health.ping}ms` + } + ] + }; + } + + // Bulk Operations + private async bulkCreateUsers(args: any) { + if (!this.users) throw new Error("Users service not initialized"); + + const { users } = args; + const results = []; + const errors = []; + + for (const user of users) { + try { + const { userId, email, password, name } = user; + const uid = userId || ID.unique(); + const createdUser = await this.users.create(uid, email, password, name); + results.push(`✅ ${createdUser.email} (${createdUser.$id})`); + } catch (error) { + errors.push(`❌ ${user.email}: ${error}`); + } + } + + return { + content: [ + { + type: "text", + text: `Bulk User Creation Results:\n\nSuccessful (${results.length}):\n${results.join('\n')}\n\nFailed (${errors.length}):\n${errors.join('\n')}` + } + ] + }; + } + + private async bulkUpdateUsers(args: any) { + if (!this.users) throw new Error("Users service not initialized"); + + const { updates } = args; + const results = []; + const errors = []; + + for (const update of updates) { + try { + const { userId, email, name, password } = update; + + if (email) await this.users.updateEmail(userId, email); + if (name) await this.users.updateName(userId, name); + if (password) await this.users.updatePassword(userId, password); + + results.push(`✅ User ${userId} updated`); + } catch (error) { + errors.push(`❌ User ${update.userId}: ${error}`); + } + } + + return { + content: [ + { + type: "text", + text: `Bulk User Update Results:\n\nSuccessful (${results.length}):\n${results.join('\n')}\n\nFailed (${errors.length}):\n${errors.join('\n')}` + } + ] + }; + } + + private async bulkDeleteUsers(args: any) { + if (!this.users) throw new Error("Users service not initialized"); + + const { userIds } = args; + const results = []; + const errors = []; + + for (const userId of userIds) { + try { + await this.users.delete(userId); + results.push(`✅ User ${userId} deleted`); + } catch (error) { + errors.push(`❌ User ${userId}: ${error}`); + } + } + + return { + content: [ + { + type: "text", + text: `Bulk User Deletion Results:\n\nSuccessful (${results.length}):\n${results.join('\n')}\n\nFailed (${errors.length}):\n${errors.join('\n')}` + } + ] + }; + } + + private async bulkCreateDocuments(args: any) { + if (!this.databases) throw new Error("Databases not initialized"); + + const { databaseId, collectionId, documents } = args; + const results = []; + const errors = []; + + for (const doc of documents) { + try { + const { documentId, data, permissions } = doc; + const docId = documentId || ID.unique(); + const createdDoc = await this.databases.createDocument(databaseId, collectionId, docId, data, permissions); + results.push(`✅ Document ${createdDoc.$id} created`); + } catch (error) { + errors.push(`❌ Document: ${error}`); + } + } + + return { + content: [ + { + type: "text", + text: `Bulk Document Creation Results:\n\nSuccessful (${results.length}):\n${results.join('\n')}\n\nFailed (${errors.length}):\n${errors.join('\n')}` + } + ] + }; + } + + private async bulkUpdateDocuments(args: any) { + if (!this.databases) throw new Error("Databases not initialized"); + + const { databaseId, collectionId, updates } = args; + const results = []; + const errors = []; + + for (const update of updates) { + try { + const { documentId, data, permissions } = update; + await this.databases.updateDocument(databaseId, collectionId, documentId, data, permissions); + results.push(`✅ Document ${documentId} updated`); + } catch (error) { + errors.push(`❌ Document ${update.documentId}: ${error}`); + } + } + + return { + content: [ + { + type: "text", + text: `Bulk Document Update Results:\n\nSuccessful (${results.length}):\n${results.join('\n')}\n\nFailed (${errors.length}):\n${errors.join('\n')}` + } + ] + }; + } + + private async bulkDeleteDocuments(args: any) { + if (!this.databases) throw new Error("Databases not initialized"); + + const { databaseId, collectionId, documentIds } = args; + const results = []; + const errors = []; + + for (const documentId of documentIds) { + try { + await this.databases.deleteDocument(databaseId, collectionId, documentId); + results.push(`✅ Document ${documentId} deleted`); + } catch (error) { + errors.push(`❌ Document ${documentId}: ${error}`); + } + } + + return { + content: [ + { + type: "text", + text: `Bulk Document Deletion Results:\n\nSuccessful (${results.length}):\n${results.join('\n')}\n\nFailed (${errors.length}):\n${errors.join('\n')}` + } + ] + }; + } + async run(): Promise { const transport = new StdioServerTransport(); await this.server.connect(transport);