Content
# EXOS Graph MCP
A local MCP server that gives Claude (in Cowork mode, Claude Code, or any other MCP host) the ability to **send Outlook email, manage your calendar, and post Teams chat messages** on your behalf — the write-side actions that the official Microsoft 365 connector doesn't cover.
Each user authenticates as themselves (delegated Graph permissions) and their refresh token is cached encrypted to their own Windows account via DPAPI. One Azure App Registration covers the whole team.
---
## Quick start (Jose, Cody, anyone else)
This is the only path you need. One PowerShell command, one Microsoft sign-in, done.
**1. Install.** Open PowerShell and run:
```powershell
irm https://raw.githubusercontent.com/Techgeek2858/exos_graph_mcp/main/install.ps1 | iex
```
If you don't have Python yet, the installer will install it via `winget`. If you're missing `pipx`, it'll install that too. It then installs the `exos-graph-mcp` command into an isolated environment, and tries to add the connector entry to your Claude config automatically.
**2. Restart Cowork** (fully quit and reopen).
**3. Sign in.** Start a fresh Cowork chat and type:
> Sign in to Microsoft 365 with the EXOS Graph tool.
Claude will give you a one-time code and a URL. Open the URL in your browser, paste the code, sign in with your EXOS Microsoft account, approve the consent screen. Then back in Cowork:
> I signed in — complete the authentication.
You'll see *"Signed in as your.name@weareexos.com. Microsoft 365 tools are ready."* That's it. From now on, refreshes are silent. You only need to repeat this if you sign out, change your password, or go 90+ days without using the tool.
**4. Try it.**
> Send me an email at my own address with the subject "MCP test" and a one-line body confirming the tool works.
> What meetings do I have tomorrow morning?
> Post a message in my chat with Devin saying "the EXOS Graph tool is connected."
If you want to upgrade later, just re-run the installer command. It always pulls the latest version from `main`.
---
## What you get
17 tools, grouped:
**Auth** — `authenticate`, `complete_authentication`, `whoami`, `sign_out`
**Outlook email** — `send_email`, `create_draft`, `reply_to_email`, `forward_email`, `list_drafts`
**Calendar** — `create_event`, `update_event`, `delete_event`, `list_events`, `respond_to_event`
**Teams chat** — `list_chats`, `send_chat_message`, `create_chat`
Each tool uses the `/me` Graph endpoint, so the signed-in user is always the "from" identity. There is no application-level send-as.
---
## Maintainer setup (one-time, Devin only)
If you're rolling this out to a new tenant, you do these steps once. End users never see them.
### A. Azure App Registration
Open <https://portal.azure.com>, sign in as an account with App Registration rights, then:
1. Search **App registrations** → click **+ New registration**.
2. Settings:
- **Name**: `EXOS Graph MCP` (this is what shows on the consent screen).
- **Supported account types**: *Accounts in this organizational directory only (Single tenant)*.
- **Redirect URI**: leave blank for now.
3. **Register**. On the *Overview* page, copy two values:
- **Application (client) ID**
- **Directory (tenant) ID**
4. Left nav → **Authentication**. Two things to do here, in order:
- Under **Platform configurations**, click **+ Add a platform** → **Mobile and desktop applications** → check **`https://login.microsoftonline.com/common/oauth2/nativeclient`** → **Configure**. Without this, Azure AD returns `AADSTS900971: No reply address provided` when the user enters the device code in the browser, even though device-code flow doesn't logically need a redirect URI.
- Scroll to **Advanced settings → Allow public client flows** → set to **Yes**.
- **Save**.
5. Left nav → **API permissions** → **+ Add a permission** → **Microsoft Graph** → **Delegated permissions**. Add:
- `Mail.Send`
- `Mail.ReadWrite`
- `Calendars.ReadWrite`
- `Chat.ReadWrite`
- `User.Read` *(usually already there)*
- `offline_access` *(under OpenId permissions)*
6. Click **Grant admin consent for EXOS**. If the button is greyed out, ask a Cloud App Administrator to click it once. Without this, every user will face a consent screen they may or may not be allowed to approve themselves.
7. (Optional) Left nav → **Branding & properties** → set a friendly logo / publisher name for the consent screen.
### B. Fork or push this code to GitHub
The installer URL points at a GitHub repo. You need that repo to exist and contain this code.
1. Create a new repo under your EXOS GitHub org. Private is fine — see the note about private-repo access below.
2. Edit `src/exos_graph_mcp/_config.py`. Replace the two placeholders with the **Tenant ID** and **Client ID** you copied from Azure. These are not secrets (tenant IDs are public; public-client client IDs are explicitly non-confidential by OAuth design), so committing them is appropriate.
3. Edit `install.ps1` — replace `Techgeek2858/exos_graph_mcp` in the default `$RepoUrl` with your repo path.
4. Edit this `README.md` — replace the same placeholder in the `irm ... | iex` line so users get a working command.
5. Commit and push to `main`.
### C. Share the install command
Send Jose and Cody one line:
```powershell
irm https://raw.githubusercontent.com/<your-org>/exos-graph-mcp/main/install.ps1 | iex
```
If your GitHub repo is **private**, they'll need to authenticate to GitHub before `pipx install` works against it. Easiest paths:
- They each install GitHub CLI (`winget install GitHub.cli`) and run `gh auth login` once.
- Or you make the repo public — there's nothing in it that needs to be private, given that tenant/client IDs aren't secrets and the code itself does nothing without user authentication.
### D. Publishing updates
To ship a bug fix or new tool to everyone:
1. Edit the code on your machine. Test locally with `pipx install --force <path-to-this-folder>` from PowerShell.
2. Commit and push to `main`.
3. Tell Jose and Cody to re-run the install command. It uses `--force`, which upgrades cleanly.
---
## Local development (Devin)
If you're hacking on the code locally and want changes to take effect immediately without re-running pipx:
```powershell
git clone https://github.com/<your-org>/exos-graph-mcp.git
cd exos-graph-mcp
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e .
```
That gives you the `exos-graph-mcp` command pointing at your working copy. Edit any file in `src/`, save, and the next time Cowork spawns the MCP it'll use the new code (no reinstall).
You can also set `EXOS_GRAPH_TENANT_ID` and `EXOS_GRAPH_CLIENT_ID` in a `.env` file in this folder to override the baked-in values — useful for testing against a separate dev tenant.
---
## Troubleshooting
**"AADSTS65001: User or administrator has not consented"** — the tenant requires admin consent for one of the scopes. App Registration → API permissions → **Grant admin consent for EXOS**. If the button is greyed out, find a Cloud App Administrator at EXOS to click it once.
**"AADSTS7000218: The request body must contain..."** — public client flows aren't enabled. Authentication → Allow public client flows → **Yes**.
**"AADSTS900971: No reply address provided"** — the app is missing the **Mobile and desktop applications** platform. Authentication → + Add a platform → Mobile and desktop applications → check `https://login.microsoftonline.com/common/oauth2/nativeclient` → Configure → Save. Then call `authenticate` again to get a fresh device code.
**"Not signed in to Microsoft 365"** from a tool that isn't `authenticate` — refresh token is gone (>90 days, manual cache delete, or password change). Just call `authenticate` again.
**Teams chat 403 "Caller is not a participant"** — to send to a chat, your user has to already be a member. Create new chats with `create_chat` first.
**Calendar event time off by hours** — Graph wants ISO 8601 without an offset plus a separate `timeZone`. The default is `America/Phoenix`. Pass `timezone="America/New_York"` (or whatever IANA name) if you need a different one.
**`pipx: command not found` after install** — close PowerShell, open a new one, and try again. `pipx ensurepath` only takes effect in new shells.
**Reset everything** — call `sign_out`, or just delete `%LOCALAPPDATA%\exos-graph-mcp\token_cache.bin`.
---
## What's intentionally not here (yet)
- **Channel posts** (`ChannelMessage.Send`) — separate Graph scope that almost always requires tenant admin consent.
- **Application-level send-as** — requires app permissions and IT approval. Out of scope for a delegated-permissions pilot.
- **Email / event attachments** — high on the list of things to add next.
- **Recurring event patterns** — `create_event` handles single events only for now.
- **Reading email bodies / chat history** — the read-only Microsoft 365 connector covers metadata search; full body retrieval would be a useful add.
Connection Info
You Might Also Like
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
awesome-mcp-servers
A collection of MCP servers.
git
A Model Context Protocol server for Git automation and interaction.
oh-my-opencode
Background agents · Curated agents like oracle, librarians, frontend...
TrendRadar
TrendRadar: Your hotspot assistant for real news in just 30 seconds.
Appwrite
Build like a team of hundreds