fix: honor Kiota request extensions in Graph transport - #1129
RKS (rksharma-owg) wants to merge 3 commits into
Conversation
|
Hi team, is there an update on getting this reviewed? And is there a plan to issue a hotfix release once it's done? |
|
Thanks for checking in. The fix is ready for review, and the checks currently reported on this PR are passing. I don't have an estimate for maintainer review or a hotfix release; the maintainers will decide the release timing after reviewing the change. |
…K placeholder (#81) * fix(deps): cap kiota below 1.13 — /me was reaching the wire as the SDK placeholder A fresh `uv tool install outlook-graph-mcp` resolved microsoft-kiota-* 1.13+ and every /me call failed with "me-token-to-replace is invalid" (#80). kiota 1.13.0 (2026-09-18) moved per-request options from a monkey-patched `request.options` attribute to `request.extensions["kiota_request_options"]`, and its UrlReplaceHandler now reads only the new place. msgraph-core 1.5.1, the latest release, still reads `request.options` in middleware/async_graph_transport.py, so the rewrite it installs for `/users/me-token-to-replace` -> `/me` never fires and Graph receives the literal placeholder. The upstream fix is microsoftgraph/msgraph-sdk-python-core#1129, open and unreleased. Nothing capped kiota; msgraph-core itself allows <2.0. The lock pinned 1.12.3, so `uv sync`, every CI job and every developer install were green while every new user's install was broken — the fresh-install and published-install canaries imported the package, counted the tools and never sent a request. Same class as the five-week outage. The kiota family is capped below 1.13 until a msgraph-core release carries #1129; the lock is unchanged at 1.12.3. tests/test_me_rewrite_reaches_the_wire.py builds the client exactly as msgraph does, hands the factory an httpx client on a mock transport, and asserts on the URL the real middleware pipeline sends. It runs against whatever the environment resolved, which is the point. Verified by mutation: under the lock it passes; against kiota 1.14.0 it fails on `.../users/me-token-to-replace`. Both install jobs in ci.yml now run it as a script after their version report. Note the published-install job installs PyPI-latest, which IS broken, so that weekly job will be red until the next release ships — that is the canary doing its job. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LparmojWQ3m9tcJhRGqZjP * test(wire): the nested /me path is rewritten too, and says why Graph tolerates /users/<anything> one segment deep, so a bare /me health check returns 200 with the placeholder still in the URL while every nested path 404s. A canary that probed Graph with /me alone would stay green (Nyaecho, #80). Asserting on the URL string catches both; the nested case carries the explanation. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LparmojWQ3m9tcJhRGqZjP --------- Co-authored-by: Michael Palermiti <253352132+mpalermiti@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
…K placeholder (#81) * fix(deps): cap kiota below 1.13 — /me was reaching the wire as the SDK placeholder A fresh `uv tool install outlook-graph-mcp` resolved microsoft-kiota-* 1.13+ and every /me call failed with "me-token-to-replace is invalid" (#80). kiota 1.13.0 (2026-09-18) moved per-request options from a monkey-patched `request.options` attribute to `request.extensions["kiota_request_options"]`, and its UrlReplaceHandler now reads only the new place. msgraph-core 1.5.1, the latest release, still reads `request.options` in middleware/async_graph_transport.py, so the rewrite it installs for `/users/me-token-to-replace` -> `/me` never fires and Graph receives the literal placeholder. The upstream fix is microsoftgraph/msgraph-sdk-python-core#1129, open and unreleased. Nothing capped kiota; msgraph-core itself allows <2.0. The lock pinned 1.12.3, so `uv sync`, every CI job and every developer install were green while every new user's install was broken — the fresh-install and published-install canaries imported the package, counted the tools and never sent a request. Same class as the five-week outage. The kiota family is capped below 1.13 until a msgraph-core release carries #1129; the lock is unchanged at 1.12.3. tests/test_me_rewrite_reaches_the_wire.py builds the client exactly as msgraph does, hands the factory an httpx client on a mock transport, and asserts on the URL the real middleware pipeline sends. It runs against whatever the environment resolved, which is the point. Verified by mutation: under the lock it passes; against kiota 1.14.0 it fails on `.../users/me-token-to-replace`. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LparmojWQ3m9tcJhRGqZjP * test(wire): the nested /me path is rewritten too, and says why Graph tolerates /users/<anything> one segment deep, so a bare /me health check returns 200 with the placeholder still in the URL while every nested path 404s. A canary that probed Graph with /me alone would stay green (Nyaecho, #80). Asserting on the URL string catches both; the nested case carries the explanation. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LparmojWQ3m9tcJhRGqZjP (cherry picked from commit d16bd30 onto v1.22.0 for the 1.22.1 hotfix; CHANGELOG entry rewritten as a 1.22.1 section. The ci.yml step that runs this test in the install jobs is not part of #81 as merged and is not included here.) --------- Co-authored-by: Michael Palermiti <253352132+mpalermiti@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The normal CI suite lacks a direct extension-only regression test for the corrected pipeline gate.
Review effort: Balanced
Findings: 1
What changed in this PR
Restores Graph middleware for Kiota requests using HTTPX extensions while preserving legacy option support.
Changes:
- Recognizes
kiota_request_optionsextensions and gives them precedence. - Adds redirect, compatibility, and no-options tests.
| File | Description |
|---|---|
src/msgraph_core/middleware/async_graph_transport.py |
Supports extension-based request options. |
tests/middleware/test_async_graph_transport.py |
Tests redirects and option handling. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|




Overview
Restore Graph middleware execution for requests built by
microsoft-kiota-http1.13.0. Graph-core 1.5.1 only enters its middleware pipeline when an HTTPX request has the legacy.optionsattribute. Kiota commitd9180bbmoved per-request options torequest.extensions["kiota_request_options"], so the Graph transport bypasses redirect, URL replacement, retry, and telemetry middleware. For an empty 302 response from DriveItem/content, this makes the binary request returnNoneinstead of following the download URL.Accept the extension-based options while retaining support for the legacy attribute used by earlier Kiota releases. Graph request context now receives the extension options when present. When the extension is absent, legacy options are mirrored into it before sending so Kiota 1.13 middleware honors nondefault options. An explicit extension mapping, including an empty mapping, takes precedence. Raise the Kiota dependency minimums and development pins to 1.13.0 so installed components stay aligned. Legacy request-option handling remains supported; no generated SDK changes are needed.
Fixes #1128. Related: microsoft/kiota-python#745 and microsoft/kiota-python#744.
Notes
A credential-free HTTPX transport reproduction returned
Noneafter one 302 with Kiota HTTP 1.13.0, but followed the redirect and returned bytes with 1.12.3. The new integration test failed on the unmodified Graph-core transport and passes with this change. It covers two binary content types, legacy option compatibility, extension precedence, and requests without options.The full-tree
isort --check-only srcreports an unchanged import-order issue inbatch_request_item.py; the repository's actual CI command,isort src, succeeds and only reorders that existing file in an isolated checkout. No unrelated formatting is included here.Testing Instructions
pytestwith the updated development requirements (Kiota 1.13.0, HTTPX 0.28.1): 84 tests passed locally on Python 3.13.14.should_redirect=Falsestops at the first 302. It fails before the bridge and passes afterward, with controls for extension precedence, explicit empty extension options, and empty legacy options.yapf -dr src, changed-fileisort --check-only,mypy src,pylint src --disable=W --rcfile=.pylintrc, and sdist/wheel build passed locally.156d94701fad9b30b21b534a853b979c1b3d3d25against upstream baseec79992519f708a014676584d5626ea8d733fe2c. Python 3.10–3.14 each passed 84 tests, formatting, changed-file import checks, types, lint, dependency consistency, and package builds. CodeQL also passed. The same matrix and CodeQL also passed for isolated merge validation atf96d4f54cd24516c0bea58069a94a4af1276e8a6.Upstream PR checks are evaluated separately from the completed fork validation.