Provides unified calendar management for Google Calendar, enabling users to perform CRUD operations on events, check free/busy status, detect scheduling conflicts, and synchronize calendar data across multiple accounts.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Calendar MCP ServerShow me my schedule for today and check for conflicts"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Calendar MCP Server
A Model Context Protocol (MCP) server providing unified calendar management across Google Calendar, Microsoft 365, and Exchange On-Premises.
Table of Contents
Features
Multi-Provider Support: Connect to Google Calendar, Microsoft 365 (Graph API), and Exchange On-Premises (EWS)
Multiple Accounts: Support for multiple accounts per provider (e.g., 2+ Exchange accounts)
12 MCP Tools: Comprehensive calendar operations including CRUD, free/busy, conflict detection, and sync helpers
4 MCP Resources: Quick access to calendar summaries, today's events, weekly schedule, and upcoming events
4 MCP Prompts: Pre-built templates for scheduling meetings and daily briefings
NTLM Authentication: Full support for Exchange NTLM authentication via
@ewsjs/xhrUnified Data Model: Consistent event format across all providers
Timezone Support: Configurable timezone for all operations
Quick Start
# 1. Clone/navigate to the project
cd calendar-mcp
# 2. Install dependencies
npm install
# 3. Build the project
npm run build
# 4. Configure environment variables (see Configuration section)
cp .env.example .env
# Edit .env with your credentials
# 5. Test the server
node dist/index.jsInstallation
Prerequisites
Node.js 18+
npm or yarn
Access to at least one calendar provider (Google, Microsoft 365, or Exchange)
Install Dependencies
npm installBuild
npm run buildThis compiles TypeScript to JavaScript in the dist/ directory.
Configuration
The server uses environment variables for configuration. Create a .env file in the project root or pass environment variables directly.
Environment Variables
Server Settings
Variable | Description | Default |
| Server name for identification |
|
| Server version |
|
| Logging level: |
|
| IANA timezone for all operations |
|
| Working hours start (HH:MM) |
|
| Working hours end (HH:MM) |
|
| Working days (comma-separated) |
|
Request Settings
Variable | Description | Default |
| Request timeout in milliseconds |
|
| Maximum retry attempts |
|
| Delay between retries |
|
| Rate limiting |
|
Google Calendar Setup
Variable | Description | Required |
| Enable Google Calendar | Yes |
| Unique identifier for this account | Yes |
| Display name | Yes |
| Google account email | Yes |
| OAuth client ID from Google Cloud Console | Yes |
| OAuth client secret | Yes |
| OAuth redirect URI | Yes |
| Pre-authorized access token | Yes |
| Refresh token for token renewal | Yes |
| Token expiry (ISO 8601) | No |
Getting Google Credentials
Go to Google Cloud Console
Create a new project or select existing
Enable the Google Calendar API
Go to Credentials → Create Credentials → OAuth client ID
Configure consent screen if prompted
Select Desktop app or Web application
Copy Client ID and Client Secret
Use OAuth Playground to get tokens:
Select
https://www.googleapis.com/auth/calendarAuthorize and get access/refresh tokens
Example Google Configuration
GOOGLE_ENABLED=true
GOOGLE_PROVIDER_ID=google-personal
GOOGLE_PROVIDER_NAME=Personal Google Calendar
GOOGLE_EMAIL=yourname@gmail.com
GOOGLE_CLIENT_ID=123456789.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxx
GOOGLE_REDIRECT_URI=http://localhost:3000/oauth/google/callback
GOOGLE_ACCESS_TOKEN=ya29.xxxxx
GOOGLE_REFRESH_TOKEN=1//xxxxxMicrosoft 365 Setup
Variable | Description | Required |
| Enable Microsoft 365 | Yes |
| Unique identifier for this account | Yes |
| Display name | Yes |
| Microsoft account email | Yes |
| Azure AD app client ID | Yes |
| Azure AD app client secret | Yes |
| Azure tenant ID ( | Yes |
| OAuth redirect URI | Yes |
| Pre-authorized access token | Yes |
| Refresh token | Yes |
| Token expiry (ISO 8601) | No |
Getting Microsoft Credentials
Go to Azure Portal
Navigate to Azure Active Directory → App registrations
Click New registration
Add redirect URI
Go to Certificates & secrets → New client secret
Go to API permissions → Add:
Calendars.ReadWriteUser.Read
Grant admin consent if required
Use Graph Explorer to test
Example Microsoft Configuration
MICROSOFT_ENABLED=true
MICROSOFT_PROVIDER_ID=microsoft-work
MICROSOFT_PROVIDER_NAME=Work Microsoft 365
MICROSOFT_EMAIL=yourname@company.com
MICROSOFT_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
MICROSOFT_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxx
MICROSOFT_TENANT_ID=common
MICROSOFT_REDIRECT_URI=http://localhost:3000/oauth/microsoft/callback
MICROSOFT_ACCESS_TOKEN=eyJ0eXAiOiJKV1QiLCJ...
MICROSOFT_REFRESH_TOKEN=0.xxxxxExchange On-Premises Setup
Variable | Description | Required |
| Enable Exchange provider | Yes |
| Unique identifier for this account | Yes |
| Display name | Yes |
| Exchange email address | Yes |
| EWS endpoint URL | Yes |
| Authentication: | Yes |
For NTLM Authentication (most common for on-premises)
Variable | Description | Required |
| Username (without domain) | Yes |
| Password (quote if special chars) | Yes |
| Active Directory domain | Yes |
For Basic Authentication
Variable | Description | Required |
| Full username or email | Yes |
| Password | Yes |
For OAuth Authentication (Hybrid Exchange)
Variable | Description | Required |
| Azure AD app ID | Yes |
| Client secret | Yes |
| Azure tenant ID | Yes |
| Access token | Yes |
| Refresh token | No |
Finding Your EWS URL
Open Outlook on desktop
Hold Ctrl and right-click the Outlook icon in system tray
Select "Test E-mail AutoConfiguration"
Look for the EWS URL (typically
https://mail.company.com/EWS/Exchange.asmx)
Or ask your Exchange administrator.
Example Exchange Configuration (NTLM)
EXCHANGE_ENABLED=true
EXCHANGE_PROVIDER_ID=exchange-work
EXCHANGE_PROVIDER_NAME=Work Exchange
EXCHANGE_EMAIL=yourname@company.com
EXCHANGE_EWS_URL=https://mail.company.com/EWS/Exchange.asmx
EXCHANGE_AUTH_METHOD=ntlm
EXCHANGE_USERNAME="yourname"
EXCHANGE_PASSWORD="YourP@ssword!"
EXCHANGE_DOMAIN="COMPANYDOMAIN"Note: If your password contains special characters like
!,@,$, wrap it in quotes.
Multiple Exchange Accounts
To configure additional Exchange accounts, use the EXCHANGE_2_, EXCHANGE_3_, etc. prefixes:
# First Exchange Account
EXCHANGE_ENABLED=true
EXCHANGE_PROVIDER_ID=exchange-account1
EXCHANGE_PROVIDER_NAME=Account 1
EXCHANGE_EMAIL=user1@company.com
EXCHANGE_EWS_URL=https://mail.company.com/EWS/Exchange.asmx
EXCHANGE_AUTH_METHOD=ntlm
EXCHANGE_USERNAME="user1"
EXCHANGE_PASSWORD="password1"
EXCHANGE_DOMAIN="DOMAIN"
# Second Exchange Account
EXCHANGE_2_ENABLED=true
EXCHANGE_2_PROVIDER_ID=exchange-account2
EXCHANGE_2_PROVIDER_NAME=Account 2
EXCHANGE_2_EMAIL=user2@company.com
EXCHANGE_2_EWS_URL=https://mail.company.com/EWS/Exchange.asmx
EXCHANGE_2_AUTH_METHOD=ntlm
EXCHANGE_2_USERNAME="user2"
EXCHANGE_2_PASSWORD="password2"
EXCHANGE_2_DOMAIN="DOMAIN"
# Third Exchange Account (if needed)
EXCHANGE_3_ENABLED=true
# ... and so onClaude Code Integration
Method 1: Using /mcp Command (Recommended)
Open Claude Code in your project directory
Run
/mcpSelect "Add Server"
Configure with:
{
"type": "stdio",
"command": "node",
"args": ["/full/path/to/calendar-mcp/dist/index.js"],
"env": {
"DEFAULT_TIMEZONE": "Your/Timezone",
"EXCHANGE_ENABLED": "true",
"EXCHANGE_PROVIDER_ID": "your-exchange",
"EXCHANGE_PROVIDER_NAME": "Your Exchange",
"EXCHANGE_EMAIL": "your@email.com",
"EXCHANGE_EWS_URL": "https://mail.company.com/EWS/Exchange.asmx",
"EXCHANGE_AUTH_METHOD": "ntlm",
"EXCHANGE_USERNAME": "username",
"EXCHANGE_PASSWORD": "password",
"EXCHANGE_DOMAIN": "DOMAIN"
}
}Restart Claude Code or run
/mcpand reconnect
Method 2: Edit ~/.claude.json Directly
Add to the mcpServers object in your project's configuration:
{
"projects": {
"/path/to/your/project": {
"mcpServers": {
"calendar": {
"type": "stdio",
"command": "node",
"args": ["/full/path/to/calendar-mcp/dist/index.js"],
"env": {
"DEFAULT_TIMEZONE": "Asia/Baku",
"EXCHANGE_ENABLED": "true",
"EXCHANGE_PROVIDER_ID": "exchange-work",
"EXCHANGE_PROVIDER_NAME": "Work Calendar",
"EXCHANGE_EMAIL": "you@company.com",
"EXCHANGE_EWS_URL": "https://mail.company.com/EWS/Exchange.asmx",
"EXCHANGE_AUTH_METHOD": "ntlm",
"EXCHANGE_USERNAME": "username",
"EXCHANGE_PASSWORD": "password",
"EXCHANGE_DOMAIN": "DOMAIN"
}
}
}
}
}
}Verify Connection
After configuring, test with:
Ask Claude: "List my calendars"
Or: "What meetings do I have today?"
Tools Reference
Core Calendar Operations
list_calendars
List all available calendars across connected providers.
Parameter | Type | Description |
| string | Filter by provider: |
Example:
List all my calendarslist_events
List events within a time range with optional filters.
Parameter | Type | Required | Description |
| string | Yes | Start of time range (ISO 8601) |
| string | Yes | End of time range (ISO 8601) |
| string[] | No | Filter to specific providers |
| string[] | No | Filter to specific calendar IDs |
| string | No | Text search in subject/body |
| number | No | Maximum results (default: 100, max: 500) |
| string | No | Sort order: |
| boolean | No | Expand recurring events (default: true) |
Example:
Show me all my meetings for tomorrow
What events do I have this week?
Search my calendar for "standup" meetingsget_event
Get detailed information about a specific event.
Parameter | Type | Required | Description |
| string | Yes | The event ID |
| string | Yes | Provider: |
| string | No | Calendar ID (required for some providers) |
Example:
Get details for event ID AAMkADA3OWE...create_event
Create a new calendar event.
Parameter | Type | Required | Description |
| string | Yes | Provider to create event on |
| string | Yes | Event title |
| string | Yes | Start time (ISO 8601) |
| string | Yes | End time (ISO 8601) |
| string | No | Target calendar ID |
| string | No | Event description |
| string | No | Body type: |
| string | No | Physical location |
| array | No | List of attendees with |
| boolean | No | All-day event |
| object | No | Recurrence settings |
| number | No | Reminder before event |
| boolean | No | Create Teams/Meet link |
| string | No |
|
| string | No |
|
| string | No |
|
Example:
Create a meeting called "Project Review" tomorrow at 2pm for 1 hour
Schedule a lunch event next Monday from 12-1pm at "Cafe Downtown"
Create a weekly team standup every Monday at 9amupdate_event
Update an existing event.
Parameter | Type | Required | Description |
| string | Yes | Event ID to update |
| string | Yes | Provider the event belongs to |
| string | No | New title |
| string | No | New start time |
| string | No | New end time |
| string | No | New description |
| string | No | New location |
| array | No | Updated attendee list |
| string | No | For recurring: |
| boolean | No | Notify attendees (default: true) |
Example:
Move my 2pm meeting to 3pm
Change the location of event AAMk... to "Room 201"delete_event
Delete a calendar event.
Parameter | Type | Required | Description |
| string | Yes | Event ID to delete |
| string | Yes | Provider the event belongs to |
| string | No | Calendar ID |
| string | No | For recurring: |
| boolean | No | Send cancellation notices |
Example:
Delete my 3pm meeting
Cancel all instances of my recurring standupAvailability & Scheduling
get_free_busy
Get aggregated availability across all calendars.
Parameter | Type | Required | Description |
| string | Yes | Start of time range (ISO 8601) |
| string | Yes | End of time range (ISO 8601) |
| string[] | No | Filter to specific providers |
| string[] | No | Filter to specific calendars |
| number | No | Minimum free slot duration in minutes |
| boolean | No | Only consider working hours |
| object | No | Custom working hours config |
Example:
When am I free tomorrow?
Find me a 1-hour free slot this week
Show my availability for the next 3 days during working hourscheck_conflicts
Check if a proposed time slot conflicts with existing events.
Parameter | Type | Required | Description |
| string | Yes | Proposed start time (ISO 8601) |
| string | Yes | Proposed end time (ISO 8601) |
| string | No | Event ID to exclude (for rescheduling) |
| string | No | Provider of excluded event |
Example:
Do I have any conflicts tomorrow at 2pm?
Check if I can schedule a meeting on Friday from 10-11amrespond_to_invite
Respond to a meeting invitation.
Parameter | Type | Required | Description |
| string | Yes | Event ID |
| string | Yes | Provider the event belongs to |
| string | Yes | Response: |
| string | No | Calendar ID |
| string | No | Optional response message |
Example:
Accept the meeting invite for AAMk...
Decline the team lunch with message "I have a conflict"
Tentatively accept the project reviewSync Helpers
find_matching_events
Find matching events between two calendars.
Parameter | Type | Required | Description |
| string | Yes | Source calendar provider |
| string | Yes | Target calendar provider |
| string | Yes | Start of time range |
| string | Yes | End of time range |
| string | No | Source calendar ID |
| string | No | Target calendar ID |
| string | No | Match confidence: |
Example:
Find events that exist in both my Exchange calendars
Check for duplicates between Google and Microsoftcopy_event
Copy an event from one calendar to another.
Parameter | Type | Required | Description |
| string | Yes | Event ID to copy |
| string | Yes | Source provider |
| string | Yes | Target provider |
| string | No | Source calendar ID |
| string | No | Target calendar ID |
| boolean | No | Include attendees (default: false) |
| boolean | No | Include description (default: true) |
Example:
Copy event AAMk... from Exchange to Google Calendarcompare_calendars
Compare two calendars to find matching events, differences, and missing events.
Parameter | Type | Required | Description |
| string | Yes | Source calendar provider |
| string | Yes | Target calendar provider |
| string | Yes | Start of time range |
| string | Yes | End of time range |
| string | No | Source calendar ID |
| string | No | Target calendar ID |
Example:
Compare my two Exchange calendars for this week
What events are only in my first calendar but not the second?Resources Reference
MCP Resources provide quick access to calendar data.
URI | Description |
| Overview of all calendars with event counts |
| Today's events across all calendars |
| This week's events grouped by day |
| Next N upcoming events (e.g., |
Usage in Claude Code:
@calendar://today
Show me @calendar://summary
What's in @calendar://weekPrompts Reference
MCP Prompts are pre-built conversation templates.
schedule-meeting
Interactive prompt to schedule a new meeting.
Arguments:
title: Meeting titleduration: Duration (e.g., "30 minutes", "1 hour")attendees: Comma-separated email addressesnotes: Additional notes
daily-briefing
Generate a briefing of today's schedule.
Arguments:
timezone: Override default timezoneinclude_details: Include full event details (true/false)
find-meeting-time
Find available time slots for a meeting.
Arguments:
duration: Required meeting durationwithin_days: Number of days to searchattendees: Participants to check availability for
sync-calendars
Compare and sync events between calendars.
Arguments:
source_provider: Source calendar providertarget_provider: Target calendar provideraction:compare,copy_missing, orfull_sync
Usage Examples
Daily Workflow
# Morning check
"What's on my calendar today?"
# Quick view
"Show me my next 5 meetings"
# Check availability
"Am I free at 3pm today?"
# Schedule meeting
"Schedule a 30-minute call with john@company.com tomorrow at 2pm"Finding Time
"When am I free this week for a 1-hour meeting?"
"Find me an open slot tomorrow afternoon"
"Do I have any conflicts if I book 10-11am on Friday?"Managing Events
"Move my 2pm meeting to 3pm"
"Cancel tomorrow's standup"
"Add 'Conference Room A' as the location for my next meeting"Multi-Calendar
"Compare my two Exchange calendars for this week"
"List all calendars I have access to"
"Copy the project review from my work calendar to personal"Troubleshooting
Common Issues
"No calendars found"
Cause: Environment variables not loaded properly.
Solutions:
Verify
EXCHANGE_ENABLED=trueis setCheck that all required variables are present
For Claude Code: ensure
envobject in MCP config has all variablesRestart Claude Code after config changes
"401 Unauthorized" (Exchange NTLM)
Cause: Invalid credentials or wrong username format.
Solutions:
Use just the username, not
domain\userformatPut the domain in
EXCHANGE_DOMAINseparatelyQuote passwords with special characters:
EXCHANGE_PASSWORD="P@ss!word"Verify credentials work by visiting EWS URL in browser
"Failed to connect to Exchange"
Cause: Network or certificate issues.
Solutions:
Verify EWS URL is accessible from your network
Check if VPN is required
For self-signed certs, you may need to set
NODE_TLS_REJECT_UNAUTHORIZED=0
"Property not loaded" errors
Cause: EWS doesn't load all properties by default.
Solution: This is handled internally with safe property access. If you see this, update to latest version.
Google/Microsoft token expired
Cause: Access tokens have limited lifetime.
Solution:
Get new tokens using OAuth flow
Update
ACCESS_TOKENandTOKEN_EXPIRYin configImplement token refresh in your setup
Debug Mode
Run with debug logging:
LOG_LEVEL=debug node dist/index.jsTesting Connection
Test Exchange connection directly:
# Set environment variables
export EXCHANGE_ENABLED=true
export EXCHANGE_EWS_URL=https://mail.company.com/EWS/Exchange.asmx
export EXCHANGE_AUTH_METHOD=ntlm
export EXCHANGE_USERNAME=youruser
export EXCHANGE_PASSWORD='yourpassword'
export EXCHANGE_DOMAIN=YOURDOMAIN
# Run test
node -e "
const { loadConfig, hasConfiguredProviders } = require('./dist/utils/config.js');
console.log('Has providers:', hasConfiguredProviders());
console.log('Config:', JSON.stringify(loadConfig(), null, 2));
"Architecture
calendar-mcp/
├── src/
│ ├── index.ts # MCP server entry point
│ ├── types/
│ │ └── index.ts # TypeScript interfaces
│ ├── providers/
│ │ ├── base.ts # Base provider interface
│ │ ├── google/
│ │ │ ├── index.ts # Google Calendar provider
│ │ │ └── client.ts # Google API client
│ │ ├── microsoft/
│ │ │ ├── index.ts # Microsoft 365 provider
│ │ │ └── client.ts # Graph API client
│ │ └── exchange/
│ │ ├── index.ts # Exchange EWS provider
│ │ ├── client.ts # EWS client with NTLM support
│ │ └── auth.ts # Authentication manager
│ ├── services/
│ │ ├── calendar-service.ts # Multi-provider orchestration
│ │ └── sync-service.ts # Calendar sync logic
│ ├── tools/
│ │ ├── index.ts # Tool registration
│ │ ├── list-calendars.ts
│ │ ├── list-events.ts
│ │ ├── get-event.ts
│ │ ├── create-event.ts
│ │ ├── update-event.ts
│ │ ├── delete-event.ts
│ │ ├── get-free-busy.ts
│ │ ├── check-conflicts.ts
│ │ ├── respond-to-invite.ts
│ │ ├── find-matching-events.ts
│ │ ├── copy-event.ts
│ │ └── compare-calendars.ts
│ ├── resources/
│ │ └── index.ts # MCP resources
│ ├── prompts/
│ │ └── index.ts # MCP prompts
│ └── utils/
│ ├── config.ts # Configuration loader
│ ├── date.ts # Date utilities
│ └── formatting.ts # Output formatting
├── dist/ # Compiled JavaScript
├── .env # Environment configuration
├── .env.example # Example configuration
├── package.json
├── tsconfig.json
└── README.mdKey Dependencies
Package | Purpose |
| MCP server implementation |
| Exchange Web Services client |
| NTLM authentication for EWS |
| Microsoft Graph API |
| Google Calendar API |
| Environment variable loading |
| Date manipulation |
| Timezone handling |
License
MIT
Contributing
Fork the repository
Create a feature branch
Make your changes
Run tests:
npm testSubmit a pull request
Support
For issues and feature requests, please open a GitHub issue.