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
128 lines (127 loc) • 4.68 kB
JavaScript
import { api } from "./api-utils.js";
// Smart capability cache with field-level TTL
export class SmartCapabilityCache {
constructor() {
this.cache = new Map();
// TTL configuration based on VME resource change patterns
this.ttlConfig = {
zones: 3600, // Zones rarely change (1 hour)
clusters: 1800, // Cluster topology changes occasionally (30 min)
service_plans: 1800, // Plans change occasionally (30 min)
instance_types: 7200, // Instance types very stable (2 hours)
virtual_images: 900, // Images updated more frequently (15 min)
cluster_status: 300, // Node status changes often (5 min)
resource_pools: 1800, // Pool config changes occasionally (30 min)
networks: 3600, // Network config rarely changes (1 hour)
groups: 3600, // Groups rarely change (1 hour)
};
// Default TTL for unknown fields
this.defaultTTL = 600; // 10 minutes
}
/**
* Get data for a specific field, using cache if fresh or fetching if stale
*/
async getField(fieldName, forceRefresh = false) {
const cached = this.cache.get(fieldName);
const now = Math.floor(Date.now() / 1000);
// Check if cache is fresh and not forcing refresh
if (!forceRefresh && cached && this.isFresh(cached, now)) {
return cached.data;
}
// Fetch fresh data from VME API
const endpoint = this.getEndpointForField(fieldName);
try {
const response = await api.get(endpoint);
// Store in cache with appropriate TTL
const cacheEntry = {
data: response.data,
cached_at: now,
ttl_seconds: this.ttlConfig[fieldName] || this.defaultTTL,
endpoint: endpoint,
field_name: fieldName
};
this.cache.set(fieldName, cacheEntry);
return response.data;
}
catch (error) {
// If API fails and we have stale cache, return it with warning
if (cached) {
console.warn(`VME API failed for ${fieldName}, returning stale cache data`);
return cached.data;
}
// No cache and API failed - propagate error
throw new Error(`Failed to fetch ${fieldName}: ${error.message}`);
}
}
/**
* Get cache status for a specific field or all fields
*/
getCacheStatus(fieldName) {
const now = Math.floor(Date.now() / 1000);
const statuses = [];
if (fieldName) {
const cached = this.cache.get(fieldName);
if (cached) {
statuses.push(this.buildCacheStatus(cached, now));
}
}
else {
// Return status for all cached fields
for (const cached of this.cache.values()) {
statuses.push(this.buildCacheStatus(cached, now));
}
}
return statuses;
}
/**
* Invalidate cache for specific field or all fields
*/
invalidate(fieldName) {
if (fieldName) {
this.cache.delete(fieldName);
}
else {
this.cache.clear();
}
}
/**
* Get cache hit rate statistics
*/
getStatistics() {
// This would need hit/miss tracking - placeholder for now
return { hits: 0, misses: 0, hitRate: 0 };
}
// Private helper methods
isFresh(cached, now) {
return (now - cached.cached_at) < cached.ttl_seconds;
}
buildCacheStatus(cached, now) {
const age = now - cached.cached_at;
const expiresIn = cached.ttl_seconds - age;
return {
field_name: cached.field_name,
cached_at: cached.cached_at,
ttl_seconds: cached.ttl_seconds,
age_seconds: age,
is_fresh: this.isFresh(cached, now),
expires_in_seconds: Math.max(0, expiresIn),
endpoint: cached.endpoint
};
}
getEndpointForField(fieldName) {
// Map field names to VME API endpoints
const endpointMap = {
zones: "/zones",
clusters: "/clusters",
service_plans: "/service-plans",
instance_types: "/instance-types",
virtual_images: "/virtual-images",
resource_pools: "/resource-pools",
networks: "/networks",
groups: "/groups"
};
return endpointMap[fieldName] || `/${fieldName}`;
}
}
// Global cache instance
export const capabilityCache = new SmartCapabilityCache();