m365-health-mcp
by sedattavukcu
README.md
# Microsoft 365 Servis Sağlığı MCP Sunucusu
Microsoft 365 servislerinin (Teams, Exchange, SharePoint, Intune vb.) genel
sağlık durumu ve bilinen olaylar/kesintiler hakkında salt-okunur bir
[Model Context Protocol](https://modelcontextprotocol.io) sunucusu.
[ArubaCentralMCP](https://github.com/sedattavukcu/aruba-central-mcp) /
[JamfMCP](https://github.com/sedattavukcu/jamf-pro-mcp) /
[DefenderCloudAppsMCP](https://github.com/sedattavukcu/defender-cloud-apps-mcp) /
[TeamsCallsMCP](https://github.com/sedattavukcu/teams-calls-mcp) /
[IntuneMCP](https://github.com/sedattavukcu/intune-mcp) /
[EntraIDMCP](https://github.com/sedattavukcu/entraid-mcp) ile aynı mimari
desenle yazılmıştır ve NetRadar Copilot Studio ajan ailesinin bir parçasıdır.
## Amaç
"Bu genel bir Microsoft kesintisi mi, yoksa sorun yalnızca bizde mi?"
sorusuna hızlı, kanıta dayalı yanıt vermek — sıfır kişisel veri (PII)
içerir, yalnızca Microsoft'un kendi tenant'a özel servis durumu/duyuru
bilgisi.
## Mimari
```mermaid
flowchart LR
A["MCP İstemcisi<br/>(Copilot Studio / Claude Code)"] -->|"HTTPS<br/>Authorization: Bearer <token>"| B["Caddy<br/>(aruba-mcp-vm üzerinde,<br/>yedinci site block'u)"]
B -->|"127.0.0.1:8426"| C["m365-health-mcp.service<br/>Python / mcp.server.MCPServer"]
C -->|"OAuth2 client_credentials<br/>Bearer <access_token>"| D["Microsoft Graph<br/>Service Communications API<br/>/admin/serviceAnnouncement/*"]
```
**Barındırma:** `aruba-mcp-vm` (Azure, `ArubaMCP` resource group) üzerinde
**yedinci bir systemd servisi** (`m365-health-mcp.service`, port `8426`) ve
Caddy'de yedinci bir site block'u olarak çalışır. Dedike, ayrıcalıksız
`m365healthmcp` sistem kullanıcısı altında.
## Kimlik Doğrulama
Microsoft Entra ID, Doğuş Grubu tenant'ı. Ayrı bir Entra ID App Registration
(`M365HealthMCP`), OAuth2 `client_credentials` akışı, iki application
permission (ikisi de admin consent almış):
| İzin | Neyi açar |
|---|---|
| **`ServiceHealth.Read.All`** | Servis sağlığı, olaylar/kesintiler, PIR raporları |
| **`ServiceMessage.Read.All`** | Message Center duyuruları (planlı değişiklikler) |
Her ikisi de bu alanlar için Microsoft'un sunduğu tek seçenek; daha dar bir
alternatif yok.
## Araçlar
### Servis sağlığı — "şu an bir sorun var mı?"
| Tool | Açıklama |
|---|---|
| `get_service_health_overview` | Abone olunan tüm M365 servislerinin güncel durumu (sorunsuz/degraded özet dahil) |
| `get_service_health` | **Tek** bir servisin durumu + o servise ait açık olaylar (`$expand=issues`) |
| `list_service_issues` | Olay/kesintiler — başlık, etki, sınıflandırma, çözüldü mü, en son durum güncellemesi |
| `get_service_issue` | Tek bir olayın TAM zaman çizelgesi — Microsoft'un yayınladığı tüm ara güncellemeler |
| `get_incident_report` | Olay Sonrası İnceleme (PIR) raporu — **kök neden**, etki kapsamı, alınan önlemler |
### Message Center — "yaklaşan değişiklikler neler?"
| Tool | Açıklama |
|---|---|
| `list_service_messages` | Duyurular; `action_required_only`, `major_changes_only`, `severity`, `service` ile filtrelenir |
| `get_service_message` | Tek bir duyurunun tam metni (HTML → düz metin) + ilgili Microsoft doküman bağlantıları |
**Kapsam dışı:** Graph'ın `markRead` / `archive` / `favorite` uç noktaları hem
yazma işlemidir (salt-okunur ilkemize aykırı) hem de yalnızca delegated
izinle (`ServiceMessageViewpoint.Write`) çalışır — app-only akışımızla zaten
kullanılamaz.
## Uygulama notları (canlı ortamda doğrulanmış)
- **camelCase `status`:** Graph'ın canlı v1.0 uç noktası `serviceOperational`
döndürüyor; resmi dokümandaki örnek yanıltıcı şekilde PascalCase
(`ServiceOperational`) gösteriyor. Kod büyük/küçük harfe duyarsız karşılaştırır.
- **Sayfalama şart:** Graph sayfa başına 100 kayıt döndürüyor ve olay arşivi
bundan çok daha büyük. İlk sayfayı çekip istemci tarafında filtrelemek
aktif olayların çoğunu sessizce kaçırıyordu (14 aktif olaydan yalnızca 3'ü
ilk sayfadaydı). `isResolved` filtresi artık **sunucu tarafında** uygulanıyor.
- **Geçersiz servis adında 403:** Var olmayan bir servis adı için Graph 404
değil **403** döndürüyor; ham hata "izin eksik" gibi görünüyor. Kod bunu
"servis adı tam eşleşmeli" hatasına çeviriyor.
- **PIR belgesi .docx'tir:** `incidentReport` bir dosya akışı
(`application/octet-stream`, ZIP tabanlı .docx) döndürüyor. Kod bunu
`mammoth` (docx→HTML) + `markdownify` (HTML→Markdown) ile **yapılandırılmış
Markdown'a** çeviriyor; başlıklar (`# Root Cause`) ve alan/değer tabloları
korunuyor. Her raporda birebir aynı olan Microsoft hukuki sorumluluk reddi
metni atlanıp doğrudan "Incident Information" bölümünden başlatılıyor.
Başlangıçta bu iş elle yazılmış bir `zipfile` + regex ayrıştırıcısıyla
yapılıyordu; o çözüm belgeyi düz satırlara indirgeyip tablo yapısını ve
başlık hiyerarşisini kaybediyordu — LLM'in "kök neden neydi" türü soruları
yanıtlaması için Markdown çıktısı belirgin şekilde daha güvenilir.
(Microsoft'un `markitdown` aracı da docx için içeride tam olarak bu iki
kütüphaneyi kullanıyor; doğrudan onları çağırmak `magika` gibi ~3 MB'lık
ML bağımlılığını VM'e taşımamızı gereksiz kılıyor.)
## Gizlilik
Bu sunucu hiçbir kullanıcıya özel veri döndürmez — yalnızca Microsoft'un
kendi servis durumu/duyuru bilgisi. Yazma/aksiyon işlemi içermez (Graph'ta
zaten bu alanda bir yazma uç noktası yok).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing