{
  "markdown": "# Minerva MCP\n\n**Microsoft 365 and Azure administration for AI assistants — including the parts Microsoft Graph cannot reach.**\n\n[![CI](https://github.com/HoussemMak/minerva-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/HoussemMak/minerva-mcp/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![MCP 2026-07-28](https://img.shields.io/badge/MCP-2026--07--28-6b46c1.svg)](https://modelcontextprotocol.io/specification/2026-07-28)\n\nAn [MCP](https://modelcontextprotocol.io) server that lets Claude, Copilot, VS Code\nor any MCP client query and administer a Microsoft 365 tenant in plain language.\n\n---\n\n## Why another Microsoft 365 MCP server?\n\nThere are already good ones, and this is not trying to replace them.\n\n| | [Lokka](https://github.com/merill/lokka) | [ms-365-mcp-server](https://github.com/softeria/ms-365-mcp-server) | **Minerva MCP** |\n|---|:---:|:---:|:---:|\n| Microsoft Graph | ✅ | ✅ 300+ tools | ✅ generic call |\n| Azure Resource Manager | ✅ | — | ✅ |\n| **Exchange Online cmdlets** | — | — | ✅ |\n| **SharePoint / PnP cmdlets** | — | — | ✅ |\n| **Teams Admin cmdlets** | — | — | ✅ |\n| **Purview / compliance cmdlets** | — | — | ✅ |\n| Risk classification per action | — | — | ✅ |\n| Human approval before writes | — | read-only switch | ✅ native to the protocol |\n| MCP 2026-07-28 (stateless) | — | — | ✅ |\n\n**If you only need Graph, use Lokka or ms-365-mcp-server.** They are excellent,\nmore mature, and more widely used. Lokka in particular is written by a Microsoft\nEntra product manager.\n\nReach for this one when you hit the wall everyone hits eventually: **a large part\nof Microsoft 365 administration has no Graph API.** Mailbox permissions,\ntransport rules, message trace, retention policies, sensitivity labels, Teams\ncalling policies, SharePoint term stores, tenant-wide Exchange settings — those\nlive in PowerShell and nowhere else. This server exposes them, with a\nrisk classification and a confirmation step in front of anything that writes.\n\n---\n\n## How it fits together\n\n```mermaid\nflowchart LR\n    A[\"AI client<br/>Claude · VS Code · Copilot Studio\"]\n    B[\"minerva-mcp\"]\n    C{\"Risk<br/>classifier\"}\n    D[\"Human<br/>confirmation\"]\n    G[\"Microsoft Graph<br/>Azure ARM · Search\"]\n    P[\"PowerShell 7<br/>Exchange · PnP · Teams · Purview\"]\n    M[\"Microsoft<br/>authorizes the token\"]\n\n    A -->|\"stdio or HTTP\"| B\n    B --> C\n    C -->|\"read\"| G\n    C -->|\"write\"| D\n    D -->|\"signed mandate\"| G\n    D -->|\"signed mandate\"| P\n    C -->|\"read\"| P\n    G --> M\n    P --> M\n```\n\nReads go straight through. Writes stop at a classifier, come back to you as an\n`input_required` describing the action and its consequences, and only run once\nyou confirm. Microsoft still has the final word: the server forwards a token, it\nnever grants access.\n\n## Install\n\nNothing to clone. Add it to your client and sign in on first use.\n\n### Claude Desktop\n\n`claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"minerva\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"minerva-mcp\", \"--stdio\"]\n    }\n  }\n}\n```\n\n### VS Code\n\n`.vscode/mcp.json`:\n\n```json\n{\n  \"servers\": {\n    \"minerva\": {\n      \"type\": \"stdio\",\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"minerva-mcp\", \"--stdio\"]\n    }\n  }\n}\n```\n\nMore, including read-only and app-only setups, in [`examples/`](examples/).\n\nOn the first tool call the server prints a device-code prompt **on stderr**:\nopen the link, enter the code, done. No app registration needed to try it — it\nuses the public Microsoft Graph PowerShell client by default.\n\n**Requirements:** Node.js 20+. For the PowerShell tools, [PowerShell 7+](https://aka.ms/powershell)\nand the modules you intend to use (`ExchangeOnlineManagement`, `PnP.PowerShell`,\n`MicrosoftTeams`).\n\n---\n\n## What you can ask\n\n> *\"Which mailboxes have full access delegated to someone outside their department?\"*\n> → `execute_exchange_online` with `Get-MailboxPermission`, no Graph equivalent exists.\n\n> *\"Show me every conditional access policy that does not require MFA, and our Secure Score.\"*\n> → `generate_security_report` + `microsoft_graph_call`.\n\n> *\"Create a shared mailbox for invoicing and give the finance team access.\"*\n> → classified as a write: the server describes the action and its consequences,\n> and waits for your confirmation before doing anything.\n\n---\n\n## Tools\n\n| Tool | What it does | Graph equivalent? |\n|---|---|:---:|\n| `microsoft_graph_call` | Any Microsoft Graph call (v1.0 or beta) | — |\n| `azure_arm_call` | Any Azure Resource Manager call | — |\n| `microsoft_search` | Unified Microsoft Search | — |\n| `get_tenant_context` | Organisation, licences, Secure Score | — |\n| `generate_security_report` | Tenant security posture | — |\n| `execute_exchange_online` | Exchange Online cmdlets | ❌ none |\n| `execute_pnp_sharepoint` | PnP PowerShell (SharePoint) cmdlets | ❌ none |\n| `execute_teams_admin` | Teams Admin cmdlets | ❌ none |\n| `execute_purview_compliance` | Purview / compliance cmdlets | ❌ none |\n\nRestrict the surface with `--tools=microsoft_graph_call,get_tenant_context`, or\nforbid every write with `--read-only`.\n\n---\n\n## Approval, and why it is not optional\n\nA model reads untrusted content — an email subject, a site name, a document —\nand decides which tool to call. Prompt injection is not hypothetical, and the\ntools here can delete mailboxes.\n\nSo writes do not simply execute. Every action is classified `auto`, `confirmed`\nor `critical` from the HTTP verb and path (or the PowerShell verb). Anything\nabove `auto` makes the server answer `input_required` under\n[MCP's Multi Round-Trip Requests](https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr):\nyour client shows what is about to happen and its consequences, you confirm, and\nonly then does it run.\n\nThe confirmation is a signed mandate (HMAC) bound to the caller, the method, and\na digest of the arguments, valid five minutes. A mandate obtained to delete\n`/users/1` cannot be replayed on `/users/999`, and cannot be presented by anyone\nelse.\n\nIf your client does not support elicitation, nothing is silently executed: the\nserver returns an error saying the action needs confirmation.\n\n---\n\n## Options\n\nEvery flag has an environment variable equivalent.\n\n### Transport\n\n| Flag | Env | Default | |\n|---|---|---|---|\n| `--stdio` | `MINERVA_TRANSPORT=stdio` | HTTP | JSON-RPC over stdin/stdout |\n| `--port=<n>` | `MCP_SERVER_PORT` | `3001` | HTTP listen port |\n\nIn stdio mode **all logs go to stderr** — stdout belongs to the protocol.\n\n### Authentication\n\n| Flag | Env | |\n|---|---|---|\n| `--auth=<mode>` | `MINERVA_AUTH_MODE` | `bearer`, `device-code`, `client-secret` |\n| `--tenant=<id>` | `MINERVA_TENANT_ID`, `AZURE_TENANT_ID` | Entra tenant |\n| `--client-id=<id>` | `MINERVA_CLIENT_ID`, `AZURE_CLIENT_ID` | Application |\n| `--client-secret=<s>` | `AZURE_CLIENT_SECRET` | App-only secret |\n\nDefaults: `bearer` over HTTP (the caller supplies the token), `device-code` over\nstdio, `client-secret` as soon as a secret is present. Each resource gets its own\ntoken: Graph, ARM, Exchange, Purview, SharePoint and Teams do not share an\naudience.\n\n### Scope\n\n| Flag | Env | |\n|---|---|---|\n| `--read-only` | `MINERVA_READ_ONLY` | Refuse every write |\n| `--tools=<a,b,c>` | `MINERVA_TOOLS` | Register only these tools |\n| `--ps=<mode>` | `MINERVA_PS_EXECUTOR` | `auto`, `redis`, `local`, `none` |\n| `--sharepoint-url=<url>` | `MINERVA_SHAREPOINT_URL` | Required by the PnP tool locally |\n| `--allowed-origins=<a,b>` | `MINERVA_ALLOWED_ORIGINS` | Browser origins allowed on `/mcp` |\n| `--redis-url=<url>` | `REDIS_URL` | Queue for an external PowerShell runner |\n| — | `MINERVA_MRTR_SECRET` | Signing key for approval mandates |\n| — | `MINERVA_PWSH_PATH` | Path to `pwsh` |\n| — | `MINERVA_REQUIRE_VERIFIED_TOKENS` | Refuse tokens whose signature cannot be verified |\n\n`--read-only` reuses the same risk classifier as the approval gate, so it covers\nPowerShell cmdlets too, not just HTTP verbs.\n\n---\n\n## Protocol support\n\nSpeaks **MCP 2026-07-28**, and still serves handshake-era clients until\n**2027-07-28**.\n\n| | |\n|---|---|\n| Stateless core (per-request `_meta`, no `initialize`, no `Mcp-Session-Id`) | ✅ |\n| `server/discover` | ✅ |\n| Multi Round-Trip Requests (`input_required`) | ✅ — this is the approval mechanism |\n| `MCP-Protocol-Version` / `Mcp-Method` / `Mcp-Name` headers, base64 sentinel | ✅ validated against the body |\n| `ttlMs` / `cacheScope` on lists | ✅ always `private` |\n| Version negotiation (`-32022` listing supported versions) | ✅ |\n| `Origin` validation (anti DNS-rebinding) | ✅ |\n| GET/DELETE on the MCP endpoint | ✅ `405`, removed by this revision |\n| Handshake era (`initialize`) | ✅ until 2027-07-28 |\n\nOne endpoint serves both generations; the request body decides which. Both read\nthe **same tool registry**, and a test compares them against a frozen contract so\nthey cannot drift.\n\n---\n\n## Security\n\nRead [SECURITY.md](SECURITY.md) before deploying. The three things that matter:\n\n1. **This server grants nothing.** It forwards a Microsoft token; Microsoft\n   authorizes. A restricted token gives a restricted server.\n2. **Graph tokens cannot be verified by a third party** — they carry a `nonce`\n   header that only Graph can check. Their `tid`/`oid` claims are therefore\n   self-reported. The server validates them structurally, flags them\n   `verified: false`, and binds approval mandates to a **fingerprint of the token\n   itself** rather than to the claims alone. A token that *should* have verified\n   and did not is rejected.\n3. **Local PowerShell mode isolates nothing.** Cmdlets run with the current\n   user's rights. Do not expose it to a caller you do not trust.\n\nCmdlet names and parameters never reach the script as code: they travel through\nenvironment variables, the name is validated against a strict `Verb-Noun`\npattern, and the call goes through `Get-Command` plus the call operator.\n\nReport a vulnerability privately — see [SECURITY.md](SECURITY.md).\n\n---\n\n## What this does not do\n\n- **No process isolation** in local mode.\n- **No multi-tenant brokering** — one server, one identity.\n- **No signed audit ledger.** Audit events are JSON on stderr, not a signed,\n  offline-verifiable record.\n\nThose three belong to [Minerva-IA](https://github.com/HoussemMak), the commercial\nplatform this server was extracted from. Everything in this repository works\nwithout it.\n\n---\n\n## Development\n\n```bash\nnpm install\nnpm run dev:stdio     # stdio, hot reload\nnpm run verify        # typecheck + tests + secret scan\nnpm run build && node scripts/smoke-stdio.mjs\n```\n\nTests need no credentials and touch no network.\n\nPull requests are closed until 2027-07-31 — see [CONTRIBUTING.md](CONTRIBUTING.md)\nfor why, and for what is welcome in the meantime (bug reports very much are).\n\n## Licence\n\nMIT — see [LICENSE](LICENSE).\n",
  "bytes": 10850,
  "sha": "584a4ea0b5b2d80d301019c783e0a6f257834dc72d52b5d9de9090fbc778b2d1",
  "repo_slug": "houssemmak/minerva-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_houssemmak_minerva_mcp_05d958fd/readme"
}