-
Notifications
You must be signed in to change notification settings - Fork 500
feat(calendly): Add Calendly MCP Server (6 Tools) #278
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Screenshots: https://drive.google.com/drive/folders/1wq_ujAoJT4LDobLP_B0NoGed1vBgYIdS?usp=sharing |
| @@ -0,0 +1,2 @@ | |||
| CALENDLY_ACCESS_TOKEN=YOUR_CALENDLY_ACCESS_TOKEN_HERE | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@udaykakade25 Bro Where are you Using this???
|
@udaykakade25 Bro, it might work when it gets the x-auth-token from the header, but for testing, it’s not really working. Also, your |
| class CalendlyClient: | ||
| """Client for Calendly API using Bearer Authentication.""" | ||
|
|
||
| @staticmethod | ||
| async def make_request( | ||
| method: str, | ||
| endpoint: str, | ||
| json_data: Optional[Dict[str, Any]] = None, | ||
| params: Optional[Dict[str, Any]] = None, | ||
| expect_empty_response: bool = False | ||
| ) -> Dict[str, Any]: | ||
| """Make an HTTP request to Calendly API.""" | ||
| access_token = get_auth_token() | ||
|
|
||
| if not access_token: | ||
| raise RuntimeError("No access token provided. Please set the x-auth-token header.") | ||
|
|
||
| headers = { | ||
| "Authorization": f"Bearer {access_token}", | ||
| "Content-Type": "application/json" | ||
| } | ||
|
|
||
| url = f"{CALENDLY_API_BASE}{endpoint}" | ||
|
|
||
| async with httpx.AsyncClient() as client: | ||
| try: | ||
| if method.upper() == "GET": | ||
| response = await client.get(url, headers=headers, params=params) | ||
| elif method.upper() == "POST": | ||
| response = await client.post(url, headers=headers, json=json_data) | ||
| elif method.upper() == "PUT": | ||
| response = await client.put(url, headers=headers, json=json_data) | ||
| elif method.upper() == "PATCH": | ||
| response = await client.patch(url, headers=headers, json=json_data) | ||
| elif method.upper() == "DELETE": | ||
| response = await client.delete(url, headers=headers) | ||
| else: | ||
| raise ValueError(f"Unsupported HTTP method: {method}") | ||
|
|
||
| response.raise_for_status() | ||
|
|
||
| # Handle empty responses | ||
| if expect_empty_response or response.status_code == 204 or not response.content: | ||
| return {"success": True} | ||
|
|
||
| try: | ||
| json_response = response.json() | ||
| # Handle null/undefined responses | ||
| if json_response is None: | ||
| return {"data": None, "message": "API returned null response"} | ||
| return json_response | ||
| except ValueError as e: | ||
| # Handle cases where response content exists but isn't valid JSON | ||
| logger.error(f"Failed to parse JSON response: {e}") | ||
| logger.error(f"Response content: {response.content}") | ||
| return {"error": "Invalid JSON response", "content": response.text} | ||
|
|
||
| except httpx.HTTPStatusError as e: | ||
| logger.error(f"Calendly API request failed: {e.response.status_code} {e.response.reason_phrase} for {method} {url}") | ||
| error_details = e.response.reason_phrase | ||
| try: | ||
| error_body = e.response.json() | ||
| error_details = f"{e.response.reason_phrase} - {error_body}" | ||
| except Exception: | ||
| pass | ||
| raise RuntimeError(f"Calendly API Error ({e.response.status_code}): {error_details}") from e | ||
| except Exception as e: | ||
| logger.error(f"An unexpected error occurred during Calendly API request: {e}") | ||
| raise RuntimeError(f"Unexpected error during API call to {method} {url}") from e | ||
|
|
||
| async def make_calendly_request( | ||
| method: str, | ||
| endpoint: str, | ||
| json_data: Optional[Dict] = None, | ||
| params: Optional[Dict] = None, | ||
| expect_empty_response: bool = False | ||
| ) -> Any: | ||
| """ | ||
| Makes an HTTP request to the Calendly API. | ||
|
|
||
| Args: | ||
| method: HTTP method (GET, POST, etc.) | ||
| endpoint: API endpoint (should start with /) | ||
| json_data: JSON payload for POST/PUT requests | ||
| params: Query parameters | ||
| expect_empty_response: Whether to expect an empty response (for some operations) | ||
|
|
||
| Returns: | ||
| Response data as dict, or None for empty responses | ||
| """ | ||
| return await CalendlyClient.make_request(method, endpoint, json_data, params, expect_empty_response) No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should keep the code simple and use httpx in a consistent, straightforward way across all tools. What do you think?
Description
Adds the MCP Server for Calendly.
Tools:
get_user_info,get_event_details,list_events,list_event_types,list_availability_schedules, andlist_event_invitees.Related issue
None
Type of change
How has this been tested?
(Add screenshots or recordings here if applicable.)
Tested using Claude in VSCode Terminal and verified on Calendly Web.
Checklist