Spaces:
Running
Running
| """ | |
| Google Calendar Agent β specialized agent with only Calendar MCP tools. | |
| Uses MCPServerStdio (local subprocess) with create_static_tool_filter so the | |
| agent sees *only* calendar / event tools. Zero network overhead. | |
| """ | |
| import asyncio | |
| import logging | |
| from openai import AsyncOpenAI | |
| from agents import Agent, Runner, OpenAIChatCompletionsModel | |
| from agents.model_settings import ModelSettings | |
| try: | |
| from .google_mcp_config import ( | |
| LONGCAT_API_KEY, LONGCAT_BASE_URL, MODEL_NAME, | |
| CALENDAR_TOOLS, create_google_mcp_server, USER_GOOGLE_EMAIL, | |
| ) | |
| except ImportError: | |
| from google_mcp_config import ( | |
| LONGCAT_API_KEY, LONGCAT_BASE_URL, MODEL_NAME, | |
| CALENDAR_TOOLS, create_google_mcp_server, USER_GOOGLE_EMAIL, | |
| ) | |
| logger = logging.getLogger(__name__) | |
| SYSTEM_PROMPT = """\ | |
| You are a specialized Google Calendar assistant. You can list calendars, | |
| query events, create / modify / delete events, and check free-busy | |
| availability using the available tools. | |
| Capabilities: | |
| - **List calendars** the user has access to | |
| - **Get events** in a date range (supports filtering by calendar) | |
| - **Create events** with title, description, start/end times, attendees, | |
| location, recurrence rules, and reminders | |
| - **Modify events** β change any field on an existing event | |
| - **Delete events** by event ID | |
| - **Free/busy queries** β check availability for one or more calendars | |
| in a given time range | |
| Rules: | |
| 1. The user's Google email is provided in the query β use it for every | |
| tool call in the `user_google_email` parameter. NEVER ask the user | |
| for their email; it is always supplied. | |
| 2. Use ISO 8601 format for dates/times (e.g. "2025-01-15T09:00:00-05:00"). | |
| If the user gives a natural-language date like "next Tuesday at 3 PM", | |
| convert it to ISO 8601 before calling the tool. | |
| 3. When creating or modifying events involving other attendees, list | |
| their email addresses explicitly. | |
| 4. After any create/modify/delete, confirm the change with the event | |
| title, date, and time. | |
| 5. For recurring events, use RRULE syntax (e.g. "RRULE:FREQ=WEEKLY;COUNT=10"). | |
| """ | |
| class GoogleCalendarAgent: | |
| """Thin wrapper around the OpenAI Agent SDK wired to Google Calendar tools.""" | |
| def __init__(self, model: str = MODEL_NAME): | |
| self.model = model | |
| self._client = AsyncOpenAI( | |
| api_key=LONGCAT_API_KEY, | |
| base_url=LONGCAT_BASE_URL, | |
| timeout=30.0, | |
| ) | |
| # ββ factory helpers ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _create_mcp_server(self): | |
| """Spawn a local MCP subprocess with only Calendar tools loaded.""" | |
| return create_google_mcp_server(service="calendar", tool_names=CALENDAR_TOOLS) | |
| def _create_agent(self, mcp_server) -> Agent: | |
| return Agent( | |
| name="Google Calendar Agent", | |
| instructions=SYSTEM_PROMPT, | |
| mcp_servers=[mcp_server], | |
| model=OpenAIChatCompletionsModel( | |
| model=self.model, | |
| openai_client=self._client, | |
| ), | |
| model_settings=ModelSettings(tool_choice="auto"), | |
| ) | |
| # ββ public API βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def run(self, query: str) -> str: | |
| """Spawn MCP connection, run a single query, then clean up.""" | |
| mcp_server = self._create_mcp_server() | |
| async with mcp_server: | |
| agent = self._create_agent(mcp_server) | |
| logger.info("Google Calendar MCP connected β agent ready") | |
| result = await Runner.run(agent, input=query) | |
| return result.final_output | |
| # βββ CLI entry point ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def main(): | |
| agent = GoogleCalendarAgent() | |
| resp = await agent.run( | |
| "Show my events for this week. My email is user@example.com" | |
| ) | |
| print("Agent Response:\n", resp) | |
| if __name__ == "__main__": | |
| from dotenv import load_dotenv, find_dotenv | |
| load_dotenv(find_dotenv()) | |
| asyncio.run(main()) | |