fix: query parsing, attribute size updates, and detailed attribute display
This commit is contained in:
148
src/index.ts
148
src/index.ts
@@ -735,8 +735,22 @@ class AppwriteMCPServer {
|
||||
case "get":
|
||||
if (!key) throw new Error("key is required for get action");
|
||||
const attribute = await this.databases.getAttribute(databaseId, collectionId, key);
|
||||
const attr = attribute as any;
|
||||
|
||||
// Build detailed attribute information
|
||||
let details = `Attribute Details:\n- Key: ${attr.key}\n- Type: ${attr.type}\n- Required: ${attr.required}\n- Status: ${attr.status}`;
|
||||
|
||||
// Add type-specific details
|
||||
if (attr.size !== undefined) details += `\n- Size: ${attr.size}`;
|
||||
if (attr.min !== undefined) details += `\n- Min: ${attr.min}`;
|
||||
if (attr.max !== undefined) details += `\n- Max: ${attr.max}`;
|
||||
if (attr.default !== undefined && attr.default !== null) details += `\n- Default: ${attr.default}`;
|
||||
if (attr.array !== undefined) details += `\n- Array: ${attr.array}`;
|
||||
if (attr.elements !== undefined) details += `\n- Elements: ${attr.elements.join(', ')}`;
|
||||
if (attr.format !== undefined) details += `\n- Format: ${attr.format}`;
|
||||
|
||||
return {
|
||||
content: [{ type: "text", text: `Attribute Details:\n- Key: ${(attribute as any).key}\n- Type: ${(attribute as any).type}\n- Required: ${(attribute as any).required}\n- Status: ${(attribute as any).status}` }]
|
||||
content: [{ type: "text", text: details }]
|
||||
};
|
||||
|
||||
case "list":
|
||||
@@ -753,7 +767,16 @@ class AppwriteMCPServer {
|
||||
let updateResult;
|
||||
switch (type) {
|
||||
case "string":
|
||||
updateResult = await this.databases.updateStringAttribute(databaseId, collectionId, key, required, otherArgs.default);
|
||||
// Appwrite API requires default param, use null for required attrs if not provided
|
||||
const strDefault = otherArgs.default !== undefined ? otherArgs.default : null;
|
||||
updateResult = await this.databases.updateStringAttribute(
|
||||
databaseId,
|
||||
collectionId,
|
||||
key,
|
||||
required,
|
||||
strDefault as any,
|
||||
otherArgs.size
|
||||
);
|
||||
break;
|
||||
case "integer":
|
||||
updateResult = await this.databases.updateIntegerAttribute(databaseId, collectionId, key, required, otherArgs.min, otherArgs.max, otherArgs.default);
|
||||
@@ -971,12 +994,127 @@ class AppwriteMCPServer {
|
||||
}
|
||||
}
|
||||
|
||||
private parseQuery(queryString: string): string {
|
||||
// Parse query strings like 'equal("field", "value")' into proper Query objects
|
||||
// This regex matches: functionName("field", value) or functionName("field", [values])
|
||||
const match = queryString.match(/^(\w+)\((.*)\)$/);
|
||||
if (!match) {
|
||||
throw new Error(`Invalid query format: ${queryString}. Expected format: equal("field", "value")`);
|
||||
}
|
||||
|
||||
const [, method, argsStr] = match;
|
||||
|
||||
// Parse arguments - handle quoted strings and arrays
|
||||
const args: any[] = [];
|
||||
let current = '';
|
||||
let inQuotes = false;
|
||||
let inArray = false;
|
||||
|
||||
for (let i = 0; i < argsStr.length; i++) {
|
||||
const char = argsStr[i];
|
||||
|
||||
if (char === '"' && argsStr[i - 1] !== '\\') {
|
||||
inQuotes = !inQuotes;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '[' && !inQuotes) {
|
||||
inArray = true;
|
||||
current += char;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === ']' && !inQuotes) {
|
||||
inArray = false;
|
||||
current += char;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === ',' && !inQuotes && !inArray) {
|
||||
args.push(current.trim());
|
||||
current = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
current += char;
|
||||
}
|
||||
|
||||
if (current.trim()) {
|
||||
args.push(current.trim());
|
||||
}
|
||||
|
||||
// Convert parsed arguments to proper types
|
||||
const processedArgs = args.map(arg => {
|
||||
// Handle arrays
|
||||
if (arg.startsWith('[') && arg.endsWith(']')) {
|
||||
const items = arg.slice(1, -1).split(',').map((item: string) => item.trim().replace(/^"(.*)"$/, '$1'));
|
||||
return items;
|
||||
}
|
||||
// Handle numbers
|
||||
if (/^-?\d+(\.\d+)?$/.test(arg)) {
|
||||
return Number(arg);
|
||||
}
|
||||
// Handle booleans
|
||||
if (arg === 'true') return true;
|
||||
if (arg === 'false') return false;
|
||||
// Handle strings (remove quotes)
|
||||
return arg.replace(/^"(.*)"$/, '$1');
|
||||
});
|
||||
|
||||
// Map to Query methods
|
||||
const queryMap: Record<string, (...args: any[]) => string> = {
|
||||
equal: Query.equal,
|
||||
notEqual: Query.notEqual,
|
||||
lessThan: Query.lessThan,
|
||||
lessThanEqual: Query.lessThanEqual,
|
||||
greaterThan: Query.greaterThan,
|
||||
greaterThanEqual: Query.greaterThanEqual,
|
||||
search: Query.search,
|
||||
isNull: Query.isNull,
|
||||
isNotNull: Query.isNotNull,
|
||||
between: Query.between,
|
||||
startsWith: Query.startsWith,
|
||||
endsWith: Query.endsWith,
|
||||
select: Query.select,
|
||||
orderDesc: Query.orderDesc,
|
||||
orderAsc: Query.orderAsc,
|
||||
limit: Query.limit,
|
||||
offset: Query.offset,
|
||||
contains: Query.contains,
|
||||
or: Query.or,
|
||||
and: Query.and,
|
||||
};
|
||||
|
||||
if (!queryMap[method]) {
|
||||
throw new Error(`Unknown query method: ${method}. Available methods: ${Object.keys(queryMap).join(', ')}`);
|
||||
}
|
||||
|
||||
return queryMap[method](...processedArgs);
|
||||
}
|
||||
|
||||
private async listDocuments(args: any) {
|
||||
if (!this.databases) throw new Error("Databases not initialized");
|
||||
|
||||
|
||||
const { databaseId, collectionId, queries, limit, offset } = args;
|
||||
const documents = await this.databases.listDocuments(databaseId, collectionId, queries || []);
|
||||
|
||||
|
||||
// Parse query strings into Query objects
|
||||
const parsedQueries: string[] = [];
|
||||
if (queries && Array.isArray(queries)) {
|
||||
for (const queryStr of queries) {
|
||||
try {
|
||||
parsedQueries.push(this.parseQuery(queryStr));
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse query "${queryStr}": ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add limit and offset if provided
|
||||
if (limit) parsedQueries.push(Query.limit(limit));
|
||||
if (offset) parsedQueries.push(Query.offset(offset));
|
||||
|
||||
const documents = await this.databases.listDocuments(databaseId, collectionId, parsedQueries);
|
||||
|
||||
const docList = documents.documents.map(doc => `- ${doc.$id} (updated: ${doc.$updatedAt})`).join('\n');
|
||||
return {
|
||||
content: [{ type: "text", text: `Documents in collection ${collectionId} (${documents.total}):\n${docList}` }]
|
||||
|
||||
Reference in New Issue
Block a user