⚠️ Disclaimer: This repository is an initial, functional implementation of a remote MCP server using OAuth 2.1 + PKCE on Cloudflare Workers with Microsoft 365 Graph API integration. It intentionally mirrors the posture of early AI experiments and is not hardened for production use.
For a secured, enterprise-ready deployment with Cloudflare Access, AI Gateway, secret management, and operational guardrails, refer to m365-mcp-server-production.
A robust Model Context Protocol (MCP) server providing secure remote access to Microsoft 365 services through OAuth 2.1 + PKCE authentication. Features industry-standard security, global edge deployment, and native integration support for MCP-compatible AI applications.
I started this project to explore three buzzworthy technologies: MCP (Model Context Protocol), Cloudflare Workers, and this whole "agentic AI" thing everyone keeps talking about. Just wanted to see what the fuss was about, maybe build a simple proof of concept during the evening after the kids fall asleep. Fast forward a weekend and 2 sleepless nights, and I've accidentally become an OAuth 2.1 expert, can explain PKCE flows in my sleep, dream in HMAC-SHA256 signatures, and have strong opinions about JWT token expiration strategies. What started as "just connect [insert your AI assistant of choice] to my calendar" turned into a full-blown production-ready authentication system with more security layers than my bank. But hey, at least now my AI assistant can read my emails securely while I ponder how a simple curiosity led to implementing half of RFC 6749. The good news? You get to benefit from my OAuth rabbit hole adventure with a server that actually works and won't leak your tokens all over the internet.
Full Disclosure: Because I'm a lazy cybersecurity architect (the good kind - the one who automates everything), this code is 80% AI-generated and the documentation is 99.7% AI-generated. It took me longer to write these "About" sections with my actual human fingers than it took the AI to generate 5000+ lines of working code and documentation. I'm not ashamed - I'm efficient. Bill Gates would be proud (you know, that quote about hiring lazy people because they find easy ways to do hard things). The 0.3% of documentation I wrote myself? You're reading it right now. You're welcome.
- What is Model Context Protocol (MCP)?
- Why Microsoft 365 Integration?
- Prerequisites
- Key Features
- Quick Start Guide
- Microsoft 365 Tools
- Common Use Cases
- System Architecture
- Troubleshooting
- Frequently Asked Questions
- Documentation
- Development
- Deployment
- Security
- Contributing
- Support
Remember when your apps couldn't talk to each other and you had to copy-paste everything like it's 1999? MCP is basically couples therapy for AI and your APIs - helping them communicate their needs, respect each other's boundaries (rate limits), and work through their authentication issues. And like any good therapist, MCP comes equipped with a Swiss Army knife of tools - except instead of a tiny scissors you'll never use, you get actual useful functions that let your AI read emails, check calendars, and do real work while you grab another coffee and contemplate how you ended up mediating between machines.
Model Context Protocol (MCP) is an open protocol developed by Anthropic that enables seamless integration between Large Language Model (LLM) applications and external data sources and tools. And yes, as a small thank you to Anthropic for actually open-sourcing this protocol (looking at you, other AI companies hoarding your toys), I tested this integration with Claude first and foremost. The other assistants can get in line behind the one that actually shares its homework with the class. It provides a standardized way for AI models to:
- Access External Tools: Execute functions and operations in external systems
- Retrieve Resources: Fetch data from APIs, databases, and services
- Maintain Context: Preserve session state across interactions
- Ensure Security: Handle authentication and authorization properly
MCP servers act as bridges between AI applications and external services, exposing tools and resources that the AI can use to perform real-world tasks.
Honestly? Because I already had a Microsoft 365 Business subscription for my actual business, and it felt wrong to have all those API endpoints just sitting there, unused and unloved. Turns out, building an OAuth integration for Microsoft's enterprise ecosystem is like solving a Rubik's cube blindfolded - technically possible, surprisingly satisfying when it works, and it makes you look way smarter than you actually are at dinner parties.
The beautiful part is that this could have been ANY service - Google Workspace, Slack, Discord, whatever has OAuth 2.1. The architecture I accidentally over-engineered is completely service-agnostic. Just swap out the Microsoft Graph endpoints for any other OAuth-compatible API, and boom - you've got yourself an MCP server for your favorite service. But since I had M365 lying around and those Exchange endpoints were calling my name...
This MCP server enables AI applications to interact with Microsoft 365 services, providing:
- Email Management: Read, send, and search emails through Outlook
- Calendar Operations: Create events, check availability, manage meetings
- Teams Integration: Send messages, create meetings, collaborate
- Contact Access: Search and manage Microsoft 365 contacts
- Secure Authentication: Enterprise-grade OAuth 2.1 + PKCE flow
- Real-time Operations: Direct API access, not cached data
Before setting up the Microsoft 365 MCP Server, ensure you have:
- Microsoft 365 Account (Business or Enterprise)
- Admin access for app registration
- Active subscription with Exchange Online
- Microsoft Entra ID Access (formerly Azure AD)
- Ability to register applications
- Permission to grant admin consent
- Cloudflare Account (Free tier supported)
- Workers & KV storage enabled
- Custom domain (optional for production)
Your Microsoft 365 administrator must grant these Graph API permissions:
User.Read- Read user profileMail.Read,Mail.ReadWrite,Mail.Send- Email operationsCalendars.Read,Calendars.ReadWrite- Calendar accessContacts.ReadWrite- Contact managementOnlineMeetings.ReadWrite- Teams meetingsChannelMessage.Send- Teams messagesTeam.ReadBasic.All- Teams information
- Node.js 18+ with npm
- Git for version control
- Wrangler CLI (
npm install -g wrangler)
- Enterprise Security: OAuth 2.1 + PKCE with dynamic client registration
- Global Edge Network: Deployed on Cloudflare Workers with 330+ edge locations
- Hybrid Protocol Support: Single endpoint supporting WebSocket, SSE, and HTTP JSON-RPC
- Complete Microsoft 365 Integration: Email, Calendar, Teams, Contacts via Microsoft Graph API
- Native MCP Compatibility: Direct integration with AI assistants and mcp-remote
- Production Security: End-to-end encryption with secure token storage
- Automatic Token Management: Handles refresh tokens and session persistence
- Real-time Operations: Direct API access without caching delays
| Document | Description |
|---|---|
| Technical Reference | Complete technical documentation, architecture, and API docs |
| Operations Guide | Development, deployment, and maintenance procedures |
| README.md | This file - overview, quick start, and troubleshooting |
- Check Prerequisites: Ensure you have all required accounts and permissions
- Clone Repository:
git clone https://github.com/nikolanovoselec/m365-mcp-server.git - Configure Azure: Register app in Microsoft Entra ID
- Deploy to Cloudflare: Run
wrangler deploy - Test Connection: Use one of the three integration methods below
If you encounter issues, run these diagnostic commands:
# Check OAuth Provider health
curl -X GET https://your-worker.workers.dev/.well-known/openid-configuration
# Verify Durable Objects binding
wrangler tail --format json | grep "MCP_OBJECT"
# Test discovery phase (should return tools without auth)
curl -X POST https://your-worker.workers.dev/mcp \
-H "Authorization: Bearer test-token" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# Check KV namespaces configuration
wrangler kv namespace list
# Monitor real-time logs
wrangler tail --format pretty
# Test OAuth flow (manual)
open "https://your-worker.workers.dev/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=YOUR_REDIRECT_URI&response_type=code&scope=openid"
# Validate Microsoft Graph token
curl -X GET https://graph.microsoft.com/v1.0/me \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"| Issue | Quick Fix |
|---|---|
| 404 on /sse endpoint | User not authenticated - complete OAuth flow first |
| 401 from Graph API | Token expired - OAuth Provider should auto-refresh |
| 403 from Graph API | Missing permissions - check Azure AD app scopes |
| 429 rate limiting | Implement exponential backoff in your client |
| WebSocket fails | Expected - Cloudflare uses HTTP/2, fallback to SSE |
| Empty discovery | Check Durable Object binding in wrangler.toml |
The simplest way to connect your AI assistant to Microsoft 365. Just add the MCP server URL as a custom connector in any MCP-compatible AI assistant.
- Open your AI assistant's settings and navigate to Connectors or Extensions
- Add a custom MCP connector with this URL:
https://your-worker-domain.com/sse - Authenticate when prompted (will open Microsoft login in your browser)
- And voilà! Now you can ask your AI to email your mom those meeting notes you keep promising to send, schedule that dentist appointment you've been avoiding, or find that one email from 2023 with the important attachment you swear you didn't delete.
Compatibility: Compatible with any AI assistant that implements the Model Context Protocol specification. Built using @modelcontextprotocol/sdk v1.17.4 for full MCP compliance and tested with Claude.
For command-line access and automation (Note: This integration is not fully implemented yet):
# 1. Install mcp-remote globally
npm install -g @modelcontextprotocol/remote
# 2. Configure connection
mcp-remote add m365 https://your-worker-domain.com/sse
# 3. Authenticate (will open browser)
mcp-remote auth m365
# 4. Test connection
mcp-remote call m365 tools/list
# 5. Use tools
mcp-remote call m365 tools/call '{"name": "getEmails", "arguments": {"count": 10}}'For web applications and services that need programmatic Microsoft 365 access:
# 1. Register your web application
curl -X POST https://your-worker-domain.com/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "Your Application",
"redirect_uris": ["https://your-app.com/api/mcp/auth_callback"],
"client_uri": "https://your-app.com",
"grant_types": ["authorization_code"],
"response_types": ["code"]
}'
# 2. Start OAuth flow - redirect user to this URL
https://your-worker-domain.com/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_CALLBACK&scope=User.Read
# 3. Exchange code for token (after user authorizes)
curl -X POST https://your-worker-domain.com/token \
-d "grant_type=authorization_code&code=AUTH_CODE&client_id=YOUR_CLIENT_ID"
# 4. Use tools with access token
curl -X POST https://your-worker-domain.com/sse \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"getEmails","arguments":{"count":10}}}'sendEmail- Send emails via Outlook- Supports HTML and plain text
- Multiple recipients (to, cc, bcc)
- File attachments support
getEmails- Retrieve emails from folders- Configurable count (max 50)
- Folder selection (inbox, sent, drafts)
- Returns sender, subject, body, date
searchEmails- Search emails with queries- Microsoft Graph search syntax
- Full-text search across all folders
- Advanced filters support
getCalendarEvents- List calendar events- Date range filtering
- Returns title, attendees, location
- Includes online meeting links
createCalendarEvent- Create new events- Set title, description, location
- Add multiple attendees
- Configure reminders
- Create Teams meetings
sendTeamsMessage- Post to Teams channels- Channel and team selection
- Rich text formatting
- Mentions support
createTeamsMeeting- Schedule Teams meetings- Set date, time, duration
- Add attendees
- Generate meeting links
- Configure meeting options
getContacts- Access Microsoft 365 contacts- Search by name or email
- Returns full contact details
- Pagination for large lists
Daily Summary Report
// Fetch recent emails
const emails = await mcp.call("getEmails", { count: 20, folder: "inbox" });
// Generate summary
const summary = emails.map((e) => `${e.from}: ${e.subject}`).join("\n");
// Send report
await mcp.call("sendEmail", {
to: "[email protected]",
subject: "Daily Email Summary",
body: `<h2>Today's Emails</h2><pre>${summary}</pre>`,
contentType: "html",
});Meeting Coordination
// Check availability
const events = await mcp.call("getCalendarEvents", {
startDateTime: "2025-01-10T09:00:00",
endDateTime: "2025-01-10T17:00:00",
});
// Find free slot
const freeSlot = findAvailableTime(events);
// Schedule meeting
await mcp.call("createCalendarEvent", {
subject: "Project Review",
startDateTime: freeSlot.start,
endDateTime: freeSlot.end,
attendees: ["[email protected]"],
isOnlineMeeting: true,
});Automated Status Updates
// Send project update
await mcp.call("sendTeamsMessage", {
teamId: "project-team-id",
channelId: "general",
message:
"Deployment completed successfully\n\nVersion 2.0.1 is now live in production.",
});Quick Contact Lookup
// Search for contact
const contacts = await mcp.call("getContacts", {
search: "John Doe",
});
// Get email and phone
const contact = contacts[0];
console.log(`Email: ${contact.emailAddresses[0].address}`);
console.log(`Phone: ${contact.mobilePhone}`);The server implements intelligent protocol detection:
- WebSocket - Full bidirectional MCP protocol for mcp-remote clients
- Server-Sent Events - Streaming responses for web connectors
- JSON-RPC over HTTP - Direct API testing and debugging
- Discovery Methods - Unauthenticated tool/resource enumeration
Built on Cloudflare Workers with:
- Durable Objects - Session persistence and WebSocket handling
- KV Storage - Three-tier architecture (OAuth, Config, Cache)
- Edge Computing - Global distribution with <200ms response times
- Enterprise Security - OAuth 2.1 + PKCE compliance
Direct API mapping to Microsoft Graph endpoints:
- Real-time Operations - Send emails, manage calendar, Teams integration
- Automatic Token Management - Refresh tokens, scope validation
- Error Resilience - Advanced retry logic and fallback handling
- Response Optimization - Edge caching and intelligent parsing
Problem: "401 Unauthorized" errors
- Cause: Token expired or invalid
- Solution:
- Check redirect URI matches exactly in Azure app registration
- Verify client secret hasn't expired (check Azure portal)
- Ensure admin consent granted for all permissions
- Try re-authenticating with
mcp-remote auth m365
Problem: "403 Forbidden" on specific operations
- Cause: Missing Microsoft Graph permissions
- Solution:
- Check required scopes in error message
- Add permissions in Azure portal
- Grant admin consent
- Re-authenticate to get new token with added scopes
Problem: WebSocket connection fails
- Cause: HTTP/2 header issues or firewall
- Solution:
- Check if behind corporate proxy
- Try HTTP JSON-RPC endpoint instead
- Verify Cloudflare Worker is deployed
- Check browser console for specific errors
Problem: Tools not appearing in AI assistant
- Cause: MCP server not properly configured
- Solution:
- Verify config file location and syntax
- Restart your AI assistant application completely
- Check AI assistant logs for errors
- Test with
mcp-remoteCLI first
Problem: "Insufficient privileges to complete the operation"
- Cause: Missing admin consent or scope
- Solution:
- Login to Azure portal as admin
- Navigate to App registrations → API permissions
- Click "Grant admin consent"
- Wait 5-10 minutes for propagation
Problem: "The mailbox is either inactive, soft-deleted, or is hosted on-premise"
- Cause: Exchange Online not configured
- Solution:
- Verify Microsoft 365 subscription includes Exchange
- Check user has Exchange license assigned
- Ensure mailbox is cloud-hosted, not on-premise
Problem: "429 Too Many Requests"
- Cause: Microsoft Graph API rate limit (2000/min)
- Solution:
- Implement exponential backoff
- Batch API requests when possible
- Cache responses in KV storage
- Spread requests over time
Q: What is the difference between this and Microsoft's official Graph SDK? A: This is an MCP server that bridges AI applications to Microsoft Graph. It provides a protocol layer that AI models can understand and use, whereas the SDK is for direct programmatic access.
Q: Can I use this with ChatGPT or other AI models? A: Currently designed for MCP-compatible clients (AI assistant applications, mcp-remote). Other AI models would need an MCP client implementation.
Q: Is this free to use? A: The server code is open source. You'll need your own Microsoft 365 subscription and Cloudflare account (free tier supported).
Q: How are my Microsoft credentials stored? A: Credentials are never stored. OAuth tokens are securely managed by the Cloudflare Workers OAuth Provider with automatic expiration.
Q: Can others access my emails through this server? A: No. Each user authenticates separately and can only access their own Microsoft 365 data.
Q: What data passes through Cloudflare? A: Only encrypted tokens and API responses. Cloudflare cannot decrypt your Microsoft data.
Q: Why do I need Cloudflare Workers? A: Cloudflare provides the infrastructure for WebSocket handling, global edge distribution, and Durable Objects for session management.
Q: Can I self-host this? A: Not directly. The architecture requires Cloudflare Workers specific features. You could adapt it for other platforms but would need significant changes.
Q: What's the latency like? A: Typically <200ms for cached operations, <500ms for Microsoft Graph API calls, depending on your location.
Q: Can I add custom tools? A: Yes! See OPERATIONS.md for development guide on adding new tools.
Q: What are the rate limits? A: Microsoft Graph: 2000 requests/min per app. Cloudflare Workers: 100,000 requests/day on free tier.
Q: Maximum email attachment size? A: 25MB per email (Microsoft Graph limitation).
Q: How many concurrent users? A: Unlimited via Cloudflare's Durable Objects architecture.
- Live API:
https://your-worker-domain.com - Tool Discovery:
POST /ssewith{"method": "tools/list"} - Repository: GitHub
- Issues: Report Issues
# Clone and setup
git clone https://github.com/nikolanovoselec/m365-mcp-server.git
cd m365-mcp-server
npm install
# Configure environment
cp .dev.vars.example .dev.vars
cp wrangler.example.toml wrangler.toml
# Edit files with your Microsoft 365 and Cloudflare credentials
# Start development server
npm run dev# Type checking
npm run type-check
# Linting
npm run lint
# Build for deployment
npm run buildSee OPERATIONS.md for complete development workflow.
The server is production-ready and deployed on Cloudflare Workers. For your own deployment:
# Deploy to Cloudflare Workers
wrangler deploy
# Set production secrets
wrangler secret put MICROSOFT_CLIENT_SECRET
wrangler secret put ENCRYPTION_KEYSee OPERATIONS.md for complete deployment guide.
- Microsoft 365 Business/Enterprise account with admin access
- Microsoft Entra ID app registration with appropriate permissions
- Cloudflare Workers account (Free tier supported)
- Node.js 18+ for development
- OAuth 2.1 + PKCE compliance with S256 challenge method
- Secure token management via Cloudflare Workers OAuth Provider
- Zero client secrets for public clients (mcp-remote compatibility)
- Session isolation via Durable Objects architecture
- Built-in protection against common web vulnerabilities
- Rate limiting and abuse prevention
MIT License - see LICENSE file for details.
We welcome contributions! Please read CONTRIBUTING.md for:
- Development setup and workflow
- Code standards and quality guidelines
- Pull request requirements
- Issue reporting guidelines
- Documentation: Complete guides in this repository
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Built with ❤️ for the Model Context Protocol ecosystem. Empowering AI applications with secure, robust Microsoft 365 integration.