We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Alice0416/uoft-student-helper-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""
Google Calendar Client
Handles interaction with Google Calendar API to add course deadlines.
Google Calendar API Documentation: https://developers.google.com/calendar/api/v3/reference
"""
import os
from datetime import datetime, timedelta
from typing import List, Dict, Any, Optional
try:
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
GOOGLE_API_AVAILABLE = True
except ImportError:
GOOGLE_API_AVAILABLE = False
print("⚠️ Google API libraries not installed. Install with: pip install google-auth google-auth-oauthlib google-auth-httplib2 google-api-python-client")
# Scopes required for Google Calendar access
SCOPES = ['https://www.googleapis.com/auth/calendar']
class GoogleCalendarClient:
"""Client for interacting with Google Calendar API"""
def __init__(self, credentials_path: str = "credentials.json", token_path: str = "token.json"):
"""
Initialize Google Calendar client.
Args:
credentials_path: Path to Google OAuth2 credentials file
token_path: Path to store/load access token
"""
if not GOOGLE_API_AVAILABLE:
raise ImportError("Google API libraries not installed")
self.credentials_path = credentials_path
self.token_path = token_path
self.creds = None
self.service = None
self.calendar_id = 'primary' # Use primary calendar by default
def authenticate(self) -> bool:
"""
Authenticate with Google Calendar API.
Returns:
True if authentication successful, False otherwise
"""
try:
# Load existing token if available
if os.path.exists(self.token_path):
self.creds = Credentials.from_authorized_user_file(self.token_path, SCOPES)
# If no valid credentials, get new ones
if not self.creds or not self.creds.valid:
if self.creds and self.creds.expired and self.creds.refresh_token:
self.creds.refresh(Request())
else:
if not os.path.exists(self.credentials_path):
print(f"❌ Credentials file not found: {self.credentials_path}")
print("Please download OAuth2 credentials from Google Cloud Console")
return False
flow = InstalledAppFlow.from_client_secrets_file(
self.credentials_path, SCOPES)
self.creds = flow.run_local_server(port=0)
# Save credentials for next run
with open(self.token_path, 'w') as token:
token.write(self.creds.to_json())
# Build service
self.service = build('calendar', 'v3', credentials=self.creds)
print("✅ Google Calendar authenticated")
return True
except Exception as e:
print(f"❌ Google Calendar authentication failed: {str(e)}")
return False
def create_event(self,
title: str,
description: str,
start_time: datetime,
end_time: Optional[datetime] = None,
location: Optional[str] = None,
reminders: Optional[List[int]] = None) -> Dict[str, Any]:
"""
Create a calendar event.
Args:
title: Event title
description: Event description
start_time: Event start time
end_time: Event end time (defaults to start_time + 1 hour)
location: Event location
reminders: List of reminder times in minutes before event
Returns:
Dictionary with event information
"""
if not self.service:
if not self.authenticate():
return {"error": "Failed to authenticate with Google Calendar"}
try:
# Default end time if not provided
if end_time is None:
end_time = start_time + timedelta(hours=1)
# Build event
event = {
'summary': title,
'description': description,
'start': {
'dateTime': start_time.isoformat(),
'timeZone': 'America/Toronto',
},
'end': {
'dateTime': end_time.isoformat(),
'timeZone': 'America/Toronto',
},
}
# Add location if provided
if location:
event['location'] = location
# Add reminders
if reminders:
event['reminders'] = {
'useDefault': False,
'overrides': [
{'method': 'popup', 'minutes': minutes}
for minutes in reminders
],
}
else:
event['reminders'] = {'useDefault': True}
# Create event
created_event = self.service.events().insert(
calendarId=self.calendar_id,
body=event
).execute()
return {
"success": True,
"event_id": created_event['id'],
"event_link": created_event.get('htmlLink'),
"title": title,
"start_time": start_time.isoformat()
}
except HttpError as e:
return {
"error": f"Google Calendar API error: {str(e)}"
}
except Exception as e:
return {
"error": f"Failed to create event: {str(e)}"
}
def add_deadline(self,
course_code: str,
assignment_name: str,
due_date: datetime,
description: str = "",
reminders: Optional[List[int]] = None) -> Dict[str, Any]:
"""
Add a course deadline to Google Calendar.
Args:
course_code: Course code (e.g., "CSC148H1")
assignment_name: Assignment name
due_date: Due date and time
description: Additional description
reminders: List of reminder times in minutes (default: [1440, 60] = 1 day and 1 hour)
Returns:
Dictionary with event information
"""
# Default reminders: 1 day before and 1 hour before
if reminders is None:
reminders = [1440, 60] # 24 hours and 1 hour
# Build title
title = f"[{course_code}] {assignment_name} DUE"
# Build description
full_description = f"Course: {course_code}\nAssignment: {assignment_name}\n"
if description:
full_description += f"\nDetails:\n{description}"
# Create event (deadline is an all-day event or specific time)
return self.create_event(
title=title,
description=full_description,
start_time=due_date,
end_time=due_date,
reminders=reminders
)
def batch_add_deadlines(self, deadlines: List[Dict[str, Any]]) -> Dict[str, Any]:
"""
Add multiple deadlines to Google Calendar.
Args:
deadlines: List of deadline dictionaries, each containing:
- course_code: str
- assignment_name: str
- due_date: datetime or ISO string
- description: str (optional)
Returns:
Dictionary with results for each deadline
"""
results = {
"total": len(deadlines),
"successful": 0,
"failed": 0,
"events": []
}
for deadline in deadlines:
# Parse due_date if it's a string
due_date = deadline['due_date']
if isinstance(due_date, str):
try:
due_date = datetime.fromisoformat(due_date.replace('Z', '+00:00'))
except:
results["failed"] += 1
results["events"].append({
"course_code": deadline['course_code'],
"assignment_name": deadline['assignment_name'],
"error": "Invalid date format"
})
continue
# Add deadline
result = self.add_deadline(
course_code=deadline['course_code'],
assignment_name=deadline['assignment_name'],
due_date=due_date,
description=deadline.get('description', '')
)
if "error" in result:
results["failed"] += 1
else:
results["successful"] += 1
results["events"].append(result)
return results
def list_upcoming_events(self, max_results: int = 10) -> List[Dict[str, Any]]:
"""
List upcoming events from Google Calendar.
Args:
max_results: Maximum number of events to return
Returns:
List of event dictionaries
"""
if not self.service:
if not self.authenticate():
return []
try:
now = datetime.utcnow().isoformat() + 'Z'
events_result = self.service.events().list(
calendarId=self.calendar_id,
timeMin=now,
maxResults=max_results,
singleEvents=True,
orderBy='startTime'
).execute()
events = events_result.get('items', [])
return [
{
"title": event['summary'],
"start": event['start'].get('dateTime', event['start'].get('date')),
"end": event['end'].get('dateTime', event['end'].get('date')),
"description": event.get('description', ''),
"link": event.get('htmlLink')
}
for event in events
]
except HttpError as e:
print(f"❌ Failed to list events: {str(e)}")
return []