import type { Tool } from '@modelcontextprotocol/sdk/types.js';
import { CostExplorerClient, GetCostAndUsageCommand } from '@aws-sdk/client-cost-explorer';
import { credentialManager } from '../utils/credential-manager.js';
import { Formatters } from '../utils/formatters.js';
import type { CloudProvider, CostAnalysis } from '../types/index.js';
export const costAnalysisTools: Tool[] = [
{
name: 'analyze_costs',
description: 'Analyze cloud costs for AWS, Azure, or GCP',
inputSchema: {
type: 'object',
properties: {
provider: {
type: 'string',
enum: ['aws', 'azure', 'gcp'],
description: 'Cloud provider',
},
startDate: {
type: 'string',
description: 'Start date (YYYY-MM-DD)',
},
endDate: {
type: 'string',
description: 'End date (YYYY-MM-DD)',
},
granularity: {
type: 'string',
enum: ['daily', 'monthly'],
description: 'Cost granularity',
default: 'monthly',
},
},
required: ['provider', 'startDate', 'endDate'],
},
},
{
name: 'get_cost_by_service',
description: 'Get cost breakdown by service',
inputSchema: {
type: 'object',
properties: {
provider: {
type: 'string',
enum: ['aws', 'azure', 'gcp'],
description: 'Cloud provider',
},
startDate: {
type: 'string',
description: 'Start date (YYYY-MM-DD)',
},
endDate: {
type: 'string',
description: 'End date (YYYY-MM-DD)',
},
},
required: ['provider', 'startDate', 'endDate'],
},
},
{
name: 'estimate_monthly_cost',
description: 'Estimate monthly cost based on current usage',
inputSchema: {
type: 'object',
properties: {
provider: {
type: 'string',
enum: ['aws', 'azure', 'gcp'],
description: 'Cloud provider',
},
},
required: ['provider'],
},
},
];
export async function handleCostAnalysisTool(name: string, args: unknown): Promise<unknown> {
const params = args as Record<string, unknown>;
const provider = params.provider as CloudProvider;
switch (name) {
case 'analyze_costs': {
const startDate = params.startDate as string;
const endDate = params.endDate as string;
const granularity = (params.granularity as string) || 'monthly';
if (provider === 'aws') {
return await analyzeAWSCosts(startDate, endDate, granularity);
} else if (provider === 'azure') {
// Azure cost analysis would go here
return { message: 'Azure cost analysis not yet fully implemented' };
} else if (provider === 'gcp') {
// GCP cost analysis would go here
return { message: 'GCP cost analysis not yet fully implemented' };
}
throw new Error(`Unsupported provider: ${provider}`);
}
case 'get_cost_by_service': {
const startDate = params.startDate as string;
const endDate = params.endDate as string;
if (provider === 'aws') {
return await getAWSCostByService(startDate, endDate);
}
return { message: `Cost by service not yet implemented for ${provider}` };
}
case 'estimate_monthly_cost': {
// Simplified estimation
return {
provider,
estimatedMonthlyCost: 'N/A - Requires actual usage data',
message: 'Monthly cost estimation requires detailed usage metrics',
};
}
default:
throw new Error(`Unknown cost analysis tool: ${name}`);
}
}
async function analyzeAWSCosts(startDate: string, endDate: string, granularity: string): Promise<string> {
try {
const credentials = await credentialManager.getCredentials('aws');
if (!credentials) {
throw new Error('AWS credentials not found');
}
const client = new CostExplorerClient({
region: 'us-east-1', // Cost Explorer is global
credentials: credentials.accessKeyId && credentials.secretAccessKey
? {
accessKeyId: credentials.accessKeyId,
secretAccessKey: credentials.secretAccessKey,
}
: undefined,
});
const command = new GetCostAndUsageCommand({
TimePeriod: {
Start: startDate,
End: endDate,
},
Granularity: granularity === 'daily' ? 'DAILY' : 'MONTHLY',
Metrics: ['BlendedCost', 'UnblendedCost'],
GroupBy: [{ Type: 'DIMENSION', Key: 'SERVICE' }],
});
const response = await client.send(command);
const costAnalysis: CostAnalysis = {
provider: 'aws',
period: {
start: new Date(startDate),
end: new Date(endDate),
},
totalCost: 0,
currency: 'USD',
breakdown: [],
trends: [],
};
if (response.ResultsByTime && response.ResultsByTime.length > 0) {
for (const result of response.ResultsByTime) {
const timePeriod = result.TimePeriod;
if (timePeriod && result.Groups) {
for (const group of result.Groups) {
const cost = parseFloat(group.Metrics?.BlendedCost?.Amount || '0');
costAnalysis.totalCost += cost;
const serviceName = group.Keys?.[0] || 'Unknown';
costAnalysis.breakdown.push({
service: serviceName,
cost,
percentage: 0, // Will calculate after
});
if (timePeriod.Start) {
costAnalysis.trends?.push({
date: new Date(timePeriod.Start),
cost,
service: serviceName,
});
}
}
}
}
// Calculate percentages
if (costAnalysis.totalCost > 0) {
for (const item of costAnalysis.breakdown) {
item.percentage = (item.cost / costAnalysis.totalCost) * 100;
}
}
}
return Formatters.formatCostAnalysis(costAnalysis);
} catch (error) {
throw new Error(`Failed to analyze AWS costs: ${error instanceof Error ? error.message : String(error)}`);
}
}
async function getAWSCostByService(startDate: string, endDate: string): Promise<unknown> {
try {
const analysis = await analyzeAWSCosts(startDate, endDate, 'monthly');
// Parse the formatted analysis to extract service breakdown
return {
breakdown: JSON.parse(analysis),
message: 'Cost breakdown by service retrieved',
};
} catch (error) {
throw new Error(`Failed to get cost by service: ${error instanceof Error ? error.message : String(error)}`);
}
}