UNPKG

elasticsearch-mcp

Version:

Secure MCP server for Elasticsearch integration with comprehensive tools and Elastic Cloud support

164 lines 6.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DeleteDocumentTool = void 0; const schemas_js_1 = require("../validation/schemas.js"); const handlers_js_1 = require("../errors/handlers.js"); class DeleteDocumentTool { elasticsearch; logger; constructor(elasticsearch, logger) { this.elasticsearch = elasticsearch; this.logger = logger.child({ tool: 'delete-document' }); } async execute(args) { try { const validatedArgs = schemas_js_1.DeleteDocumentArgsSchema.parse(args); this.logger.info('Deleting document(s)', { index: validatedArgs.index, hasId: !!validatedArgs.id, hasQuery: !!validatedArgs.query, conflicts: validatedArgs.conflicts, refresh: validatedArgs.refresh, }); const client = this.elasticsearch.getClient(); // Check if index exists const indexExists = await client.indices.exists({ index: validatedArgs.index, }); if (!indexExists) { throw new handlers_js_1.NotFoundError(`Index '${validatedArgs.index}' does not exist`); } if (validatedArgs.id) { // Delete by ID return await this.deleteById(validatedArgs); } else if (validatedArgs.query) { // Delete by query return await this.deleteByQuery(validatedArgs); } else { throw new handlers_js_1.ValidationError('Either id or query must be provided'); } } catch (error) { if (error instanceof Error && error.name === 'ZodError') { throw new handlers_js_1.ValidationError('Invalid arguments for delete_document', { details: error.message, }); } if (error instanceof handlers_js_1.ValidationError || error instanceof handlers_js_1.NotFoundError) { throw error; } this.logger.error('Failed to delete document(s)', {}, error); throw new handlers_js_1.ElasticsearchError('Failed to delete document(s) from Elasticsearch', error, { args }); } } async deleteById(args) { const client = this.elasticsearch.getClient(); try { const response = await client.delete({ index: args.index, id: args.id, refresh: this.normalizeRefreshParameter(args.refresh), }); this.logger.info('Successfully deleted document by ID', { id: args.id, index: args.index, version: response._version, result: response.result, }); return { deleted: response.result === 'deleted' ? 1 : 0, tookMs: 0, // Single document delete doesn't provide timing timedOut: false, }; } catch (error) { if (error instanceof Error) { const errorMessage = error.message.toLowerCase(); if (errorMessage.includes('not_found') || errorMessage.includes('document_missing')) { throw new handlers_js_1.NotFoundError(`Document with ID '${args.id}' not found in index '${args.index}'`); } } throw error; } } async deleteByQuery(args) { const client = this.elasticsearch.getClient(); // Sanitize and validate the query const sanitizedQuery = (0, schemas_js_1.sanitizeQuery)(args.query); if (!sanitizedQuery) { throw new handlers_js_1.ValidationError('Query cannot be empty'); } // Validate query structure this.validateQuery(sanitizedQuery); const deleteRequest = { index: args.index, body: { query: sanitizedQuery, }, refresh: this.normalizeRefreshParameter(args.refresh), conflicts: args.conflicts || 'abort', timeout: '5m', // 5 minute timeout wait_for_completion: true, }; const response = await client.deleteByQuery(deleteRequest); this.logger.info('Successfully executed delete by query', { index: args.index, deleted: response.deleted, versionConflicts: response.version_conflicts, took: response.took, }); return { deleted: response.deleted || 0, versionConflicts: response.version_conflicts || 0, noops: response.noops || 0, retries: { bulk: response.retries?.bulk || 0, search: response.retries?.search || 0, }, tookMs: response.took || 0, timedOut: response.timed_out || false, }; } validateQuery(query) { // Check for potentially dangerous queries const queryStr = JSON.stringify(query).toLowerCase(); // Prevent match_all without limits (could delete entire index) if (queryStr.includes('match_all') && !queryStr.includes('size') && !queryStr.includes('from')) { this.logger.warn('Detected match_all query without size limit', { query }); // Allow but log warning - user should be aware } // Check for script queries (potential security risk) if (queryStr.includes('script_score') || queryStr.includes('script_query')) { throw new handlers_js_1.ValidationError('Script-based queries are not allowed in delete operations'); } // Validate query depth this.validateQueryDepth(query, 0); } validateQueryDepth(obj, depth) { const MAX_QUERY_DEPTH = 20; if (depth > MAX_QUERY_DEPTH) { throw new handlers_js_1.ValidationError(`Query nesting exceeds maximum depth of ${MAX_QUERY_DEPTH}`); } for (const value of Object.values(obj)) { if (typeof value === 'object' && value !== null && !Array.isArray(value)) { this.validateQueryDepth(value, depth + 1); } } } normalizeRefreshParameter(refresh) { if (refresh === undefined || refresh === false || refresh === 'false') { return false; } if (refresh === true || refresh === 'true') { return true; } if (refresh === 'wait_for') { return 'wait_for'; } return false; } } exports.DeleteDocumentTool = DeleteDocumentTool; //# sourceMappingURL=delete-document.js.map