vme-mcp-server
Version:
An intelligent Model Context Protocol (MCP) server that transforms HPE VM Essentials (VME) infrastructure management into natural language conversations. Provision VMs, manage resources, and control your infrastructure through simple English commands with
171 lines (170 loc) • 6.96 kB
JavaScript
import { capabilityDiscovery } from "./capability-discovery.js";
// Generic name->ID resolver using capability discovery data
export class NameResolver {
constructor() {
this.capabilitiesCache = null;
this.cacheTimestamp = 0;
this.CACHE_TTL = 300000; // 5 minutes
}
async getCapabilities() {
const now = Date.now();
if (!this.capabilitiesCache || (now - this.cacheTimestamp) > this.CACHE_TTL) {
this.capabilitiesCache = await capabilityDiscovery.discoverCapabilities(['all']);
this.cacheTimestamp = now;
}
return this.capabilitiesCache;
}
async resolveNameToId(resourceType, name) {
if (!name)
return null;
const capabilities = await this.getCapabilities();
const nameLower = name.toLowerCase();
switch (resourceType) {
case 'virtualImage':
case 'image':
return this.resolveImageName(capabilities, nameLower);
case 'group':
case 'site':
return this.resolveGroupName(capabilities, nameLower);
case 'servicePlan':
case 'plan':
return this.resolvePlanName(capabilities, nameLower);
case 'zone':
case 'cloud':
return this.resolveZoneName(capabilities, nameLower);
case 'instanceType':
return this.resolveInstanceTypeName(capabilities, nameLower);
default:
return null;
}
}
resolveImageName(capabilities, nameLower) {
const images = capabilities.compute?.available_images || [];
// Try exact name match first
let found = images.find((img) => img.name?.toLowerCase() === nameLower);
if (found)
return found.id;
// Try partial name match
found = images.find((img) => img.name?.toLowerCase().includes(nameLower));
if (found)
return found.id;
// Try category + version matching (e.g., "ubuntu 20.04" -> ubuntu-2004)
for (const img of images) {
const imgName = img.name?.toLowerCase() || '';
const category = img.category?.toLowerCase() || '';
const version = img.version?.toString() || '';
// Match patterns like "ubuntu 20.04" to "ubuntu-2004"
if (nameLower.includes(category) && nameLower.includes(version)) {
return img.id;
}
// Match patterns like "ubuntu" to first ubuntu image
if (nameLower === category) {
return img.id;
}
}
return null;
}
resolveGroupName(capabilities, nameLower) {
const groups = capabilities.platform?.groups || [];
// Try exact match
let found = groups.find((g) => g.name?.toLowerCase() === nameLower);
if (found)
return found.id;
// Try partial match
found = groups.find((g) => g.name?.toLowerCase().includes(nameLower));
if (found)
return found.id;
return null;
}
resolvePlanName(capabilities, nameLower) {
const plans = capabilities.compute?.service_plans || [];
// Try exact match
let found = plans.find((p) => p.name?.toLowerCase() === nameLower);
if (found)
return found.id;
// Try size-based matching
if (nameLower.includes('small') || nameLower.includes('1 cpu') || nameLower.includes('4gb')) {
found = plans.find((p) => p.name?.toLowerCase().includes('1 cpu') ||
p.name?.toLowerCase().includes('4gb') ||
p.max_cpu === 1);
if (found)
return found.id;
}
if (nameLower.includes('medium') || nameLower.includes('2 cpu') || nameLower.includes('8gb')) {
found = plans.find((p) => p.name?.toLowerCase().includes('2 cpu') ||
p.name?.toLowerCase().includes('8gb') ||
p.max_cpu === 2);
if (found)
return found.id;
}
if (nameLower.includes('large') || nameLower.includes('4 cpu') || nameLower.includes('16gb')) {
found = plans.find((p) => p.name?.toLowerCase().includes('4 cpu') ||
p.name?.toLowerCase().includes('16gb') ||
p.max_cpu === 4);
if (found)
return found.id;
}
// Try partial match
found = plans.find((p) => p.name?.toLowerCase().includes(nameLower));
if (found)
return found.id;
return null;
}
resolveZoneName(capabilities, nameLower) {
const zones = capabilities.networking?.zones || [];
// Try exact match
let found = zones.find((z) => z.name?.toLowerCase() === nameLower);
if (found)
return found.id;
// Try partial match
found = zones.find((z) => z.name?.toLowerCase().includes(nameLower));
if (found)
return found.id;
return null;
}
resolveInstanceTypeName(capabilities, nameLower) {
const instanceTypes = capabilities.compute?.instance_types || [];
// Try exact match
let found = instanceTypes.find((t) => t.name?.toLowerCase() === nameLower);
if (found)
return found.id;
// Try code match
found = instanceTypes.find((t) => t.code?.toLowerCase() === nameLower);
if (found)
return found.id;
// Try partial match
found = instanceTypes.find((t) => t.name?.toLowerCase().includes(nameLower));
if (found)
return found.id;
return null;
}
// Helper method to get available names for a resource type
async getAvailableNames(resourceType) {
const capabilities = await this.getCapabilities();
switch (resourceType) {
case 'virtualImage':
return capabilities.compute?.available_images?.map((img) => img.name) || [];
case 'group':
return capabilities.platform?.groups?.map((g) => g.name) || [];
case 'servicePlan':
return capabilities.compute?.service_plans?.map((p) => p.name) || [];
case 'zone':
return capabilities.networking?.zones?.map((z) => z.name) || [];
case 'instanceType':
return capabilities.compute?.instance_types?.map((t) => t.name) || [];
default:
return [];
}
}
// Helper to resolve multiple resources at once
async resolveMultiple(resources) {
const results = {};
for (const resource of resources) {
const key = `${resource.type}_${resource.name}`;
results[key] = await this.resolveNameToId(resource.type, resource.name);
}
return results;
}
}
// Global resolver instance
export const nameResolver = new NameResolver();