Graph
An MCP to interact with Office 365 - Teams, mail, calendar.
Install
Install and configure the MCP from https://github.com/JustStas/Graph-MCP now. Follow the repository's installation instructions, ask me for anything you can't complete yourself, and verify its tools load.Graph MCP
Graph MCP is a Node.js MCP server that connects Claude Code and Codex to Microsoft Teams, Outlook mail and calendar, online meetings, OneDrive, users, and presence through Microsoft Graph. It runs locally over stdio and requires Node.js 22 or newer.
What it does
Graph MCP exposes exactly 127 tools:
| Category | Tools |
|---|---|
| Authentication | Check status, log in with browser or device code, log out |
| Users and org | Read your profile, search users, look up a manager or direct reports |
| People and contacts | Relevance-ranked people search, list, create, update, or delete Outlook contacts, list contact folders |
| Search | Search Teams messages, or search mail, calendar, files, and Teams together in one ranked query |
| Chats | List, get, or create chats, read, send, edit, or delete messages, react to messages, rename chats, manage members, mark read |
| Teams and channels | List teams, channels, and members, get a team or its primary channel, create channels, read, send, reply, edit, or delete channel messages, reach a channel's SharePoint folder |
| Calendar | List calendars and events, get, create, update, cancel, or delete events, recurrence, RSVP, free/busy, suggested meeting times, series occurrences, bookable rooms, shared calendars |
| List, read, search, or delta-sync mail, send, reply, or forward with drafts, bcc, importance, and attachments, move and archive, delete, mark read, flag, categorize, manage folders and inbox rules, mail tips, shared mailboxes | |
| Mailbox settings | Read mailbox settings, set automatic replies, set time zone and working hours |
| Meetings | Resolve a meeting ID from a calendar event, join URL, or meeting chat, look a meeting up by join URL or the numeric invite ID, create or get online meetings with join links, attendance reports, transcripts and recordings |
| Presence | Read your own, another user's, or a whole team's presence, set availability or a status message, clear presence |
| Tasks | List To Do lists and tasks, create, update, complete, or delete tasks, list assigned Planner tasks |
| Files | Browse, search, or resolve links to OneDrive and SharePoint content, upload, download as text or base64 bytes, copy, move, delete, version, and share files, manage permissions, read recent and shared items, read and write Excel ranges |
Prerequisites
- Node.js 22 or newer.
- A Microsoft Entra ID app registration configured as a public client on the Mobile and desktop applications platform.
- Redirect URI
http://localhost:3000/auth/callback. - No client secret. Graph MCP uses delegated user authentication.
Add these exact delegated permissions to the app registration:
offline_accessopenidprofileUser.ReadUser.ReadBasic.AllUser.Read.AllChat.ReadChat.ReadWriteChatMember.ReadWriteChatMessage.SendChannelMessage.Read.AllChannelMessage.SendChannelMessage.ReadWriteChannel.CreateTeamMember.Read.AllTeam.ReadBasic.AllChannel.ReadBasic.AllChannelMember.Read.AllCalendars.ReadWriteCalendars.Read.SharedCalendars.ReadWrite.SharedPlace.Read.AllMail.ReadMail.ReadWriteMail.SendMailboxSettings.ReadWriteMail.ReadWrite.SharedMail.Send.SharedPresence.ReadPresence.Read.AllPresence.ReadWriteOnlineMeetings.ReadOnlineMeetings.ReadWriteOnlineMeetingArtifact.Read.AllOnlineMeetingTranscript.Read.AllOnlineMeetingRecording.Read.AllFiles.ReadWrite.AllSites.Read.AllPeople.ReadContacts.ReadWriteTasks.ReadWrite
Some organizations require administrator consent for one or more permissions. Use the least privilege your deployment needs and follow your organization's approval process.
Install
Claude Code plugin
This repository is itself a plugin marketplace, so Claude Code can install it straight from GitHub:
claude plugin marketplace add JustStas/Graph-MCP --scope user
claude plugin install graph-mcp@graph-mcp --scope user
The same thing works inside a Claude Code session with /plugin marketplace add JustStas/Graph-MCP followed by /plugin install graph-mcp@graph-mcp.
Claude clones the repository into its marketplace cache, validates
.claude-plugin/marketplace.json, and installs the self-contained plugin under the plugin
cache. The MCP server launches from the installed plugin bundle, so no source checkout is
needed. To pick up a new release, re-run the two commands.
For plugin development, a local checkout can be added the same way by path instead of
owner/repo:
claude plugin marketplace add /absolute/path/to/Graph-MCP --scope user
claude plugin install graph-mcp@graph-mcp --scope user
Codex plugin
Codex accepts the same GitHub marketplace source:
codex plugin marketplace add JustStas/Graph-MCP --json
codex plugin add graph-mcp@personal --json
codex plugin marketplace add takes a local path, owner/repo[@ref], or an HTTPS or SSH Git
URL, and --ref pins a specific tag or branch. The Codex manifest launches
./dist/graph-mcp.js relative to the installed plugin root, so no source checkout is needed.
For plugin development, point it at a local checkout instead:
codex plugin marketplace add /absolute/path/to/Graph-MCP --json
codex plugin add graph-mcp@personal --json
npm
Install the public scoped package globally:
npm install --global @juststas/graph-mcp
graph-mcp setup
The npm package is scoped to JustStas, but the installed executable remains graph-mcp. Invoking graph-mcp without arguments starts the MCP server over stdio.
Source checkout
npm ci
npm run build
node dist/cli.js setup
Then register the built entrypoint with your host:
claude mcp add graph-mcp -- node /absolute/path/to/Graph-MCP/dist/cli.js
codex mcp add graph-mcp -- node /absolute/path/to/Graph-MCP/dist/cli.js
First-run setup and authentication
setup asks for the Entra application Client ID and Tenant ID and saves them to
~/.graph-mcp/config.json. The Client ID and Tenant ID are identifiers, not secrets. The
setup command does not perform login.
For an installed plugin, use the bundled setup skill and its host-specific command:
- Claude Code:
node "${CLAUDE_PLUGIN_ROOT}/dist/graph-mcp.js" setup - Codex: resolve the installed plugin root from
skills/setup/SKILL.md, change to that directory, then runnode "./dist/graph-mcp.js" setup
After setup, call graph_auth_login. Browser PKCE login is the default and opens a local
loopback callback on the configured redirect URI. If a browser or loopback callback is
unavailable, call graph_auth_login with method: "device_code" and follow the returned
Microsoft verification instructions.
Never paste a client secret, access token, refresh token, authorization code, MFA code, or other credentials into a conversation. Graph MCP does not need a client secret.
Configuration
For Client ID and Tenant ID, environment variables take precedence over
~/.graph-mcp/config.json, which takes precedence over built-in defaults. The setup command
only persists those two identifiers. Other options are environment-only overrides of the
built-in defaults.
| Variable | Required | Default | Description |
|---|---|---|---|
AZURE_CLIENT_ID | Yes | saved azureClientId, then empty | Entra public-client application ID |
AZURE_TENANT_ID | No | saved azureTenantId, then common | Tenant ID or common |
GRAPH_REDIRECT_URI | No | http://localhost:3000/auth/callback | Must exactly match the app registration |
GRAPH_TOKEN_ENCRYPTION_KEY | No | generated local key | Explicit token-encryption key material |
GRAPH_TOKEN_REFRESH_BUFFER | No | 300 | Refresh access tokens this many seconds before expiry |
GRAPH_RATE_LIMIT_MAX_REQUESTS | No | 10000 | Sliding-window request limit |
GRAPH_RATE_LIMIT_WINDOW | No | 600 | Sliding-window duration in seconds |
GRAPH_DEBUG | No | false | Enable diagnostic logging on stderr |
Positive integer options reject zero, negatives, decimals, and malformed values. Boolean
values accept true, false, 1, 0, yes, no, on, or off.
Token storage and migration from Python
The Node server encrypts tokens with AES-256-GCM and stores them under ~/.graph-mcp:
tokens-v2.enc— encrypted token data.key-v2— generated local encryption key when no environment key is supplied
The previous Python runtime used tokens.enc and .key. Version 0.6.0 deliberately does
not read, overwrite, or delete those legacy files because the ciphertext formats differ.
After upgrading from the Python release, authenticate once with graph_auth_login; the Node
server then creates its separate versioned token files. Existing Python token files remain
untouched and may be removed later according to your local security policy.
Access tokens refresh automatically before expiry. graph_auth_logout clears the Node token
state; it does not modify the legacy Python files.
Message and email formatting
The Teams message tools (graph_send_chat_message, graph_send_channel_message, and
graph_reply_to_channel_message) and outbound mail tools (graph_send_mail and
graph_reply_mail) default to HTML mode. When is_html=true, pass explicit HTML; Markdown
is not converted automatically.
<p><strong>Status update</strong></p>
<ul>
<li>Use <code><strong></code> for bold text.</li>
<li>Use <code><pre><code></code> for multi-line code blocks.</li>
</ul>
Use is_html=false for exact plain text. Mentions may use raw Graph data or this simplified
shape, paired with the corresponding <at id="0">Jane Smith</at> tag in the HTML body:
[
{
"name": "Jane Smith",
"user_id": "ef1c916a-3135-4417-ba27-8eb7bd084193"
}
]
Development and verification
Install the locked dependencies and run the complete Node verification pipeline:
npm ci
npm run verify
Useful individual commands:
npm run format:check
npm run lint
npm run typecheck
npm test
npm run build
npm run validate:versions
npm run validate:package
npx vitest run tests/plugin-install-smoke.test.ts
Plugin and release validation:
claude plugin validate --strict plugins/graph-mcp
claude plugin validate --strict .
python3 "${CODEX_HOME:-$HOME/.codex}/skills/.system/plugin-creator/scripts/validate_plugin.py" plugins/graph-mcp
node scripts/test-plugin-install.mjs
npm pack --json --dry-run
The Codex validator is release tooling supplied by Codex's plugin-creator skill; Python is
not required to build, test, or run Graph MCP itself. Before publishing, verify that package,
Claude manifest, and Codex manifest versions match the target release, the committed plugin
bundle is current, both installed plugins expose exactly 127 tools, and the working tree is clean.
Release procedure
Graph MCP releases use the public npm package @juststas/graph-mcp; there is no Python/PyPI
release step. Version 0.6.0 completed the Node migration but was not published to npm because
npm rejected the unscoped graph-mcp@0.6.0 name as too similar to the existing graphmcp
package. Version 0.6.1 is the first scoped npm release.
Normal releases
- Update
package.json,package-lock.json, both plugin manifests, runtime metadata,CHANGELOG.md, and the committed plugin bundle to one version. - Run
npm ci,npm run verify,node scripts/test-plugin-install.mjs, andnpm pack --json --dry-runfrom a clean worktree. - Merge the reviewed pull request to
main. A repository administrator then creates the annotatedv<version>tag on the merged commit through the mandatory release-tag authority ruleset; the separate no-bypass immutability ruleset blocks later update or deletion. - Publish the matching GitHub Release. The workflow trigger is
release: types: [published]. - The package job installs locked dependencies, runs
npm run verify, and prepares the exact tarball without OIDC permission. - The publish job runs in the
npmGitHub environment and is the only job that receives OIDC permission. It downloads a data-only artifact containing the tarball and metadata, checks out its trusted helper atgithub.workflow_sha, binds the expected tag directly to the release event, validates npm's JSON dry-run manifest for the exact private snapshot, and uses npm Trusted Publishing. It has noNODE_AUTH_TOKENor npm secret. - Verify the workflow, npm version,
dist.integrity, installed CLI version, and 127-tool MCP inventory.
Workflow reruns are idempotent. If the version already exists, the workflow succeeds only when npm's dist.integrity equals the prepared tarball. A different integrity fails and requires a new patch version.
First scoped-package bootstrap
npm requires a package to exist before Trusted Publishing can be configured. Bootstrap the first scoped release in this order:
- Verify merged
main, then activate the administrator-authorityv*ruleset. - Audit the exact historical tag inventory and ancestry, require the exact allowlisted historical PyPI workflow blob where expected, and require the new release helper to be absent everywhere.
- Activate the separate no-bypass immutability ruleset.
- Create the annotated
v0.6.1tag only after those gates pass. - Run
publish.ymlfrommainwithprepare_onlyenabled and inspect its prepared artifact. - Validate the exact filename, regular-file status, SHA-512 and SHA-1 digests, and npm's JSON
dry-run manifest. Publish that same private snapshot once with the maintainer's interactive
2FA, explicit npmjs registry,
latesttag, disabled lifecycle scripts, and public access; then verify its registry version and integrity. - Reverify both release-tag rulesets.
- Create the
npmGitHub environment. - Add separate typed environment policies for branch
mainand tagv*. - Verify both rulesets and both typed environment policies.
- Configure npm Trusted Publishing:
npx --yes npm@11.15.0 trust github @juststas/graph-mcp \
--file publish.yml \
--repo JustStas/Graph-MCP \
--env npm \
--allow-publish
Verify the saved repository, workflow filename, environment, and publish permission, then set npm publishing access to require 2FA and disallow traditional tokens.
The manual 0.6.1 bootstrap uses neither OIDC nor provenance, and its integrity-matched release workflow is a no-op that does not test the OIDC exchange. Version 0.6.2 is the first real OIDC publish and provenance check.
Recovery
Use workflow_dispatch from main with an existing protected tag to rerun publication. Use
prepare_only when only the verified tarball is needed. The release-tag rulesets prohibit
moving or deleting published v* tags. Never overwrite an npm version; recover from a bad
publication with a new patch release.
Architecture and runtime behavior
Claude Code or Codex --stdio--> Graph MCP --HTTPS--> Microsoft Graph API
|
~/.graph-mcp/
config.json
tokens-v2.enc
.key-v2
- Authentication uses OAuth 2.0 Authorization Code with PKCE or device code.
- Access-token refresh is serialized so concurrent Graph calls share one refresh.
- Graph requests use bounded timeouts, sliding-window rate limiting, and exponential retry
behavior that honors
Retry-Afteron throttled responses. - MCP protocol output is written to stdout; diagnostics are written to stderr.
Troubleshooting
Approval required during login
Confirm that the exact delegated permissions above are present and that required administrator consent has been granted.
403 Forbidden for one tool
The endpoint may need a delegated permission or administrator consent not available to the signed-in user. Check the tool's permission and your organizational policy.
Browser callback is unavailable
Call graph_auth_login with method: "device_code" and complete sign-in at the Microsoft
verification URL.
Configuration changed but the host still uses old values
Restart the MCP server or host so the process reloads config.json and its environment.
Environment variables override saved Client ID and Tenant ID values.
Upgraded from the Python release and appear logged out
This is expected once. Run graph_auth_login; the Node runtime creates tokens-v2.enc and
.key-v2 without changing the old tokens.enc and .key files.
Disclaimer
This project is an independent open-source effort and is not affiliated with, endorsed by, or sponsored by Microsoft Corporation. Microsoft, Microsoft Teams, Outlook, Microsoft 365, Microsoft Graph, and Azure are trademarks of the Microsoft group of companies.
This software is provided "as is", without warranty of any kind. Use it at your own risk. The authors accept no liability for damages, data loss, or security issues arising from its use. You are responsible for complying with your organization's policies and Microsoft's API Terms of Use.
This software accesses Microsoft services on your behalf using your own credentials and app registration. Data retrieved from Microsoft Graph (including mail, messages, calendar events, meetings, and files) is passed to the model that invoked the tool. Follow BP and your organization's data-handling, retention, and acceptable-use requirements when using cloud-hosted AI models.
License
MIT — see LICENSE.
