Metadata-Version: 2.4
Name: m365-service-comms-mcp
Version: 0.1.2
Summary: Read-only Model Context Protocol (MCP) server for the Microsoft Graph Service Communications API — exposes M365 service health and Message Center posts to AI agents.
Project-URL: Homepage, https://github.com/trobichaux/m365-service-comms-mcp
Project-URL: Repository, https://github.com/trobichaux/m365-service-comms-mcp
Project-URL: Issues, https://github.com/trobichaux/m365-service-comms-mcp/issues
Project-URL: Changelog, https://github.com/trobichaux/m365-service-comms-mcp/releases
Author-email: Tim Robichaux <timrob@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Keywords: m365,mcp,message-center,microsoft-graph,model-context-protocol,service-health
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.11
Requires-Dist: azure-identity>=1.19.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: mcp>=1.2.0
Requires-Dist: msal-extensions>=1.2.0
Requires-Dist: msal>=1.31.0
Requires-Dist: pydantic>=2.9.0
Requires-Dist: tenacity>=9.0.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.32.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.7.0; extra == 'dev'
Description-Content-Type: text/markdown

# m365-service-comms-mcp

> ⚠️ **Preview (v0.1).** Read-only [Model Context Protocol](https://modelcontextprotocol.io)
> server for the Microsoft Graph **Service Communications API**. Exposes M365
> service health and Message Center posts to AI agents (Claude, GitHub Copilot,
> Cursor, VS Code, Claude Desktop, etc.).

[![CI](https://github.com/trobichaux/m365-service-comms-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/trobichaux/m365-service-comms-mcp/actions/workflows/ci.yml)
[![CodeQL](https://github.com/trobichaux/m365-service-comms-mcp/actions/workflows/codeql.yml/badge.svg)](https://github.com/trobichaux/m365-service-comms-mcp/actions/workflows/codeql.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)

## Status

| Item | State |
|---|---|
| Version | `0.1.0` (preview) |
| Tools | 3 — `list_service_health`, `list_message_center_posts`, `get_message_center_post` |
| Auth | Delegated only (browser sign-in / device code) |
| Transport | stdio |
| Distribution | PyPI via `uvx` |
| Listing | GitHub MCP Registry (planned for v0.1.08) |

Application-permission auth, additional tools, Docker image, and additional
documentation are deferred to **v1.0**.

## Why this exists

There is no published MCP server today that wraps the Graph
[Service Communications API](https://learn.microsoft.com/en-us/graph/api/resources/service-communications-api-overview)
with delegated authentication. The closest alternatives:

- [`Softeria/ms-365-mcp-server`](https://github.com/Softeria/ms-365-mcp-server)
  — delegated auth, but **no service health / message center coverage**.
- [`okapi-ca/ms-365-admin-mcp-server`](https://github.com/okapi-ca/ms-365-admin-mcp-server)
  — covers service health and messages, but **application permissions only**
  (cannot be used by an admin signing in interactively).

This server fills the gap: **delegated auth + service health + message center**,
in Python so M365 admins on any platform can install it with a single
`uvx` command.

## Quickstart

### Zero-setup quickstart (no app registration required)

You don't need to register your own Entra app — by default, the server uses the
**Microsoft Graph PowerShell** well-known multi-tenant public client
(`14d82eec-204b-4c2f-b7e8-296a70dab67e`) so any admin can sign in via browser
and grant consent on first use.

```jsonc
// .vscode/mcp.json (or any MCP-compatible client config)
{
  "servers": {
    "m365-svc-comms": {
      "type": "stdio",
      "command": "uvx",
      "args": ["m365-service-comms-mcp"]
    }
  }
}
```

Then ask your agent:

> *List the M365 services and tell me which ones are degraded.*

A browser will open the first time, prompting you to sign in to your tenant.
On first sign-in, an admin must grant consent for the
`ServiceHealth.Read.All` and `ServiceMessage.Read.All` Graph permissions
(this is a one-click step in the consent prompt). Subsequent runs reuse the
cached token \u2014 no browser unless the token expires.

### Try it without any tenant (`--demo` mode)

Verify the MCP wire protocol works with your AI client *before* signing in:

```jsonc
{
  "servers": {
    "m365-svc-comms-demo": {
      "type": "stdio",
      "command": "uvx",
      "args": ["m365-service-comms-mcp", "--demo"]
    }
  }
}
```

You should see 3 services returned, with **Microsoft Teams** flagged as
`serviceDegradation` (canned data).

### Use your own Entra app registration (optional)

If you want a dedicated audit identity in your tenant (so audit logs show
your app's display name instead of "Microsoft Graph PowerShell"), register
your own Entra app following [Entra app registration](#entra-app-registration)
below, then set `M365_TENANT_ID` and `M365_CLIENT_ID`.

### Verify your setup

```sh
uvx m365-service-comms-mcp --auth-test
```

You should see (using defaults):

```
Tenant ID : organizations
Client ID : 14d82eec-204b-4c2f-b7e8-296a70dab67e
            (using the Microsoft Graph PowerShell public client \u2014 no Entra app registration needed)
Auth flow : interactive-browser (default)
Acquiring access token \u2026
\u2713  Token acquired.
Probing https://graph.microsoft.com/v1.0/admin/serviceAnnouncement/healthOverviews?$top=1 \u2026
\u2713  Graph responded HTTP 200 (returned 1 healthOverview record(s)).
Auth test passed.
```

## Recommended testing tenant

If your primary tenant is locked down (typical for large organizations
including Microsoft itself), use a free
[**Microsoft 365 Developer Program**](https://developer.microsoft.com/microsoft-365/dev-program)
sandbox tenant. It comes with:

- An E5 license
- 25 pre-provisioned admin and user accounts
- Full Global Administrator rights for the tenant owner
- Auto-renewing 90-day subscription as long as you stay active

Sign up takes about 10 minutes.

## Entra app registration (optional, advanced)

You only need this if you want a dedicated audit identity instead of the
default "Microsoft Graph PowerShell" client. The walkthrough below assumes the
[Microsoft Entra admin center](https://entra.microsoft.com) UI as of 2026.

1. Go to **Microsoft Entra admin center \u2192 Identity \u2192 Applications \u2192
   App registrations \u2192 + New registration**.
2. **Name**: `m365-service-comms-mcp` (or whatever you like).
3. **Supported account types**: *Accounts in this organizational directory only
   (single tenant)*.
4. **Redirect URI**: select **Public client/native (mobile & desktop)** and
   enter `http://localhost`.
5. Click **Register**. Copy the **Application (client) ID** and **Directory
   (tenant) ID** from the Overview page \u2014 these become `M365_CLIENT_ID` and
   `M365_TENANT_ID` below.
6. Go to **API permissions \u2192 + Add a permission \u2192 Microsoft Graph \u2192 Delegated
   permissions**. Add:
   - `ServiceHealth.Read.All`
   - `ServiceMessage.Read.All`
7. Click **Grant admin consent for &lt;your tenant&gt;** and confirm. Both
   permissions should now show **Granted**.

The signed-in user must hold one of these directory roles (regardless of
whether you use the default client or your own app):

- Service Support Administrator
- Helpdesk Administrator
- Global Reader
- Global Administrator

## MCP client configuration

Set these two environment variables in whatever MCP client you use:

```
M365_TENANT_ID=<directory-tenant-guid>
M365_CLIENT_ID=<application-client-guid>
```

### VS Code (Copilot Chat)

Create `.vscode/mcp.json` in your workspace, or add to your user `mcp.json`:

```jsonc
{
  "servers": {
    "m365-svc-comms": {
      "type": "stdio",
      "command": "uvx",
      "args": ["m365-service-comms-mcp"],
      "env": {
        "M365_TENANT_ID": "00000000-0000-0000-0000-000000000000",
        "M365_CLIENT_ID": "00000000-0000-0000-0000-000000000000"
      }
    }
  }
}
```

### Claude Desktop

Add to `claude_desktop_config.json` (Settings → Developer → Edit Config):

```jsonc
{
  "mcpServers": {
    "m365-svc-comms": {
      "command": "uvx",
      "args": ["m365-service-comms-mcp"],
      "env": {
        "M365_TENANT_ID": "00000000-0000-0000-0000-000000000000",
        "M365_CLIENT_ID": "00000000-0000-0000-0000-000000000000"
      }
    }
  }
}
```

### Cursor

Add to `~/.cursor/mcp.json`:

```jsonc
{
  "mcpServers": {
    "m365-svc-comms": {
      "command": "uvx",
      "args": ["m365-service-comms-mcp"],
      "env": {
        "M365_TENANT_ID": "00000000-0000-0000-0000-000000000000",
        "M365_CLIENT_ID": "00000000-0000-0000-0000-000000000000"
      }
    }
  }
}
```

### GitHub Copilot CLI

Add to `~/.github/copilot/mcp.json`:

```jsonc
{
  "servers": {
    "m365-svc-comms": {
      "type": "stdio",
      "command": "uvx",
      "args": ["m365-service-comms-mcp"],
      "env": {
        "M365_TENANT_ID": "00000000-0000-0000-0000-000000000000",
        "M365_CLIENT_ID": "00000000-0000-0000-0000-000000000000"
      }
    }
  }
}
```

### Headless / CI (device-code flow)

If your environment can't open a browser (SSH session, container, CI), set
`M365_AUTH_DEVICE_CODE=1`. The server will then print a code and a URL for you
to complete sign-in from a different device.

## Verify your setup

After configuring, run:

```sh
uvx m365-service-comms-mcp --auth-test
```

You should see:

```
Tenant ID : <your-tenant-guid>
Client ID : <your-app-client-guid>
Auth flow : interactive-browser (default)
Acquiring access token …
✓  Token acquired.
Probing https://graph.microsoft.com/v1.0/admin/serviceAnnouncement/healthOverviews?$top=1 …
✓  Graph responded HTTP 200 (returned 1 healthOverview record(s)).
Auth test passed. ServiceHealth.Read.All is granted and admin consent is in place.
```

Then in your AI client, ask:

> *Use the m365-svc-comms server to list the current Microsoft 365 service health.*

You should see real service health data for your tenant.

## Tools reference

### `list_service_health`

List the current health status of every M365 service the tenant subscribes to.

| Input | Type | Default | Notes |
|---|---|---|---|
| `top` | `int` (1..50) | 25 | Maximum number of services to return |

Returns Graph
[`healthOverviews`](https://learn.microsoft.com/en-us/graph/api/serviceannouncement-list-healthoverviews)
records: `id`, `service`, `status` (`serviceOperational` / `serviceDegradation` /
`serviceInterruption` / `extendedRecovery` / `investigating` / etc.).

### `list_message_center_posts`

List Message Center posts ordered by most recently modified.

| Input | Type | Default | Notes |
|---|---|---|---|
| `top` | `int` (1..50) | 25 | Maximum number of posts to return |
| `category` | `planForChange` \| `preventOrFixIssue` \| `stayInformed` \| `unknownFutureValue` | none | Optional category filter |
| `severity` | `normal` \| `high` \| `critical` | none | Optional severity filter |

Returns Graph
[`messages`](https://learn.microsoft.com/en-us/graph/api/serviceannouncement-list-messages)
records (without bodies — use `get_message_center_post` for the full content).

### `get_message_center_post`

Fetch the full Message Center post body and metadata.

| Input | Type | Notes |
|---|---|---|
| `message_id` | `str` matching `^[Mm][Cc][0-9]{4,8}$` | e.g. `MC123456` |

Returns the full Graph
[`serviceUpdateMessage`](https://learn.microsoft.com/en-us/graph/api/resources/serviceupdatemessage)
record including the rendered HTML body.

## Troubleshooting

### `--auth-test` fails with `Authorization_RequestDenied`

You haven't granted **admin consent** for the API permissions in step 7 of
[Entra app registration](#entra-app-registration). Open your app registration
in the Entra admin center, go to **API permissions**, and click **Grant admin
consent for &lt;your tenant&gt;**.

### `--auth-test` fails with `Forbidden` even after admin consent

The signed-in user does not hold an admin role. See the role list at the end of
[Entra app registration](#entra-app-registration). Add the user to one of those
roles via **Microsoft Entra admin center → Identity → Roles & admins**.

### Browser doesn't open / I'm on SSH

Set `M365_AUTH_DEVICE_CODE=1` in the `env` block of your MCP client config.
The server will print a code + URL on first call; complete sign-in on any
device.

### `uvx: command not found`

Install `uv` first:

```sh
pip install uv
# or follow https://docs.astral.sh/uv/getting-started/installation/
```

If your corporate machine blocks `uv`, fall back to `pipx`:

```sh
pipx install m365-service-comms-mcp
```

…and replace `"command": "uvx"` with `"command": "m365-svc-comms-mcp"` and drop
the first `args` entry.

### Token cache problems on Linux

If you see errors about `Secret Service` or `keyring`, install the keyring
backend:

```sh
sudo apt install gnome-keyring  # Debian/Ubuntu
```

The server will fall back to a file-permission–restricted cache automatically
if the keyring is unavailable.

### Graph returns `429 TooManyRequests`

The server retries 429s with exponential backoff (4 attempts by default). If
you keep seeing this, you're likely hammering the API in a loop. Service
Communications has a soft limit of about 10 RPS per tenant.

### Locked-down corporate tenant won't let me grant consent

Use a free [Microsoft 365 Developer Program](https://developer.microsoft.com/microsoft-365/dev-program)
sandbox tenant for testing. See [Recommended testing tenant](#recommended-testing-tenant).

If you don't want to set up a sandbox tenant, you can still verify the MCP
wire protocol with `--demo` mode (no tenant required).

## Known limitations (v0.1)

- **Read-only.** No tools for marking messages as read / archiving / favoriting
  yet (the Graph API supports it; v1.0 will).
- **3 tools.** `get_service_health` (per-service deep-dive), `list_service_issues`,
  `get_service_issue`, `get_incident_report`, and `summarize_my_tenant` are
  planned for v1.0.
- **Delegated auth only.** No client-secret or certificate flows. Backend
  monitoring scenarios will need to wait for v1.0.
- **stdio transport only.** No Streamable HTTP for hosted/multi-tenant
  scenarios yet.
- **No Docker image** on `mcr.microsoft.com` — install via PyPI/`uvx` only.

## Local development

```powershell
# from the repo root
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e ".[dev]"
ruff check .
ruff format --check .
pytest --cov
```

End-to-end smoke test against the demo client (no tenant required):

```sh
.\.venv\Scripts\m365-svc-comms-mcp.exe --demo
# then connect any MCP client to it via stdio
```

## License

[MIT](LICENSE) — chosen for broad compatibility:

- ✅ **Customer use**: commercial use, modification, redistribution, and
  incorporation into proprietary products are all permitted with the license
  notice retained.
- ✅ **Microsoft use**: MIT is on Microsoft's approved OSS-license list and is
  the license used by every Microsoft-authored MCP server in the
  [`microsoft/mcp`](https://github.com/microsoft/mcp) catalog (Azure MCP
  Server, Markitdown MCP, Fabric MCP) and by the comparable community
  Microsoft 365 MCP servers (Softeria/ms-365-mcp-server,
  okapi-ca/ms-365-admin-mcp-server).
- ✅ **GPL-compatible**, so downstream redistribution under copyleft licenses
  is permitted if a consumer chooses.

The license is declared in `pyproject.toml` using
[PEP 639](https://peps.python.org/pep-0639/) SPDX format
(`license = "MIT"`) and the `LICENSE` file is included in the published wheel.

## Security

See [SECURITY.md](SECURITY.md). Do not file security issues in the public
tracker — open a [private security advisory](https://github.com/trobichaux/m365-service-comms-mcp/security/advisories/new)
instead.

## Code of Conduct

See [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). This project follows the
[Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
