This commit is contained in:
2025-07-25 17:45:41 +03:00
parent 62f7f125d3
commit 76f1180cde

View File

@@ -401,6 +401,7 @@ class AppwriteMCPServer {
properties: { properties: {
userId: { type: "string", description: "Unique user ID (optional)" }, userId: { type: "string", description: "Unique user ID (optional)" },
email: { type: "string", description: "User email" }, email: { type: "string", description: "User email" },
phone: { type: "string", description: "User phone number in international format (optional, e.g., '+1234567890')" },
password: { type: "string", description: "User password" }, password: { type: "string", description: "User password" },
name: { type: "string", description: "User name (optional)" } name: { type: "string", description: "User name (optional)" }
}, },
@@ -891,6 +892,7 @@ class AppwriteMCPServer {
properties: { properties: {
userId: { type: "string", description: "User ID (optional)" }, userId: { type: "string", description: "User ID (optional)" },
email: { type: "string", description: "User email" }, email: { type: "string", description: "User email" },
phone: { type: "string", description: "User phone number (optional)" },
password: { type: "string", description: "User password" }, password: { type: "string", description: "User password" },
name: { type: "string", description: "User name (optional)" } name: { type: "string", description: "User name (optional)" }
}, },
@@ -1533,6 +1535,17 @@ class AppwriteMCPServer {
const { databaseId, collectionId, documentId, data, permissions } = args; const { databaseId, collectionId, documentId, data, permissions } = args;
const docId = documentId || ID.unique(); const docId = documentId || ID.unique();
// Validate document against collection schema
try {
const validation = await this.validateDocumentData(databaseId, collectionId, data);
if (!validation.isValid) {
throw new Error(`Document validation failed: ${validation.errors.join(', ')}`);
}
} catch (validationError) {
// If validation fails, provide helpful error message
throw new Error(`Schema validation error: ${validationError}. Use list_attributes to check collection schema.`);
}
const document = await this.databases.createDocument( const document = await this.databases.createDocument(
databaseId, databaseId,
collectionId, collectionId,
@@ -1629,10 +1642,10 @@ class AppwriteMCPServer {
private async createUser(args: any) { private async createUser(args: any) {
if (!this.users) throw new Error("Users service not initialized"); if (!this.users) throw new Error("Users service not initialized");
const { userId, email, password, name } = args; const { userId, email, phone, password, name } = args;
const uid = userId || ID.unique(); const uid = userId || ID.unique();
const user = await this.users.create(uid, email, password, name); const user = await this.users.create(uid, email, phone, password, name);
return { return {
content: [ content: [
@@ -2371,6 +2384,71 @@ ${attributes.attributes.map((attr: any) =>
}; };
} }
// Helper method for createDocument validation
private async validateDocumentData(databaseId: string, collectionId: string, documentData: any) {
if (!this.databases) throw new Error("Databases not initialized");
// Get collection schema
const attributes = await this.databases.listAttributes(databaseId, collectionId);
const attributeMap = new Map(attributes.attributes.map((attr: any) => [attr.key, attr]));
const errors: string[] = [];
// Check required fields
attributes.attributes.forEach((attr: any) => {
if (attr.required && !(attr.key in documentData)) {
errors.push(`Missing required field: ${attr.key} (${attr.type})`);
}
});
// Check field types and constraints
Object.keys(documentData).forEach(key => {
const attr = attributeMap.get(key);
const value = documentData[key];
if (!attr) {
errors.push(`Unknown field: ${key} (not in collection schema)`);
return;
}
// Type validation
switch (attr.type) {
case 'string':
if (typeof value !== 'string') {
errors.push(`Field ${key}: expected string, got ${typeof value}`);
} else if (attr.size && value.length > attr.size) {
errors.push(`Field ${key}: string too long (${value.length} > ${attr.size})`);
}
break;
case 'integer':
if (!Number.isInteger(value)) {
errors.push(`Field ${key}: expected integer, got ${typeof value}`);
}
break;
case 'boolean':
if (typeof value !== 'boolean') {
errors.push(`Field ${key}: expected boolean, got ${typeof value}`);
}
break;
case 'email':
if (typeof value !== 'string' || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
errors.push(`Field ${key}: invalid email format`);
}
break;
case 'datetime':
if (isNaN(Date.parse(value))) {
errors.push(`Field ${key}: invalid datetime format`);
}
break;
}
});
return {
isValid: errors.length === 0,
errors: errors
};
}
private async schemaMigration(args: any) { private async schemaMigration(args: any) {
if (!this.databases) throw new Error("Databases not initialized"); if (!this.databases) throw new Error("Databases not initialized");
@@ -3608,9 +3686,9 @@ For accurate usage tracking, consider:
for (const user of users) { for (const user of users) {
try { try {
const { userId, email, password, name } = user; const { userId, email, phone, password, name } = user;
const uid = userId || ID.unique(); const uid = userId || ID.unique();
const createdUser = await this.users.create(uid, email, password, name); const createdUser = await this.users.create(uid, email, phone, password, name);
results.push(`${createdUser.email} (${createdUser.$id})`); results.push(`${createdUser.email} (${createdUser.$id})`);
} catch (error) { } catch (error) {
errors.push(`${user.email}: ${error}`); errors.push(`${user.email}: ${error}`);