bulk and auth functions
This commit is contained in:
10
README.md
10
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:
|
||||
|
||||
602
src/index.ts
602
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<void> {
|
||||
const transport = new StdioServerTransport();
|
||||
await this.server.connect(transport);
|
||||
|
||||
Reference in New Issue
Block a user