We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ilvolodel/iris-legacy'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Calendar operations for Microsoft Graph API."""
from typing import Dict, List, Any
from datetime import datetime
from .base import BaseOperation, OperationError
from ..graph_client import MicrosoftGraphClient
class CalendarOperations(BaseOperation):
"""Operations for calendar and event management."""
def get_supported_actions(self) -> List[str]:
"""Return supported calendar actions."""
return [
"list", "get", "create", "update", "delete",
"find_times", "get_schedule"
]
def _validate_action_params(self, action: str, params: Dict) -> None:
"""Validate parameters for calendar actions."""
# access_token is required for all actions (from TrustyVault)
self._require_param(params, "access_token", str)
self._require_param(params, "microsoft_user", str)
if action == "list":
if "start_date" in params:
self._validate_datetime(params["start_date"], "start_date")
if "end_date" in params:
self._validate_datetime(params["end_date"], "end_date")
elif action == "get":
self._require_param(params, "event_id", str)
elif action == "create":
self._require_param(params, "subject", str)
start = self._require_param(params, "start", str)
end = self._require_param(params, "end", str)
self._validate_datetime(start, "start")
self._validate_datetime(end, "end")
# Validate end is after start
start_dt = datetime.fromisoformat(start.replace("Z", "+00:00"))
end_dt = datetime.fromisoformat(end.replace("Z", "+00:00"))
if end_dt <= start_dt:
raise OperationError(
code="INVALID_TIME_RANGE",
message="Event end time must be after start time",
details={"start": start, "end": end}
)
# Validate attendees if provided
if "attendees" in params:
attendees = params["attendees"]
if not isinstance(attendees, list):
raise OperationError(
code="INVALID_PARAM_TYPE",
message="attendees must be a list of email addresses"
)
for email in attendees:
self._validate_email(email)
elif action == "update":
self._require_param(params, "event_id", str)
changes = self._require_param(params, "changes", dict)
# Validate datetime fields if present
if "start" in changes:
self._validate_datetime(changes["start"], "start")
if "end" in changes:
self._validate_datetime(changes["end"], "end")
elif action == "delete":
self._require_param(params, "event_id", str)
elif action == "find_times":
attendees = self._require_param(params, "attendees", list)
if len(attendees) == 0:
raise OperationError(
code="INVALID_PARAM",
message="At least one attendee required"
)
for email in attendees:
self._validate_email(email)
# Accept both 'duration' and 'duration_minutes' for compatibility
duration = params.get("duration") or params.get("duration_minutes")
if not duration:
raise OperationError(
code="MISSING_PARAM",
message="Required parameter 'duration_minutes' not provided"
)
if not isinstance(duration, int):
raise OperationError(
code="INVALID_PARAM",
message="Parameter 'duration_minutes' must be an integer"
)
if duration < 15 or duration > 480:
raise OperationError(
code="INVALID_PARAM",
message="Duration must be between 15 and 480 minutes",
details={"duration": duration}
)
# Validate time window
if "time_window" in params:
window = params["time_window"]
if "start" not in window or "end" not in window:
raise OperationError(
code="INVALID_PARAM",
message="time_window must have 'start' and 'end' fields"
)
self._validate_datetime(window["start"], "time_window.start")
self._validate_datetime(window["end"], "time_window.end")
elif action == "get_schedule":
users = self._require_param(params, "users", list)
if len(users) == 0:
raise OperationError(
code="INVALID_PARAM",
message="At least one user required"
)
start = self._require_param(params, "start", str)
end = self._require_param(params, "end", str)
self._validate_datetime(start, "start")
self._validate_datetime(end, "end")
def _execute_action(self, action: str, params: Dict) -> Any:
"""Execute calendar action."""
if action == "list":
return self._action_list(params)
elif action == "get":
return self._action_get(params)
elif action == "create":
return self._action_create(params)
elif action == "update":
return self._action_update(params)
elif action == "delete":
return self._action_delete(params)
elif action == "find_times":
return self._action_find_times(params)
elif action == "get_schedule":
return self._action_get_schedule(params)
def _action_list(self, params: Dict) -> Dict:
"""List calendar events with optional filters."""
access_token = params["access_token"]
microsoft_user = params["microsoft_user"]
query_params = {
"$select": "id,subject,start,end,location,attendees,isOnlineMeeting,onlineMeeting,organizer",
"$orderby": "start/dateTime"
}
# Add date filters
filter_parts = []
if "start_date" in params:
filter_parts.append(f"start/dateTime ge '{params['start_date']}'")
if "end_date" in params:
filter_parts.append(f"end/dateTime le '{params['end_date']}'")
# Add subject filter
if "subject_contains" in params:
filter_parts.append(f"contains(subject, '{params['subject_contains']}')")
if filter_parts:
query_params["$filter"] = " and ".join(filter_parts)
# Limit results
max_results = params.get("max_results", 50)
query_params["$top"] = max_results
try:
# Create GraphClient with access_token from TrustyVault
client = MicrosoftGraphClient(access_token=access_token)
response = client.get(f"/users/{microsoft_user}/calendar/events", params=query_params)
events = response.get("value", [])
return {
"events": events,
"count": len(events)
}
except Exception as e:
raise OperationError(
code="LIST_FAILED",
message=f"Failed to list events: {str(e)}"
)
def _action_get(self, params: Dict) -> Dict:
"""Get event details by ID."""
access_token = params["access_token"]
microsoft_user = params["microsoft_user"]
event_id = params["event_id"]
try:
# Create GraphClient with access_token from TrustyVault
client = MicrosoftGraphClient(access_token=access_token)
event = client.get(f"/users/{microsoft_user}/calendar/events/{event_id}")
return {"event": event}
except Exception as e:
if "404" in str(e) or "ResourceNotFound" in str(e):
raise OperationError(
code="EVENT_NOT_FOUND",
message=f"Event not found: {event_id}",
details={"event_id": event_id}
)
raise OperationError(
code="GET_FAILED",
message=f"Failed to get event: {str(e)}",
details={"event_id": event_id}
)
def _action_create(self, params: Dict) -> Dict:
"""Create new calendar event with optional Teams meeting."""
access_token = params["access_token"]
microsoft_user = params["microsoft_user"]
# Build event object
event = {
"subject": params["subject"],
"start": {
"dateTime": params["start"],
"timeZone": params.get("timezone", "UTC")
},
"end": {
"dateTime": params["end"],
"timeZone": params.get("timezone", "UTC")
}
}
# Add optional fields
if "body" in params:
event["body"] = {
"contentType": "HTML",
"content": params["body"]
}
if "location" in params:
event["location"] = {
"displayName": params["location"]
}
if "attendees" in params:
event["attendees"] = [
{
"emailAddress": {"address": email},
"type": "required"
}
for email in params["attendees"]
]
# Add Teams meeting if requested
if params.get("is_online", False):
event["isOnlineMeeting"] = True
event["onlineMeetingProvider"] = "teamsForBusiness"
try:
# Create GraphClient with access_token from TrustyVault
client = MicrosoftGraphClient(access_token=access_token)
created_event = client.post(f"/users/{microsoft_user}/calendar/events", json=event)
return {
"event": created_event,
"event_id": created_event.get("id"),
"subject": created_event.get("subject"),
"teams_link": created_event.get("onlineMeeting", {}).get("joinUrl") if created_event.get("isOnlineMeeting") else None
}
except Exception as e:
raise OperationError(
code="CREATE_FAILED",
message=f"Failed to create event: {str(e)}",
details={"subject": params["subject"]}
)
def _action_update(self, params: Dict) -> Dict:
"""Update existing event."""
access_token = params["access_token"]
microsoft_user = params["microsoft_user"]
event_id = params["event_id"]
changes = params["changes"]
# Build update object
update = {}
if "subject" in changes:
update["subject"] = changes["subject"]
if "start" in changes:
update["start"] = {
"dateTime": changes["start"],
"timeZone": changes.get("timezone", "UTC")
}
if "end" in changes:
update["end"] = {
"dateTime": changes["end"],
"timeZone": changes.get("timezone", "UTC")
}
if "body" in changes:
update["body"] = {
"contentType": "HTML",
"content": changes["body"]
}
if "location" in changes:
update["location"] = {
"displayName": changes["location"]
}
try:
# Create GraphClient with access_token from TrustyVault
client = MicrosoftGraphClient(access_token=access_token)
updated_event = client.patch(f"/users/{microsoft_user}/calendar/events/{event_id}", json=update)
return {
"event": updated_event,
"event_id": event_id,
"updated_fields": list(changes.keys())
}
except Exception as e:
if "404" in str(e):
raise OperationError(
code="EVENT_NOT_FOUND",
message=f"Event not found: {event_id}",
details={"event_id": event_id}
)
raise OperationError(
code="UPDATE_FAILED",
message=f"Failed to update event: {str(e)}",
details={"event_id": event_id}
)
def _action_delete(self, params: Dict) -> Dict:
"""Delete event."""
access_token = params["access_token"]
microsoft_user = params["microsoft_user"]
event_id = params["event_id"]
try:
# Create GraphClient with access_token from TrustyVault
client = MicrosoftGraphClient(access_token=access_token)
client.delete(f"/users/{microsoft_user}/calendar/events/{event_id}")
return {
"deleted": True,
"event_id": event_id
}
except Exception as e:
if "404" in str(e):
raise OperationError(
code="EVENT_NOT_FOUND",
message=f"Event not found: {event_id}",
details={"event_id": event_id}
)
raise OperationError(
code="DELETE_FAILED",
message=f"Failed to delete event: {str(e)}",
details={"event_id": event_id}
)
def _action_find_times(self, params: Dict) -> Dict:
"""Find common availability slots for multiple attendees."""
access_token = params["access_token"]
microsoft_user = params["microsoft_user"]
attendees = params["attendees"]
# Accept both 'duration' and 'duration_minutes' for compatibility
duration = params.get("duration") or params.get("duration_minutes")
# Build request
request_body = {
"attendees": [
{"emailAddress": {"address": email}, "type": "required"}
for email in attendees
],
"timeConstraint": {
"activityDomain": "work",
"timeslots": []
},
"meetingDuration": f"PT{duration}M",
"maxCandidates": params.get("max_candidates", 10),
"isOrganizerOptional": False
}
# Add time window if provided
if "time_window" in params:
window = params["time_window"]
request_body["timeConstraint"]["timeslots"] = [
{
"start": {"dateTime": window["start"], "timeZone": "UTC"},
"end": {"dateTime": window["end"], "timeZone": "UTC"}
}
]
try:
# Create GraphClient with access_token from TrustyVault
client = MicrosoftGraphClient(access_token=access_token)
response = client.post(f"/users/{microsoft_user}/findMeetingTimes", json=request_body)
suggestions = response.get("meetingTimeSuggestions", [])
if not suggestions:
raise OperationError(
code="NO_SLOTS_FOUND",
message="No common availability found for the specified attendees and time window",
details={
"attendees": attendees,
"duration": duration
}
)
# Format suggestions
formatted_suggestions = []
for suggestion in suggestions:
time_slot = suggestion.get("meetingTimeSlot", {})
formatted_suggestions.append({
"start": time_slot.get("start", {}).get("dateTime"),
"end": time_slot.get("end", {}).get("dateTime"),
"confidence": suggestion.get("confidence"),
"attendee_availability": suggestion.get("attendeeAvailability", [])
})
return {
"suggestions": formatted_suggestions,
"count": len(formatted_suggestions),
"attendees": attendees,
"duration": duration
}
except OperationError:
raise
except Exception as e:
raise OperationError(
code="FIND_TIMES_FAILED",
message=f"Failed to find meeting times: {str(e)}",
details={"attendees": attendees}
)
def _action_get_schedule(self, params: Dict) -> Dict:
"""Get free/busy schedule for users."""
access_token = params["access_token"]
microsoft_user = params["microsoft_user"]
users = params["users"]
start = params["start"]
end = params["end"]
# Build request
request_body = {
"schedules": users,
"startTime": {
"dateTime": start,
"timeZone": "UTC"
},
"endTime": {
"dateTime": end,
"timeZone": "UTC"
},
"availabilityViewInterval": params.get("interval", 60)
}
try:
# Create GraphClient with access_token from TrustyVault
client = MicrosoftGraphClient(access_token=access_token)
response = client.post(f"/users/{microsoft_user}/calendar/getSchedule", json=request_body)
schedules = response.get("value", [])
return {
"schedules": schedules,
"users": users,
"time_range": {"start": start, "end": end}
}
except Exception as e:
raise OperationError(
code="GET_SCHEDULE_FAILED",
message=f"Failed to get schedule: {str(e)}",
details={"users": users}
)