{
  "markdown": "# ms-graph-mcp\n\n<!-- mcp-name: io.github.nitin27may/ms-graph-mcp -->\n\n[![PyPI](https://img.shields.io/pypi/v/ms-graph-mcp)](https://pypi.org/project/ms-graph-mcp/)\n[![Release](https://img.shields.io/github/v/release/nitin27may/ms-graph-mcp)](https://github.com/nitin27may/ms-graph-mcp/releases/latest)\n[![Docs](https://img.shields.io/badge/docs-nitinksingh.com-0f766e)](https://nitinksingh.com/ms-graph-mcp/)\n[![CI](https://github.com/nitin27may/ms-graph-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/nitin27may/ms-graph-mcp/actions/workflows/ci.yml)\n[![Python](https://img.shields.io/badge/python-3.12%20%7C%203.13-blue)](https://pypi.org/project/ms-graph-mcp/)\n[![Container](https://img.shields.io/badge/ghcr.io-ms--graph--mcp-blue)](https://github.com/nitin27may/ms-graph-mcp/pkgs/container/ms-graph-mcp)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green)](https://github.com/nitin27may/ms-graph-mcp/blob/main/LICENSE)\n[![MCP](https://img.shields.io/badge/MCP-server-orange)](https://modelcontextprotocol.io)\n\n[Releases](https://github.com/nitin27may/ms-graph-mcp/releases) · [Changelog](CHANGELOG.md)\n\nA [Model Context Protocol](https://modelcontextprotocol.io) server for **Microsoft Graph** — 85\ntools across mail, calendar, meetings (including transcripts), Teams chat, files, SharePoint,\nsearch, people, contacts, directory, tasks and OneNote, over **stdio** or **Streamable HTTP**.\n\n**Signs you in with your own Microsoft account** — browser SSO, no token to paste, no client secret.\n\n![The ms-graph-mcp tools listed in VS Code's Configure Tools panel](https://raw.githubusercontent.com/nitin27may/ms-graph-mcp/main/docs/tools.png)\n\n- **No `msgraph-sdk`, no `azure-identity`** — the Graph client is raw `httpx`, so the dependency tree\n  stays small and the wire behaviour is inspectable.\n- **Read/write separation is enforced, not advisory** — write tools are hidden *and* refused unless\n  the caller explicitly opts in.\n- **Auth-agnostic by default** — tools receive an already-acquired Graph token via the request\n  context. The server can also perform its own on-behalf-of exchange when you want it to act as a\n  proper OAuth resource server.\n\n> **Status: early.** Extracted from a production agent platform where it has been running against a\n> real tenant. The code is battle-tested; the packaging and public API surface are newer. Expect the\n> config surface to move before 1.0.\n\n## Install\n\nRequires **Python 3.12+**. Two paths — pick the one that matches what you want to do.\n\n### A. Use the package\n\nNothing to clone. [uv](https://docs.astral.sh/uv/) runs it straight from PyPI:\n\n```bash\nuvx --from ms-graph-mcp ms-graph-mcp          # stdio, for an MCP client\nuvx --from ms-graph-mcp ms-graph-mcp-http     # Streamable HTTP\n```\n\nor install it into an environment:\n\n```bash\npip install ms-graph-mcp\n```\n\nRelease candidates are published too. pip skips them unless you ask:\n\n```bash\npip install --pre ms-graph-mcp          # newest, including candidates\npip install ms-graph-mcp==0.3.0rc1      # a specific one; no --pre needed for an exact pin\n```\n\nA container image is on GHCR for the HTTP transport — see\n[docs/hosting.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/hosting.md#docker).\n\nAlso listed on the [official MCP registry](https://modelcontextprotocol.io/registry) as\n`io.github.nitin27may/ms-graph-mcp`.\n\n> **TestPyPI is not a distribution channel.** Every release is published there first, but that is a\n> rehearsal of the publishing process: it can be wiped without notice and does not mirror PyPI, so\n> installing from it needs `--extra-index-url https://pypi.org/simple/` just to resolve ordinary\n> dependencies. Use PyPI, or `--pre`.\n\n### B. Run from source\n\nFor hacking on it, forking it, or running an unreleased change:\n\n```bash\ngit clone https://github.com/nitin27may/ms-graph-mcp\ncd ms-graph-mcp\nuv sync                       # creates .venv and installs everything\nuv run ms-graph-mcp           # check it starts\n```\n\n`uv sync` is the only setup step. See\n[CONTRIBUTING.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/CONTRIBUTING.md) before\nopening a pull request — the tool allowlists and the tier separation have invariants that are\nenforced rather than advisory.\n\nTo point an MCP client at your clone, you need its absolute path — clients do not inherit your\nworking directory and most do not expand `~`:\n\n```bash\ncd ms-graph-mcp && pwd\n# /Users/you/workspace/ms-graph-mcp\n```\n\nThen use the source form of the config in\n[Configure your MCP client](#running-from-source-instead) below.\n\n## Set up the Entra app\n\nYou need an **Entra ID app registration** — about two minutes. **Do not create a client secret:**\nthis registers as a *public client*, which signs you in through your browser using PKCE. A secret on\na program running on your own machine would be readable by anyone with the config file, which is why\nthe flow is designed not to need one. Nothing goes into a config file except two ids, neither of\nwhich is sensitive.\n\nIn the [Entra portal](https://entra.microsoft.com) → **App registrations** → **New registration**:\n\n- **Name:** anything, e.g. `ms-graph-mcp`\n- **Supported account types:** *Accounts in this organizational directory only*\n- **Redirect URI:** select **Public client/native**, value `http://localhost`\n\nLeave **Certificates & secrets** alone — you do not need anything from it.\n\nThen, on the new app:\n\n- **Authentication** → enable **Allow public client flows**\n- **API permissions** → **Add a permission** → **Microsoft Graph** → **Delegated permissions**, and\n  add what you want the agent to reach. A sensible read-only starting set:\n\n  ```\n  User.Read  Mail.Read  Calendars.Read  Files.Read.All\n  People.Read  Chat.Read  Tasks.Read  Notes.Read  Contacts.Read\n  ```\n\n  The complete copy-paste consent sets — and which permission each individual tool needs — are in\n  [docs/permissions.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/permissions.md).\n\nCopy the **Application (client) ID** and **Directory (tenant) ID** from the Overview page. That is\neverything you need.\n\n## Configure your MCP client\n\nEvery MCP client that speaks stdio takes the same three things — a command, its arguments, and an\nenvironment block:\n\n```jsonc\n{\n  \"command\": \"uvx\",\n  \"args\": [\"--from\", \"ms-graph-mcp\", \"ms-graph-mcp\"],\n  \"env\": {\n    \"GRAPH_MCP_CLIENT_ID\": \"<application-client-id>\",\n    \"GRAPH_MCP_TENANT_ID\": \"<directory-tenant-id>\"\n  }\n}\n```\n\nWhere that block goes, and what the surrounding key is called, differs:\n\n| Client | Config file | Key |\n|---|---|---|\n| VS Code | `.vscode/mcp.json` (workspace), or **MCP: Open User Configuration** | `servers` |\n| Claude Code | `claude mcp add …` — no file to edit | — |\n| Claude Desktop | macOS `~/Library/Application Support/Claude/claude_desktop_config.json` · Windows `%APPDATA%\\Claude\\claude_desktop_config.json` | `mcpServers` |\n| Cursor | `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global) | `mcpServers` |\n| Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers` |\n| MCP Inspector | command line, `-e` flags | — |\n\nThe first sign-in opens your browser for normal Microsoft 365 SSO — including MFA and conditional\naccess. The result is cached in `~/.ms-graph-mcp/token_cache.json`, owner-readable only, so it does\nnot prompt again.\n\n### VS Code\n\n```jsonc\n{\n  \"inputs\": [\n    { \"id\": \"clientId\", \"type\": \"promptString\", \"description\": \"Entra application (client) ID\" },\n    { \"id\": \"tenantId\", \"type\": \"promptString\", \"description\": \"Entra directory (tenant) ID\" }\n  ],\n  \"servers\": {\n    \"ms-graph\": {\n      \"type\": \"stdio\",\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"ms-graph-mcp\", \"ms-graph-mcp\"],\n      \"env\": {\n        \"GRAPH_MCP_CLIENT_ID\": \"${input:clientId}\",\n        \"GRAPH_MCP_TENANT_ID\": \"${input:tenantId}\"\n      }\n    }\n  }\n}\n```\n\nReload the window. VS Code prompts once for the two ids and remembers them, so this file is safe to\ncommit. Open the Chat view, switch to **Agent** mode, and the tools appear under the tools picker —\nthat is the panel in the screenshot above. **Confirm with** `MCP: List Servers`, which shows status\nand output if it does not connect.\n\n### Claude Code\n\n```bash\nclaude mcp add ms-graph \\\n  --env GRAPH_MCP_CLIENT_ID=<application-client-id> \\\n  --env GRAPH_MCP_TENANT_ID=<directory-tenant-id> \\\n  -- uvx --from ms-graph-mcp ms-graph-mcp\n```\n\n**Confirm with** `/mcp` inside Claude Code — it lists the server and its tools.\n\n### Claude Desktop\n\n```jsonc\n{\n  \"mcpServers\": {\n    \"ms-graph\": {\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"ms-graph-mcp\", \"ms-graph-mcp\"],\n      \"env\": {\n        \"GRAPH_MCP_CLIENT_ID\": \"<application-client-id>\",\n        \"GRAPH_MCP_TENANT_ID\": \"<directory-tenant-id>\"\n      }\n    }\n  }\n}\n```\n\n**Confirm by** quitting Claude Desktop fully — not just closing the window — reopening it, and\nlooking for the tools icon in the composer.\n\n### Cursor and Windsurf\n\nBoth use the same `mcpServers` shape as Claude Desktop, in the file named in the table above.\n\n### MCP Inspector\n\nThe quickest way to check the server independently of any client:\n\n```bash\nnpx @modelcontextprotocol/inspector \\\n  uvx --from ms-graph-mcp ms-graph-mcp \\\n  -e GRAPH_MCP_CLIENT_ID=<application-client-id> \\\n  -e GRAPH_MCP_TENANT_ID=<directory-tenant-id>\n```\n\nNeeds Node 22.19+. It opens a browser UI where you can list tools and call them by hand — worth\ndoing before blaming your client. There is a scriptable `--cli` mode too; see\n[docs/testing.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/testing.md).\n\n> **Pass variables with `-e`, not from your shell.** Inspector does not give the server it spawns\n> your environment, so `GRAPH_MCP_CLIENT_ID=… npx @modelcontextprotocol/inspector …` starts the\n> server with *no* client id. The `-e` flags go **after** the server command.\n\n### Running from source instead\n\nSame blocks as above — swap the command and args for your clone's absolute path:\n\n```jsonc\n\"command\": \"uv\",\n\"args\": [\"run\", \"--directory\", \"/Users/you/workspace/ms-graph-mcp\", \"ms-graph-mcp\"]\n```\n\nTwo things catch people out here:\n\n**`uv` must be on the client's PATH.** GUI apps launched from Finder or the Dock do not inherit your\nshell's PATH, so a client can fail to start the server with an unhelpful error. If that happens, put\nthe output of `which uv` in `\"command\"` instead of the bare name.\n\n**`--directory` is not optional.** Without it, `uv run` resolves against whatever directory the\nclient happened to launch from, which will not be the project.\n\n### Something not working?\n\n[docs/troubleshooting.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/troubleshooting.md)\ncovers the Entra errors, Conditional Access, corporate TLS proxies, and the \"server disconnected\"\nthat is almost always a startup error your client is hiding.\n\n## Configuration\n\nTwo settings get you running; everything else has a working default.\n\n| Env var | Purpose |\n|---|---|\n| `GRAPH_MCP_CLIENT_ID` | Entra application (client) id. Enables interactive sign-in. |\n| `GRAPH_MCP_TENANT_ID` | Entra directory (tenant) id. Defaults to `common`. |\n| `GRAPH_MCP_SCOPES` | Comma-separated delegated scopes to request. Defaults to a read-only set. |\n| `GRAPH_MCP_TOOLSETS` | Which tool profiles to expose. Defaults to `core`. See below. |\n| `GRAPH_MCP_WRITE_SCOPE` | `true` to expose the 23 write tools. **Default off.** |\n| `GRAPH_MCP_READ_ONLY` | `true` to remove the write tier from the deployment entirely. |\n| `GRAPH_MCP_LOG_LEVEL` | `INFO` logs every Graph call to stderr. Defaults to `WARNING`. |\n\n**Every setting, both deployment shapes, and the hosted/OBO options** are in\n[docs/configuration.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/configuration.md).\nFor running it as a service, see\n[docs/hosting.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/hosting.md).\n\n## Toolset profiles\n\n85 tools is a lot to put in front of a model. `GRAPH_MCP_TOOLSETS` selects named profiles, each a\ngroup of namespaces:\n\n| Profile | Namespaces | Read tools | Approx. tokens |\n|---|---|---:|---:|\n| `core` *(default)* | search, mail, calendar, files, people | 23 | ~4,200 |\n| `mail` | mail | 5 | ~800 |\n| `calendar` | calendar | 6 | ~1,500 |\n| `meetings` | meetings, calendar | 13 | ~2,900 |\n| `files` | files | 6 | ~900 |\n| `chat` | chat | 7 | ~1,000 |\n| `people` | people | 5 | ~750 |\n| `directory` | directory, people | 12 | ~1,900 |\n| `tasks` | tasks | 5 | ~830 |\n| `notes` | notes | 4 | ~570 |\n| `search` | search | 1 | ~290 |\n| `all` | everything | 53 | ~9,200 |\n\nCombine them with commas:\n\n```\nGRAPH_MCP_TOOLSETS=mail,calendar,tasks\n```\n\n**`core` is the default, so some tools are not advertised unless you ask for them.** If you want\nTeams chat, Planner, OneNote, meeting transcripts or directory lookups, name those profiles — or set\n`GRAPH_MCP_TOOLSETS=all` to expose everything.\n\nOver HTTP a caller may send `X-Toolsets` to narrow further for one request. **It can only narrow.**\nThe startup value is a ceiling, so a client asking for `all` gains nothing the deployment did not\nalready enable. This filters *visibility*, not authority — the write-scope and internal-tier gates\nare what actually stop a call.\n\n## Tool surface\n\nThree tiers, one auth seam.\n\n| Tier | Count | Exposed when | Examples |\n|---|---:|---|---|\n| **Read** | 53 | always | `calendar_list_upcoming_events`, `mail_search`, `meetings_get_transcript`, `files_search`, `search_query` |\n| **Write** | 23 | `X-Write-Scope: true` | `mail_send`, `calendar_create_event`, `files_create_sharing_link`, `tasks_complete_todo` |\n| **Internal** | 9 | `X-Internal-Scope: true`, machine principal only | `graph_request` passthrough, drive walk/upload, message attachments, app-only `probe_graph_access` |\n\nThe internal tier is not part of the agent surface. A model sees **76 agent-visible** tools.\nBy namespace: mail 11 · tasks 11 · calendar 10 · files 10 · chat 8 · directory 7 · meetings 7 ·\npeople 6 · notes 5 · search 1.\n\nTool names are namespaced by Graph permission family rather than by Microsoft product, because real\nquestions cross product boundaries — `files_` covers OneDrive *and* SharePoint document libraries,\nwhich are the same `driveItem` resource underneath.\n\nEvery tool declares MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`) so clients\nknow what needs confirming, and every description names the delegated permission it requires.\n\n> **Renamed in 0.2.0.** Every pre-0.2.0 tool name still works as an alias, and will keep working\n> until 0.4.0. Aliases are honoured by `tools/call` but never advertised in `tools/list`, so they\n> cost no context.\n\n## Documentation\n\nPublished as a searchable site at **<https://nitinksingh.com/ms-graph-mcp/>**.\n\n| | |\n|---|---|\n| [Documentation site](https://nitinksingh.com/ms-graph-mcp/) | Everything below, searchable and cross-linked |\n| [docs/configuration.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/configuration.md) | Every environment variable, split by deployment shape |\n| [docs/permissions.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/permissions.md) | Every tool and the delegated permission it needs, plus copy-paste consent sets |\n| [docs/hosting.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/hosting.md) | Streamable HTTP, headers, Docker and GHCR |\n| [docs/troubleshooting.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/troubleshooting.md) | Entra errors, Conditional Access, corporate TLS proxies |\n| [docs/debugging.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/debugging.md) | Logs, error codes, and the auth failures people actually hit |\n| [docs/graph-coverage.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/graph-coverage.md) | What this covers of the Graph v1.0 surface, what it does not, and what is out of scope |\n| [docs/roadmap.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/roadmap.md) | What is not done yet |\n| [docs/testing.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/testing.md) | Running the suite, how it is arranged, and MCP Inspector |\n| [CONTRIBUTING.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/CONTRIBUTING.md) | Dev setup, the add-a-tool checklist, and the invariants enforced by tests |\n| [SECURITY.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/SECURITY.md) | Reporting vulnerabilities, and what to change before exposing this beyond localhost |\n| [CHANGELOG.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/CHANGELOG.md) | Release history |\n| [CLAUDE.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/CLAUDE.md) | Architecture and the non-obvious traps, for coding agents and new contributors alike |\n\n## What's next\n\nSharePoint sites and lists are the largest gap; directory completion, file move/delete, and mail\ndrafts follow. Sovereign clouds (GCC High / 21Vianet) are unsupported today. The full list, and what\nis deliberately out of scope, is in\n[docs/roadmap.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/docs/roadmap.md).\n\n## Getting help\n\n- **Setup and app-registration questions** →\n  [Discussions](https://github.com/nitin27may/ms-graph-mcp/discussions)\n- **Bugs** → [Issues](https://github.com/nitin27may/ms-graph-mcp/issues), with the output of running\n  the server in a terminal\n- **Security vulnerabilities** → never a public issue; see\n  [SECURITY.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/SECURITY.md)\n\nNever paste an access token, client secret or shared secret into any of them.\n\n## Contributing\n\nIssues and pull requests are welcome. Start with\n[CONTRIBUTING.md](https://github.com/nitin27may/ms-graph-mcp/blob/main/CONTRIBUTING.md);\nparticipation is governed by the\n[Code of Conduct](https://github.com/nitin27may/ms-graph-mcp/blob/main/CODE_OF_CONDUCT.md).\n\n```bash\nuv sync\nuv run pytest -q            # full suite, offline, about two seconds\nuv run ruff check .\nuv run ruff format .\n```\n\n## License\n\nMIT — see [LICENSE](https://github.com/nitin27may/ms-graph-mcp/blob/main/LICENSE).\n",
  "bytes": 18075,
  "sha": "28862893d8efe0c90404c84292be510377b431ed0f502ba5cba39cd7b0b7d4e1",
  "repo_slug": "nitin27may/ms-graph-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_nitin27may_ms_graph_mcp_41da3457/readme"
}