elasticsearch-mcp
Version:
Secure MCP server for Elasticsearch integration with comprehensive tools and Elastic Cloud support
140 lines • 6.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CreateIndexTool = void 0;
const schemas_js_1 = require("../validation/schemas.js");
const handlers_js_1 = require("../errors/handlers.js");
class CreateIndexTool {
elasticsearch;
logger;
constructor(elasticsearch, logger) {
this.elasticsearch = elasticsearch;
this.logger = logger.child({ tool: 'create-index' });
}
async execute(args) {
try {
const validatedArgs = schemas_js_1.CreateIndexArgsSchema.parse(args);
this.logger.info('Creating index', {
name: validatedArgs.name,
hasMappings: !!validatedArgs.mappings,
hasSettings: !!validatedArgs.settings,
aliasCount: validatedArgs.aliases?.length || 0,
});
const client = this.elasticsearch.getClient();
// Check if index already exists
const exists = await client.indices.exists({
index: validatedArgs.name,
});
if (exists) {
throw new handlers_js_1.ValidationError(`Index '${validatedArgs.name}' already exists`);
}
// Prepare index body
const indexBody = {};
if (validatedArgs.mappings) {
indexBody.mappings = this.validateMappings(validatedArgs.mappings);
}
if (validatedArgs.settings) {
indexBody.settings = this.validateSettings(validatedArgs.settings);
}
if (validatedArgs.aliases && validatedArgs.aliases.length > 0) {
indexBody.aliases = this.buildAliases(validatedArgs.aliases);
}
// Create the index
const response = await client.indices.create({
index: validatedArgs.name,
body: indexBody,
});
this.logger.info('Successfully created index', {
name: validatedArgs.name,
acknowledged: response.acknowledged,
shardsAcknowledged: response.shards_acknowledged,
});
return {
acknowledged: response.acknowledged,
index: validatedArgs.name,
shardsAcknowledged: response.shards_acknowledged,
};
}
catch (error) {
if (error instanceof Error && error.name === 'ZodError') {
throw new handlers_js_1.ValidationError('Invalid arguments for create_index', {
details: error.message,
});
}
if (error instanceof handlers_js_1.ValidationError) {
throw error;
}
this.logger.error('Failed to create index', {}, error);
throw new handlers_js_1.ElasticsearchError('Failed to create index in Elasticsearch', error, { args });
}
}
validateMappings(mappings) {
// Basic validation for mappings structure
if (typeof mappings !== 'object' || Array.isArray(mappings)) {
throw new handlers_js_1.ValidationError('Mappings must be a valid object');
}
// Check for common mapping structure
if (mappings.properties && typeof mappings.properties !== 'object') {
throw new handlers_js_1.ValidationError('Mappings properties must be an object');
}
// Validate property types
if (mappings.properties) {
this.validateMappingProperties(mappings.properties);
}
return mappings;
}
validateMappingProperties(properties) {
const validFieldTypes = [
'text', 'keyword', 'integer', 'long', 'float', 'double', 'boolean',
'date', 'ip', 'geo_point', 'geo_shape', 'nested', 'object', 'binary',
'integer_range', 'float_range', 'long_range', 'double_range', 'date_range', 'ip_range',
];
for (const [fieldName, fieldConfig] of Object.entries(properties)) {
if (typeof fieldConfig === 'object' && fieldConfig !== null) {
const config = fieldConfig;
if (config.type && typeof config.type === 'string') {
if (!validFieldTypes.includes(config.type)) {
throw new handlers_js_1.ValidationError(`Invalid field type '${config.type}' for field '${fieldName}'`);
}
}
// Recursively validate nested properties
if (config.properties) {
this.validateMappingProperties(config.properties);
}
}
}
}
validateSettings(settings) {
// Basic validation for settings structure
if (typeof settings !== 'object' || Array.isArray(settings)) {
throw new handlers_js_1.ValidationError('Settings must be a valid object');
}
// Validate critical settings
if (settings.number_of_shards && typeof settings.number_of_shards === 'number') {
if (settings.number_of_shards < 1 || settings.number_of_shards > 1024) {
throw new handlers_js_1.ValidationError('Number of shards must be between 1 and 1024');
}
}
if (settings.number_of_replicas && typeof settings.number_of_replicas === 'number') {
if (settings.number_of_replicas < 0 || settings.number_of_replicas > 10) {
throw new handlers_js_1.ValidationError('Number of replicas must be between 0 and 10');
}
}
return settings;
}
buildAliases(aliases) {
const aliasObject = {};
for (const alias of aliases) {
if (typeof alias !== 'string' || alias.trim().length === 0) {
throw new handlers_js_1.ValidationError('All aliases must be non-empty strings');
}
// Validate alias name
if (alias.includes(' ') || alias.includes('\t') || alias.includes('\n')) {
throw new handlers_js_1.ValidationError(`Invalid alias name '${alias}': cannot contain whitespace`);
}
aliasObject[alias.trim()] = {};
}
return aliasObject;
}
}
exports.CreateIndexTool = CreateIndexTool;
//# sourceMappingURL=create-index.js.map