UNPKG

elasticsearch-mcp

Version:

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

135 lines 5.72 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InsertDataTool = void 0; const schemas_js_1 = require("../validation/schemas.js"); const handlers_js_1 = require("../errors/handlers.js"); class InsertDataTool { elasticsearch; logger; constructor(elasticsearch, logger) { this.elasticsearch = elasticsearch; this.logger = logger.child({ tool: 'insert-data' }); } async execute(args) { try { const validatedArgs = schemas_js_1.InsertDataArgsSchema.parse(args); this.logger.info('Inserting document', { index: validatedArgs.index, hasId: !!validatedArgs.id, documentKeys: Object.keys(validatedArgs.document), refresh: validatedArgs.refresh, }); const client = this.elasticsearch.getClient(); // Validate document content this.validateDocument(validatedArgs.document); // Check if index exists const indexExists = await client.indices.exists({ index: validatedArgs.index, }); if (!indexExists) { this.logger.warn('Index does not exist, it will be created automatically', { index: validatedArgs.index, }); } // Prepare the request const request = { index: validatedArgs.index, body: validatedArgs.document, refresh: this.normalizeRefreshParameter(validatedArgs.refresh), }; let response; if (validatedArgs.id) { // Use index API for specific ID request.id = validatedArgs.id; response = await client.index(request); } else { // Use create API to auto-generate ID response = await client.index(request); } this.logger.info('Successfully inserted document', { id: response._id, index: response._index, version: response._version, result: response.result, }); return { _id: response._id, _index: response._index, _version: response._version, result: response.result, }; } catch (error) { if (error instanceof Error && error.name === 'ZodError') { throw new handlers_js_1.ValidationError('Invalid arguments for insert_data', { details: error.message, }); } this.logger.error('Failed to insert document', {}, error); throw new handlers_js_1.ElasticsearchError('Failed to insert document into Elasticsearch', error, { args }); } } validateDocument(document) { // Check if document is empty if (Object.keys(document).length === 0) { throw new handlers_js_1.ValidationError('Document cannot be empty'); } // Check for invalid field names for (const key of Object.keys(document)) { if (key.startsWith('_')) { throw new handlers_js_1.ValidationError(`Field name '${key}' cannot start with underscore (reserved)`); } if (key.includes('.') && !this.isValidDottedField(key)) { throw new handlers_js_1.ValidationError(`Invalid field name '${key}': improper dot notation`); } if (key.length > 256) { throw new handlers_js_1.ValidationError(`Field name '${key}' exceeds maximum length of 256 characters`); } } // Validate document size (rough estimate) const documentSize = JSON.stringify(document).length; if (documentSize > 100 * 1024 * 1024) { // 100MB limit throw new handlers_js_1.ValidationError('Document size exceeds 100MB limit'); } // Recursively validate nested objects this.validateNestedObject(document, 0); } isValidDottedField(fieldName) { // Check for valid dot notation (no consecutive dots, no leading/trailing dots) return !/^\.|\.$|\.\./.test(fieldName); } validateNestedObject(obj, depth) { const MAX_NESTING_DEPTH = 20; if (depth > MAX_NESTING_DEPTH) { throw new handlers_js_1.ValidationError(`Document nesting exceeds maximum depth of ${MAX_NESTING_DEPTH}`); } for (const [key, value] of Object.entries(obj)) { if (typeof value === 'object' && value !== null && !Array.isArray(value)) { this.validateNestedObject(value, depth + 1); } // Check for extremely large arrays if (Array.isArray(value) && value.length > 10000) { throw new handlers_js_1.ValidationError(`Array field '${key}' contains too many elements (max 10,000)`); } // Check for very long strings if (typeof value === 'string' && value.length > 1024 * 1024) { // 1MB throw new handlers_js_1.ValidationError(`String field '${key}' exceeds maximum length of 1MB`); } } } 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.InsertDataTool = InsertDataTool; //# sourceMappingURL=insert-data.js.map