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":
|
case "get":
|
||||||
if (!key) throw new Error("key is required for get action");
|
if (!key) throw new Error("key is required for get action");
|
||||||
const attribute = await this.databases.getAttribute(databaseId, collectionId, key);
|
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 {
|
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":
|
case "list":
|
||||||
@@ -753,7 +767,16 @@ class AppwriteMCPServer {
|
|||||||
let updateResult;
|
let updateResult;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "string":
|
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;
|
break;
|
||||||
case "integer":
|
case "integer":
|
||||||
updateResult = await this.databases.updateIntegerAttribute(databaseId, collectionId, key, required, otherArgs.min, otherArgs.max, otherArgs.default);
|
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) {
|
private async listDocuments(args: any) {
|
||||||
if (!this.databases) throw new Error("Databases not initialized");
|
if (!this.databases) throw new Error("Databases not initialized");
|
||||||
|
|
||||||
const { databaseId, collectionId, queries, limit, offset } = args;
|
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');
|
const docList = documents.documents.map(doc => `- ${doc.$id} (updated: ${doc.$updatedAt})`).join('\n');
|
||||||
return {
|
return {
|
||||||
content: [{ type: "text", text: `Documents in collection ${collectionId} (${documents.total}):\n${docList}` }]
|
content: [{ type: "text", text: `Documents in collection ${collectionId} (${documents.total}):\n${docList}` }]
|
||||||
|
|||||||
Reference in New Issue
Block a user