Reported issues for ms-graph-mcp
Pod holds 16 of 21 problems reported by people outside the maintainer team. Issues filed by the project's own owners, members and collaborators are excluded entirely — a maintainer's release checklist is not a warning to a prospective user.
Back to ms-graph-mcp.
Most discussed
Consolidate onto a single HTTP stack (httpx vs httpx2)
MCP SDK 2.0 depends on httpx2, a distribution separate from httpx. Both are currently
installed: the SDK uses httpx2, client.py uses httpx.
Deliberately left out of the SDK 2.0 migration — an HTTP-stack swap and a protocol migration are two failure domains and did not belong in one change.
Spike first: confirm the httpx2 API surface covers what client.py uses (streaming reads,
timeouts, verify=, Retry-After handling) before committing to the move.
Read the thread · 2026-08-05 · open · 1 comment
Dockerfile and GHCR publishing
Multi-stage build (uv build stage -> slim runtime), non-root user, HEALTHCHECK against
/health, resource-limit guidance.
Add the ghcr job to release.yml, which currently ends with a comment explaining why it is
absent: referencing a Dockerfile that does not exist would fail the first tagged release.
Note GitHub Packages has no Python registry — GHCR is the container registry, and PyPI remains the only place the wheel can live.
Read the thread · 2026-08-05 · closed · 1 comment
Client configuration documentation
docs/clients.md, one section per client, all pointing at the same server:
- VS Code —
.vscode/mcp.json, both tiers. Local:type: "stdio"withuvx. Hosted:type: "http"with anoauthblock carryingclientId, using theinputsarray and${input:...}substitution so no secret is ever hardcoded. - Claude Code —
claude mcp add, plus the Entra DCR caveat for the hosted tier - Claude Desktop —
claude_desktop_config.json, stdio - MCP Inspector — as the neutral c
Read the thread · 2026-08-05 · closed · 1 comment
Testing and debugging documentation
docs/testing.md — the existing pytest patterns (contract tests, the entra/ RS256
fixtures, asyncio_mode=auto, the in-process mcp.Client conformance suite) plus MCP
Inspector, the reference tool. Node 22.19+, three clients behind one binary: web, --cli,
--tui. Include an Inspector CLI tools/list smoke check for CI — it exercises a real client
and catches protocol regressions pytest cannot.
docs/debugging.md — reading [Graph] client logs, the OTEL spans, the structured error
Read the thread · 2026-08-05 · closed · 1 comment
Consolidate meetings.py onto client.py (14 raw httpx clients)
meetings.py holds 14 hand-rolled httpx.AsyncClient instances — by far the largest
concentration left. Every one bypasses the OTEL span, the [Graph] error logging and the TLS
toggle, so transcript retrieval is the least observable part of the server despite being its
most-used feature.
graph_post_no_content and graph_post_raw have already removed the legitimate reasons for
an inline client. Anything genuinely not expressible through client.py earns a helper there
instead.
Being hand
Read the thread · 2026-08-05 · closed · 1 comment
OAuth resource server: PRM, WWW-Authenticate, audience binding
Implement the MCP authorization spec so any conforming client can authenticate without client-specific configuration — not just one client's convention.
GET /.well-known/oauth-protected-resource(RFC 9728) — unauthenticated, like/health- 401 with
WWW-Authenticate: Bearer resource_metadata="...", scope="...". Today auth.py returns a bare JSON error, which gives a client nothing to discover from. This one header is what lets a spec-compliant client self-configure. - **403 wi
Read the thread · 2026-08-05 · closed · 1 comment
Contacts tools
The user's personal address book is not reachable.
people_list_contacts—GET /me/contacts(READ)people_search_contacts(READ)people_create_contact—POST /me/contacts(WRITE)
Not a duplicate of people_search, and the descriptions must say so. /me/people is the
relevance-ranked People API over colleagues the user interacts with. /me/contacts is the
user's saved address book, and it includes external contacts who are not in the tenant
directory at all — invisible to
Read the thread · 2026-08-05 · closed · 1 comment
OneNote page read tools
The server can write a OneNote page but cannot read one back — notes_create_page exists
with no counterpart.
notes_list_pages—GET /me/onenote/sections/{id}/pages(READ)notes_get_page_content—GET /me/onenote/pages/{id}/content(READ)
Page content is HTML, not JSON, so it uses client.py:graph_get_text — the same helper the
transcript VTT path already uses. Requires Notes.Read.
Read the thread · 2026-08-05 · closed · 1 comment
Most recent
Make the HTTP transport a spec-conformant OAuth resource server (agent → MCP → Graph OBO)
Summary
Make the HTTP transport a spec-conformant OAuth 2.1 resource server, so an agent acting for a signed-in user can call this server and the server mints its own Microsoft Graph token via on-behalf-of. The user's Graph token is never handed around.
Most of this flow already exists behind GRAPH_MCP_DOES_OBO=true. It is off by default, and the
default posture is the problem.
The problem
With GRAPH_MCP_DOES_OBO=false (today's default) the server accepts a token whose aud is
Read the thread · 2026-08-05 · open · 0 comments
Replace X-Write-Scope with a 403 insufficient_scope challenge
Split out of #20, which delivered the discovery half (RFC 9728 metadata + WWW-Authenticate on 401). This is the remaining piece, deliberately deferred because it is a breaking change to a working gate and had no business riding along with the change that introduced discovery.
What
The write tier is currently opted into with X-Write-Scope: true. A custom header only works for a client that has been told about it, which is the opposite of the point of #20 — a spec-compliant client should
Read the thread · 2026-08-05 · open · 0 comments
Toolset profiles: startup default with per-connection narrowing
Deferred by decision until the tool surface is complete, then decided on measured evidence rather than a guess.
Design already settled:
GRAPH_MCP_TOOLSETSselects namespaces at startup; anX-Toolsetsheader or?toolsets=query parameter may narrow further but never widen — the startup value is the ceiling- Namespaces follow permission-scope families, which the Phase B renames already established, so filtering is a prefix match
- Tool visibility is a context-efficiency lever, no
Read the thread · 2026-08-05 · closed · 0 comments
Unified search tool over POST /search/query
One endpoint searches messages, events, driveItems, lists, listItems, sites and people, plus Copilot-connector external data that nothing else in Graph reaches. Highest coverage per tool available.
search_query— entity types selectable by the callersearch_sharepoint_content— narrowed todriveItem/listItem/site
Note chat_search_messages already calls /search/query for chatMessage, so this
generalises an existing pattern rather than introducing one.
Read the thread · 2026-08-05 · closed · 1 comment
Teams chat tools: 1:1 and group chats
Teams coverage looks complete but is channel-shaped. 1:1 and group chats — where most Teams conversation actually happens — are entirely absent.
chat_list—GET /me/chats(READ)chat_list_messages—GET /chats/{id}/messages(READ)chat_send_message—POST /chats/{id}/messages(WRITE)chat_list_members—GET /chats/{id}/members(READ)
Chat.Read and ChatMessage.Send are already in the permission set, so this is purely
unimplemented.
Gotcha: Microsoft imposes a on
Read the thread · 2026-08-05 · closed · 1 comment
Mail action tools: reply, reply-all, forward, mark read
mail_send exists but reply does not, which is backwards for an assistant — replying
in-thread is both more common and safer than composing a new message to addresses the model
chose.
mail_reply,mail_reply_all,mail_forward(WRITE,Mail.Send)mail_mark_read(WRITE,Mail.ReadWrite)
All four are action endpoints returning 202 with an empty body, so they use
client.py:graph_post_no_content.
Read the thread · 2026-08-05 · closed · 1 comment
Task write tools: To Do and Planner
"Mark it done" is the most-used task verb and is missing on both platforms.
tasks_complete_todo,tasks_update_todotasks_create_planner,tasks_update_planner,tasks_complete_planner
Planner writes are the fiddly part. Every POST/PATCH/DELETE requires If-Match
carrying the current @odata.etag and returns 409/412 on conflict, so each tool is
read-then-write with a retry. client.py:graph_patch already accepts extra_headers, and
errors.py:conflict() is the respons
Read the thread · 2026-08-05 · closed · 0 comments
Calendar write and scheduling tools
Booking is the largest functional hole in the surface — the calendar is readable but nothing can be scheduled.
calendar_create_event(WRITE) —POST /me/eventscalendar_update_event(WRITE) —PATCH /me/events/{id}calendar_cancel_event(WRITE,destructiveHint: true) — notifies attendeescalendar_respond_to_event(WRITE) — accept / decline / tentativecalendar_find_meeting_times(READ) —POST /me/findMeetingTimescalendar_get_free_busy(READ) — `POST /me/ca
Read the thread · 2026-08-05 · closed · 0 comments
The remaining reports are on the project's issue tracker.