Cloudzero Model Context Protocol Server
by burkestar
Verified
from typing import Dict, List, Optional
import httpx
from pydantic import BaseModel, Field
from mcp.server.fastmcp import Context, FastMCP
import os
from dotenv import load_dotenv
from contextlib import asynccontextmanager
from dataclasses import dataclass
from typing import AsyncIterator
# Load environment variables
load_dotenv()
class CostQuery(BaseModel):
start_date: Optional[str] = Field(None, description="Start date for cost query")
end_date: Optional[str] = Field(None, description="End date for cost query")
class CloudZeroAPI:
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.cloudzero.com/v2"
self.client = httpx.AsyncClient()
self.headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
async def make_request(
self,
method: str,
endpoint: str,
data: Optional[Dict] = None,
params: Optional[Dict] = None
) -> Dict:
url = f"{self.base_url}/{endpoint}"
response = await self.client.request(
method=method,
url=url,
headers=self.headers,
json=data,
params=params
)
response.raise_for_status()
return response.json()
async def close(self):
await self.client.aclose()
@dataclass
class AppContext:
api: CloudZeroAPI
@asynccontextmanager
async def app_lifespan(server: FastMCP) -> AsyncIterator[AppContext]:
"""Server lifespan context manager"""
# Initialize API client
api = CloudZeroAPI(api_key=os.getenv("CLOUDZERO_API_KEY", ""))
try:
# Test connection
await api.make_request("GET", "billing/dimensions")
yield AppContext(api=api)
finally:
# Cleanup
if api:
await api.close()
mcp = FastMCP("CloudZero", dependencies=[], lifespan=app_lifespan)
@mcp.tool()
async def get_costs(ctx: Context, start_date: Optional[str] = None, end_date: Optional[str] = None) -> Dict:
"""Get billing costs for a specified date range"""
api = ctx.request_context.lifespan_context.api
if not api:
raise RuntimeError("CloudZero API client not initialized")
params = {"start_date": start_date, "end_date": end_date}
return await api.make_request("GET", "billing/costs", params=params)
@mcp.tool()
async def get_dimensions(ctx: Context) -> Dict:
"""Get billing dimensions"""
api = ctx.request_context.lifespan_context.api
if not api:
raise RuntimeError("CloudZero API client not initialized")
return await api.make_request("GET", "billing/dimensions")
@mcp.tool()
async def list_budgets(ctx: Context) -> List[Dict]:
"""List all budgets"""
api = ctx.request_context.lifespan_context.api
if not api:
raise RuntimeError("CloudZero API client not initialized")
return await api.make_request("GET", "budgets")
@mcp.tool()
async def list_insights(ctx: Context) -> List[Dict]:
"""List all insights"""
api = ctx.request_context.lifespan_context.api
if not api:
raise RuntimeError("CloudZero API client not initialized")
return await api.make_request("GET", "insights")
@mcp.prompt()
async def analyze_costs(ctx: Context, period: str) -> str:
"""Create a cost analysis prompt for a specific period"""
return f"""Please analyze the costs for {period} and provide insights on:
1. Overall spending patterns
2. Major cost centers
3. Potential cost optimization opportunities
4. Budget compliance"""