This lean MCP server provides direct Microsoft Graph API access with multi-tenant account management, designed to minimize context bloat while enabling comprehensive Microsoft 365 integration.
Core Capabilities:
Multi-tenant authentication - Log in to multiple Microsoft 365 accounts simultaneously using device code flow, ideal for headless/SSH environments
Universal Graph API access - Execute any Graph API request through a single
graph-requesttool supporting all HTTP methods (GET, POST, PUT, PATCH, DELETE), custom headers, and request bodiesCross-tenant querying - Target specific accounts via
accountIdparameter to query different tenants in the same conversation without switchingFull Microsoft 365 coverage - Access all services including Outlook, Calendar, OneDrive, Users, Groups, Teams, and SharePoint
Advanced OData queries - Use
$select,$filter,$top,$orderby,$expand,$count, and$searchfor precise data retrievalAPI version control - Choose between stable (v1.0) and preview (beta) endpoints
Account lifecycle management - List, select, verify status, and remove cached accounts
Context-efficient design - Only 7 tools (~1KB context) replace 30-40 specialized tools (~12KB), reducing AI model overhead
PnP CLI complement - Works alongside PnP CLI for specialized SharePoint, Teams, and Power Platform operations
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., "@ForIT Microsoft Graphshow my upcoming calendar events for this week"
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.
Microsoft Master (MM)
Unified Microsoft 365 MCP server for AI assistants. One server, two tools, all of M365.
Prerequisites
Requirement | Why |
Python 3.10+ | MCP server runtime ( |
Docker | Session pool runs PowerShell modules in containers |
An Azure AD (Entra ID) app registration | Both tools authenticate via device code flow against your app |
MCPJungle (or any MCP host) | Hosts the |
Quick Start
1. Create an Azure AD app registration
Every tenant you want to manage needs an app registration. This is how MM authenticates — there are no shared/default apps.
Option A: Azure Portal (recommended for first-time setup)
Name it something like
MM-CLI(or whatever you want)Set Supported account types to "Accounts in this organizational directory only"
Leave Redirect URI blank (device code flow doesn't need one)
Click Register
Copy the Application (client) ID — this is your
appIdCopy the Directory (tenant) ID — this is your
tenantIdGo to API permissions > Add a permission > Microsoft Graph > Delegated permissions and add:
Permission | For |
| Email (read, send, manage) |
| Calendar operations |
| OneDrive / SharePoint files |
| SharePoint sites |
| Basic profile |
| Look up other users |
| Contacts |
| To Do / Planner tasks |
| OneNote |
| Teams chat |
| Teams team info |
| Teams channels |
| Send Teams messages |
Click Grant admin consent (requires admin role)
No client secret needed. MM uses device code flow (public client), not client credentials.
Option B: CLI for Microsoft 365
npm install -g @pnp/cli-microsoft365
m365 setup # Creates the app registration interactivelySee docs/M365-CLI-SETUP.md for details.
2. Configure the connection registry
The connection registry tells MM which tenants exist and how to reach them.
# Create your first connection interactively
./mm-connections add Contoso-GAThis creates/updates ~/.m365-connections.json. You can also create it manually:
{
"connections": {
"Contoso-GA": {
"appId": "your-app-client-id",
"tenant": "contoso.com",
"tenantId": "your-tenant-guid",
"expectedEmail": "admin@contoso.com",
"description": "Contoso Global Admin",
"mcps": ["mm"]
}
}
}Connection fields:
Field | Required | Description |
| Yes | Application (client) ID from your app registration |
| Yes | Domain ( |
| Yes | Directory (tenant) ID GUID |
| Recommended | Expected sign-in email — MM warns if you auth as the wrong account |
| Yes | Human-readable label |
| Yes | Which MCP servers can use this connection ( |
| No | Set |
Connection naming convention:
GA — Global Admin (tenant admin operations)
Individual — Your user account on a work tenant
Personal — Your own personal tenant
Managing connections:
./mm-connections list # List all connections
./mm-connections add Contoso-GA # Add interactively
./mm-connections edit Contoso-GA appId abc-123 # Set a field
./mm-connections duplicate Contoso-GA Contoso-Individual # Copy
./mm-connections remove Old-Connection # Delete3. Install Python dependencies
cd mm
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt4. Start the session pool
The session pool runs PowerShell modules (Exchange, SharePoint, Azure, Teams) in Docker containers.
cd session-pool
docker compose -p m365-session-pool -f docker-compose.unified.yml up -dVerify it's running:
curl http://localhost:5200/health | jq5. Register with your MCP host
cp mm/mcpjungle-config.example.json mm/mcpjungle-config.json
# Edit mm/mcpjungle-config.json — update paths to match your system
mcpjungle register --conf mm/mcpjungle-config.jsonThe example config expects a venv at mm/.venv/bin/python. Update the command and args paths to match your setup.
6. Use it
# List connections
mcpjungle invoke mm run '{}'
# PowerShell (Exchange)
mcpjungle invoke mm run '{"connection":"Contoso-GA","module":"exo","command":"Get-Mailbox -ResultSize 1"}'
# Graph API
mcpjungle invoke mm graph_request '{"connection":"Contoso-GA","endpoint":"/me"}'
# Power Automate (Flow API)
mcpjungle invoke mm graph_request '{"connection":"Contoso-GA","endpoint":"/providers/Microsoft.ProcessSimple/environments","resource":"flow"}'Auth is automatic. If a connection isn't authenticated, the tool returns a device code:
DEVICE CODE: XXXXXXXX
Go to: https://microsoft.com/deviceloginComplete the sign-in, then retry the command. No pre-auth step needed.
Architecture
┌─────────────────────────────────────────────────────────┐
│ AI Assistant │
└─────────────────────┬───────────────────────────────────┘
│ MCP Protocol (stdio)
▼
┌─────────────┐
│ mm/server │ Python MCP server
│ │ - graph_request (MSAL → Graph API)
│ │ - run (HTTP → session pool)
└──────┬──────┘
│ HTTP :5200
▼
┌───────────────────────┐
│ session-pool/ │ Docker container(s)
│ session_pool.py │ - PowerShell processes per module
│ │ - Native device code auth
│ Modules: │ - Session persistence
│ exo, pnp, azure, │ - Command guardrails
│ teams │ - Comprehensive logging
└───────────────────────┘
│
┌───────────┴───────────┐
│ ~/.m365-connections │ Connection registry (READ-ONLY)
│ ~/.mm-graph-tokens/ │ Graph MSAL token cache
│ ~/.m365-logs/ │ Persistent logs
│ ~/.m365-state/ │ Session state persistence
└───────────────────────┘Tools
mm__run — PowerShell via Session Pool
Execute PowerShell commands through persistent Docker-hosted sessions.
Parameter | Description |
| Connection name from registry |
|
|
| PowerShell command to execute |
| Set |
Omit all parameters to list available connections.
mm__graph_request — Microsoft Graph REST API
Direct HTTP requests to Microsoft Graph (or Flow API) via MSAL tokens.
Parameter | Description |
| Connection name |
| API path (e.g., |
|
|
| Request body for POST/PATCH/PUT |
|
|
| Set |
Send Guards
Email and Teams message sends are blocked by default. When an AI assistant tries to send an email or Teams message, MM intercepts the request and returns a formatted draft preview instead. The assistant must re-call with confirmed: true to actually send.
Guarded Graph endpoints:
POST .../sendMail,.../reply,.../replyAll,.../forward,.../sendPOST /teams/{id}/channels/{id}/messages,/chats/{id}/messages
Guarded PowerShell commands:
Send-MailMessage,Send-MgUserMailNew-MgChatMessage,New-MgTeamChannelMessage,Submit-PnPTeamsChannelMessage
Disabling send guards:
Method | Scope | How |
Per-connection | Single connection | Add |
Global | All connections | Set env var |
Per-connection overrides the global setting. Example:
{
"connections": {
"Contoso-Automation": {
"appId": "...",
"skipSendGuards": true,
"description": "Automated sends, no confirmation needed"
}
}
}Session Pool
The session pool manages PowerShell processes with native device code authentication.
Deployment Modes
Mode | File | Use Case | RAM |
Unified |
| Dev, small servers | ~2-4GB total |
Isolated |
| Production, multi-user | ~512MB per connection |
Features
Session persistence — Authenticated sessions survive container restarts. Session metadata saved to
~/.m365-state/, PowerShell token caches persisted via Docker volumes. Azure sessions restore from cached tokens; EXO/Teams require re-auth (in-process only).Command guardrails — Blocks dangerous operations:
Install-Module(container integrity),New-AzRoleAssignment(access escalation), raw OAuth requests, app registration modifications. Warned but allowed:Remove-Az*, mail forwarding rules.Azure context isolation — In unified mode,
Disable-AzContextAutosave+Select-AzContextby expectedEmail prevents cross-tenant context contamination when multiple Azure sessions share~/.Azure.Comprehensive logging — Dual output: stdout (docker logs) + persistent files (
~/.m365-logs/). Every command's output content logged. AADSTS and auth error patterns flagged at WARNING level.Keepalive — Background thread pings authenticated sessions every 5 minutes to prevent token expiry. Stale sessions (auth_pending > 15 min) automatically reaped.
Metrics —
/metricsendpoint with request counts, error rates, response times, session states.
Session Pool API
Endpoint | Method | Description |
| GET | Health check |
| GET | All session states |
| GET | List registry connections |
| POST | Execute a command |
| POST | Reset a connection's sessions |
| GET | Performance metrics |
Host Directories
Path | Purpose |
| Connection registry (read-only) |
| Persistent log files |
| Session state for restart persistence |
| MSAL token cache for Graph API |
Monitoring
# Container health
docker ps --format "{{.Names}}\t{{.Status}}" | grep m365
# Live logs
docker logs -f m365-pool
# Persistent logs (survive container restarts)
tail -f ~/.m365-logs/session-pool-unified.log
# Session status
curl http://localhost:5200/status | jq
# Metrics
curl http://localhost:5200/metrics | jqHistory
This repo originally contained four separate MCP servers (graph, pnp, pwsh-manager, registry), consolidated in February 2026 into the single mm server. The old servers are preserved in _archived/ for reference.
License
MIT
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.