Skip to content

Merge main into alpha/v2.0.0#393

Merged
heyitsaamir merged 8 commits intoalpha/v2.0.0from
main
Apr 16, 2026
Merged

Merge main into alpha/v2.0.0#393
heyitsaamir merged 8 commits intoalpha/v2.0.0from
main

Conversation

@heyitsaamir
Copy link
Copy Markdown
Collaborator

Summary

Merge latest main commits into alpha for the next alpha release:

Test plan

  • CI passes on the PR
  • Trigger ADO publish pipeline on alpha/v2.0.0 after merge

corinagum and others added 8 commits April 13, 2026 10:02
## Summary
- Introduces `CloudEnvironment` frozen dataclass with predefined presets
(`PUBLIC`, `US_GOV`, `US_GOV_DOD`, `CHINA`) bundling all cloud-specific
service endpoints
- Threads cloud environment through `App`, `TokenManager`,
`BotTokenClient`, `TokenValidator`, `ApiClient`, and `ApiClientSettings`
- Supports `CLOUD` environment variable and programmatic
`AppOptions.cloud` configuration
- Adds `graph_scope` to `CloudEnvironment` for cloud-aware Microsoft
Graph token acquisition
- Simplifies `merge_api_client_settings` with `cloud` default to
`PUBLIC`, removes `DEFAULT_API_CLIENT_SETTINGS`
- Fixes cloud propagation through `ApiClient` -> `BotClient` ->
`BotTokenClient`

### Note
`graph_base_url` (Graph API endpoint per cloud) is intentionally
deferred. This PR focuses on auth/token acquisition. Graph API routing
is a separate concern.

### Sources
- Bot Framework GCCH/DoD endpoints:
https://learn.microsoft.com/en-us/azure/bot-service/how-to-deploy-gov-cloud-high
- Bot Framework China endpoints:
https://learn.microsoft.com/en-us/azure/bot-service/how-to-deploy-china-cloud
- Graph national cloud deployments:
https://learn.microsoft.com/en-us/graph/deployments

## Test plan
- [x] `pytest packages/api/tests/` -- 168 tests pass
- [x] `pytest packages/apps/tests/` -- 389 tests pass (1 pre-existing
failure unrelated)
- [x] E2E: Echo bot with `CLOUD=USGov` against real GCCH tenant --
message received, echo reply sent
- [x] Copilot review feedback addressed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Corina Gum <>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Summary
- Removes the 10-item batch cap in `HttpStream._flush()` so each flush
cycle drains the entire queue
- Prevents a long streaming tail when the LLM finishes generating faster
than chunks are sent to Teams
- Port of
[microsoft/teams.ts#520](microsoft/teams.ts#520)

## Test plan
- [x] Existing `test_http_stream.py` tests pass (11/11)
- [x] Updated `test_stream_multiple_emits_with_timer` to verify the
first flush drains all 12 messages with no second flush scheduled
- [x] Pre-commit hooks (ruff, pyright) pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
- Remove `with_reply_to_id()` method

`reply_to_id` is a field on the activity that is used by the service,
but any changes to it on the bot side are completely ignored. After
thorough testing to confirm, this PR removes the misleading method
`with_reply_to_id`; setting replyToId on outgoing activities is a no-op.

Originally part of #321 but separated out due to timing concerns for GA
When `Client.clone()` was called with headers containing a `User-Agent`,
it would overwrite the base client's `User-Agent` rather than merging.
This loses the SDK's default UA token when callers supply their own.

## Changes

- **`packages/common/src/microsoft_teams/common/http/client.py`**
- Added `_merge_headers(base, overrides)` helper: performs a
case-insensitive `User-Agent` merge (space-concatenated, token-based
dedup to avoid substring false positives); all other headers follow
standard last-write-wins semantics
- `Client.clone()` now calls `_merge_headers()` instead of a plain dict
unpack

- **`packages/common/tests/test_client.py`**
- Added 7 tests covering: preservation without overrides, merge when
both sides define UA, non-UA overrides leave UA untouched,
case-insensitive key matching, duplicate-token suppression, substring
false-positive guard, and override-only UA when base has none

## Example

```python
client = Client(ClientOptions(headers={"User-Agent": "teams-py/1.0"}))
clone = client.clone(ClientOptions(headers={"User-Agent": "myapp/2.0"}))

# Before: "myapp/2.0"  (SDK UA lost)
# After:  "teams-py/1.0 myapp/2.0"
print(clone._options.headers["User-Agent"])
```

> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `releases.astral.sh`
> - Triggering command: `/home/REDACTED/.local/bin/uv uv sync
--all-packages --group dev` (dns block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/microsoft/teams.py/settings/copilot/coding_agent)
(admins only)
>
> </details>

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> Implement the change described by PR microsoft/teams.ts#508 ("Merge
User-Agent headers when cloning HTTP client") in the Python SDK
repository @microsoft/teams.py.
> 
> Goal:
> - When the HTTP client is cloned (or a new client is created from an
existing client/session), ensure that `User-Agent` headers are merged
rather than overwritten.
> - Preserve any existing default `User-Agent` set by the library while
also incorporating a user-provided `User-Agent` (or additional UA
tokens) when cloning.
> 
> Requirements:
> 1. Locate the HTTP client implementation and any clone/copy method(s)
in the repository.
> 2. Identify how headers are stored (e.g., requests.Session headers,
httpx.Client headers, custom dict) and how cloning currently works.
> 3. Modify cloning logic so that:
> - If both source and override headers contain `User-Agent`, the
resulting `User-Agent` should include both (e.g., concatenated with a
space, or comma per convention) without duplicating identical values.
>    - If only one side has `User-Agent`, keep it.
>    - Header merging should be case-insensitive for the header key.
> 4. Add/adjust unit tests to cover:
>    - Cloning without overrides preserves original `User-Agent`.
> - Cloning with headers override that includes `User-Agent` results in
merged value.
> - Cloning with headers override without `User-Agent` leaves original
UA intact.
>    - Case-insensitive behavior (`user-agent`, `User-Agent`).
> 5. Update any relevant docs or changelog entry if the repo uses one.
> 
> Reference:
> - PR: microsoft/teams.ts#508
> 
> Notes:
> - Ensure behavior is consistent across sync/async HTTP clients if both
exist.
> - Keep backward compatibility with existing public APIs.
> 
> 
> The following is the prior conversation context from the user's chat
exploration (may be truncated):
> 
> User: Can you implement this in @microsoft/teams.py ?
> 
> Assistant: I’ve queued up the implementation for `microsoft/teams.py`
and I’m now waiting on your confirmation in the prompt to proceed with
making the code changes and opening the PR.
> 
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

*This pull request was created from Copilot chat.*
>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: heyitsaamir <48929123+heyitsaamir@users.noreply.github.com>
Co-authored-by: heyitsaamir <ajawaid191@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.4.2 to
9.0.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pytest-dev/pytest/releases">pytest's
releases</a>.</em></p>
<blockquote>
<h2>9.0.3</h2>
<h1>pytest 9.0.3 (2026-04-07)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12444">#12444</a>:
Fixed <code>pytest.approx</code> which now correctly takes into account
<code>~collections.abc.Mapping</code> keys order to compare them.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13634">#13634</a>:
Blocking a <code>conftest.py</code> file using the <code>-p no:</code>
option is now explicitly disallowed.</p>
<p>Previously this resulted in an internal assertion failure during
plugin loading.</p>
<p>Pytest now raises a clear <code>UsageError</code> explaining that
conftest files are not plugins and cannot be disabled via
<code>-p</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13734">#13734</a>:
Fixed crash when a test raises an exceptiongroup with
<code>__tracebackhide__ = True</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14195">#14195</a>:
Fixed an issue where non-string messages passed to <!-- raw HTML omitted
-->unittest.TestCase.subTest()<!-- raw HTML omitted --> were not
printed.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>:
Fixed use of insecure temporary directory (CVE-2025-71176).</p>
</li>
</ul>
<h2>Improved documentation</h2>
<ul>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13388">#13388</a>:
Clarified documentation for <code>-p</code> vs
<code>PYTEST_PLUGINS</code> plugin loading and fixed an incorrect
<code>-p</code> example.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13731">#13731</a>:
Clarified that capture fixtures (e.g. <code>capsys</code> and
<code>capfd</code>) take precedence over the <code>-s</code> /
<code>--capture=no</code> command-line options in <code>Accessing
captured output from a test function
&lt;accessing-captured-output&gt;</code>.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14088">#14088</a>:
Clarified that the default <code>pytest_collection</code> hook sets
<code>session.items</code> before it calls
<code>pytest_collection_finish</code>, not after.</li>
<li><a
href="https://redirect.github.com/pytest-dev/pytest/issues/14255">#14255</a>:
TOML integer log levels must be quoted: Updating reference
documentation.</li>
</ul>
<h2>Contributor-facing changes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/12689">#12689</a>:
The test reports are now published to Codecov from GitHub Actions.
The test statistics is visible <a
href="https://app.codecov.io/gh/pytest-dev/pytest/tests">on the web
interface</a>.</p>
<p>-- by <code>aleguy02</code></p>
</li>
</ul>
<h2>9.0.2</h2>
<h1>pytest 9.0.2 (2025-12-06)</h1>
<h2>Bug fixes</h2>
<ul>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13896">#13896</a>:
The terminal progress feature added in pytest 9.0.0 has been disabled by
default, except on Windows, due to compatibility issues with some
terminal emulators.</p>
<p>You may enable it again by passing <code>-p terminalprogress</code>.
We may enable it by default again once compatibility improves in the
future.</p>
<p>Additionally, when the environment variable <code>TERM</code> is
<code>dumb</code>, the escape codes are no longer emitted, even if the
plugin is enabled.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13904">#13904</a>:
Fixed the TOML type of the <code>tmp_path_retention_count</code>
settings in the API reference from number to string.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/pytest-dev/pytest/issues/13946">#13946</a>:
The private <code>config.inicfg</code> attribute was changed in a
breaking manner in pytest 9.0.0.
Due to its usage in the ecosystem, it is now restored to working order
using a compatibility shim.
It will be deprecated in pytest 9.1 and removed in pytest 10.</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/pytest-dev/pytest/commit/a7d58d7a21b78581e636bbbdea13c66ad1657c1e"><code>a7d58d7</code></a>
Prepare release version 9.0.3</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/089d98199c253d8f89a040243bc4f2aa6cd5ab22"><code>089d981</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14366">#14366</a>
from bluetech/revert-14193-backport</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/8127eaf4ab7f6b2fdd0dc1b38343ec97aeef05ac"><code>8127eaf</code></a>
Revert &quot;Fix: assertrepr_compare respects dict insertion order (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14050">#14050</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14193">#14193</a>)&quot;</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/99a7e6029e7a6e8d53e5df114b1346e035370241"><code>99a7e60</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14363">#14363</a>
from pytest-dev/patchback/backports/9.0.x/95d8423bd...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/ddee02a578da30dd43aedc39c1c1f1aaadfcee95"><code>ddee02a</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14343">#14343</a>
from bluetech/cve-2025-71176-simple</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/74eac6916fee34726cb194f16c516e96fbd29619"><code>74eac69</code></a>
doc: Update training info (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14298">#14298</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14301">#14301</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/f92dee777cfdb77d1c43633d02766ddf1f07c869"><code>f92dee7</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14267">#14267</a>
from pytest-dev/patchback/backports/9.0.x/d6fa26c62...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/7ee58acc8777c31ac6cf388d01addf5a414a7439"><code>7ee58ac</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/12378">#12378</a>
from Pierre-Sassoulas/fix-implicit-str-concat-and-d...</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/37da870d37e3a2f5177cae075c7b9ae279432bf8"><code>37da870</code></a>
Merge pull request <a
href="https://redirect.github.com/pytest-dev/pytest/issues/14259">#14259</a>
from mitre88/patch-4 (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14268">#14268</a>)</li>
<li><a
href="https://github.com/pytest-dev/pytest/commit/c34bfa3b7acb65b594707c714f1d8461b0304eed"><code>c34bfa3</code></a>
Add explanation for string context diffs (<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14257">#14257</a>)
(<a
href="https://redirect.github.com/pytest-dev/pytest/issues/14266">#14266</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pytest-dev/pytest/compare/8.4.2...9.0.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pytest&package-manager=uv&previous-version=8.4.2&new-version=9.0.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/microsoft/teams.py/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…s/tab/Web (#388)

Bumps
[follow-redirects](https://github.com/follow-redirects/follow-redirects)
from 1.15.11 to 1.16.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/follow-redirects/follow-redirects/commit/0c23a223067201c368035e82954c11eb2578a33b"><code>0c23a22</code></a>
Release version 1.16.0 of the npm package.</li>
<li><a
href="https://github.com/follow-redirects/follow-redirects/commit/844c4d302ac963d29bdb5dc1754ec7df3d70d7f9"><code>844c4d3</code></a>
Add sensitiveHeaders option.</li>
<li><a
href="https://github.com/follow-redirects/follow-redirects/commit/5e8b8d024e2c76f804a284258e585ecb49a575be"><code>5e8b8d0</code></a>
ci: add Node.js 24.x to the CI matrix</li>
<li><a
href="https://github.com/follow-redirects/follow-redirects/commit/7953e2255aa0b93602eed3804f3bc5e6923a03af"><code>7953e22</code></a>
ci: upgrade GitHub Actions to use setup-node@v6 and checkout@v6</li>
<li><a
href="https://github.com/follow-redirects/follow-redirects/commit/86dc1f86e4b56bcd642c78384d51f10f123aea75"><code>86dc1f8</code></a>
Sanitizing input.</li>
<li>See full diff in <a
href="https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=follow-redirects&package-manager=npm_and_yarn&previous-version=1.15.11&new-version=1.16.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/microsoft/teams.py/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [python-multipart](https://github.com/Kludex/python-multipart)
from 0.0.22 to 0.0.26.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Kludex/python-multipart/releases">python-multipart's
releases</a>.</em></p>
<blockquote>
<h2>Version 0.0.26</h2>
<h2>What's Changed</h2>
<ul>
<li>Skip preamble before first multipart boundary by <a
href="https://github.com/Kludex"><code>@​Kludex</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/262">Kludex/python-multipart#262</a></li>
<li>Silently discard epilogue data after the closing boundary by <a
href="https://github.com/Kludex"><code>@​Kludex</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/259">Kludex/python-multipart#259</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/Kludex/python-multipart/compare/0.0.25...0.0.26">https://github.com/Kludex/python-multipart/compare/0.0.25...0.0.26</a></p>
<h2>Version 0.0.25</h2>
<h2>What's Changed</h2>
<ul>
<li>Apply Apache-2.0 properly by <a
href="https://github.com/Kludex"><code>@​Kludex</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/247">Kludex/python-multipart#247</a></li>
<li>Handle multipart headers case-insensitively by <a
href="https://github.com/Kludex"><code>@​Kludex</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/252">Kludex/python-multipart#252</a></li>
<li>Emit <code>field_end</code> for trailing bare field names on
finalize by <a
href="https://github.com/bysiber"><code>@​bysiber</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/230">Kludex/python-multipart#230</a></li>
<li>Add <code>UPLOAD_DELETE_TMP</code> to <code>FormParser</code> config
by <a href="https://github.com/Kludex"><code>@​Kludex</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/254">Kludex/python-multipart#254</a></li>
<li>Remove custom FormParser classes by <a
href="https://github.com/Kludex"><code>@​Kludex</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/257">Kludex/python-multipart#257</a></li>
<li>Handle CTE values case-insensitively by <a
href="https://github.com/Kludex"><code>@​Kludex</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/258">Kludex/python-multipart#258</a></li>
<li>Add MIME content type info to File by <a
href="https://github.com/jhnstrk"><code>@​jhnstrk</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/143">Kludex/python-multipart#143</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/Kludex/python-multipart/compare/0.0.24...0.0.25">https://github.com/Kludex/python-multipart/compare/0.0.24...0.0.25</a></p>
<h2>Version 0.0.24</h2>
<h2>What's Changed</h2>
<ul>
<li>Validate <code>chunk_size</code> in <code>parse_form()</code> by <a
href="https://github.com/Kludex"><code>@​Kludex</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/244">Kludex/python-multipart#244</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/Kludex/python-multipart/compare/0.0.23...0.0.24">https://github.com/Kludex/python-multipart/compare/0.0.23...0.0.24</a></p>
<h2>Version 0.0.23</h2>
<h2>What's Changed</h2>
<ul>
<li>Remove unused <code>trust_x_headers</code> parameter and
<code>X-File-Name</code> fallback by <a
href="https://github.com/jhnstrk"><code>@​jhnstrk</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/196">Kludex/python-multipart#196</a></li>
<li>Return processed length from
<code>QuerystringParser._internal_write</code> by <a
href="https://github.com/bysiber"><code>@​bysiber</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/229">Kludex/python-multipart#229</a></li>
<li>Cleanup metadata dunders from <code>__init__.py</code> by <a
href="https://github.com/Chesars"><code>@​Chesars</code></a> in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/227">Kludex/python-multipart#227</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/Chesars"><code>@​Chesars</code></a> made
their first contribution in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/227">Kludex/python-multipart#227</a></li>
<li><a href="https://github.com/bysiber"><code>@​bysiber</code></a> made
their first contribution in <a
href="https://redirect.github.com/Kludex/python-multipart/pull/229">Kludex/python-multipart#229</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/Kludex/python-multipart/compare/0.0.22...0.0.23">https://github.com/Kludex/python-multipart/compare/0.0.22...0.0.23</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Kludex/python-multipart/blob/master/CHANGELOG.md">python-multipart's
changelog</a>.</em></p>
<blockquote>
<h2>0.0.26 (2026-04-10)</h2>
<ul>
<li>Skip preamble before the first multipart boundary more efficiently
<a
href="https://redirect.github.com/Kludex/python-multipart/pull/262">#262</a>.</li>
<li>Silently discard epilogue data after the closing multipart boundary
<a
href="https://redirect.github.com/Kludex/python-multipart/pull/259">#259</a>.</li>
</ul>
<h2>0.0.25 (2026-04-10)</h2>
<ul>
<li>Add MIME content type info to <code>File</code> <a
href="https://redirect.github.com/Kludex/python-multipart/pull/143">#143</a>.</li>
<li>Handle CTE values case-insensitively <a
href="https://redirect.github.com/Kludex/python-multipart/pull/258">#258</a>.</li>
<li>Remove custom <code>FormParser</code> classes <a
href="https://redirect.github.com/Kludex/python-multipart/pull/257">#257</a>.</li>
<li>Add <code>UPLOAD_DELETE_TMP</code> to <code>FormParser</code> config
<a
href="https://redirect.github.com/Kludex/python-multipart/pull/254">#254</a>.</li>
<li>Emit <code>field_end</code> for trailing bare field names on
finalize <a
href="https://redirect.github.com/Kludex/python-multipart/pull/230">#230</a>.</li>
<li>Handle multipart headers case-insensitively <a
href="https://redirect.github.com/Kludex/python-multipart/pull/252">#252</a>.</li>
<li>Apply Apache-2.0 properly <a
href="https://redirect.github.com/Kludex/python-multipart/pull/247">#247</a>.</li>
</ul>
<h2>0.0.24 (2026-04-05)</h2>
<ul>
<li>Validate <code>chunk_size</code> in <code>parse_form()</code> <a
href="https://redirect.github.com/Kludex/python-multipart/pull/244">#244</a>.</li>
</ul>
<h2>0.0.23 (2026-04-05)</h2>
<ul>
<li>Remove unused <code>trust_x_headers</code> parameter and
<code>X-File-Name</code> fallback <a
href="https://redirect.github.com/Kludex/python-multipart/pull/196">#196</a>.</li>
<li>Return processed length from
<code>QuerystringParser._internal_write</code> <a
href="https://redirect.github.com/Kludex/python-multipart/pull/229">#229</a>.</li>
<li>Cleanup metadata dunders from <code>__init__.py</code> <a
href="https://redirect.github.com/Kludex/python-multipart/pull/227">#227</a>.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/Kludex/python-multipart/commit/28f47859b4a40c2e11e02dc514b2e9743ceedd2e"><code>28f4785</code></a>
Version 0.0.26 (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/263">#263</a>)</li>
<li><a
href="https://github.com/Kludex/python-multipart/commit/d4452a78bbde94995dd3c0d1b4aff3610a5c472f"><code>d4452a7</code></a>
Silently discard epilogue data after the closing boundary (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/259">#259</a>)</li>
<li><a
href="https://github.com/Kludex/python-multipart/commit/6a7b76dd2653d99d8e5981d7ff09a4a047750b37"><code>6a7b76d</code></a>
Skip preamble before first multipart boundary (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/262">#262</a>)</li>
<li><a
href="https://github.com/Kludex/python-multipart/commit/4addb60350fc843f77a1502f14247db91930b3bf"><code>4addb60</code></a>
Version 0.0.25 (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/261">#261</a>)</li>
<li><a
href="https://github.com/Kludex/python-multipart/commit/d3a4698e0dc16cbd85f98076b2ebf9b696cd3604"><code>d3a4698</code></a>
Add MIME content type info to File (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/143">#143</a>)</li>
<li><a
href="https://github.com/Kludex/python-multipart/commit/9a1ecbd074801fcd3911266f3f4442181d10ab92"><code>9a1ecbd</code></a>
Handle CTE values case-insensitively (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/258">#258</a>)</li>
<li><a
href="https://github.com/Kludex/python-multipart/commit/ef2a0b94f95676ea6a7b77d2252b09f5797cb8ed"><code>ef2a0b9</code></a>
Remove custom FormParser classes (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/257">#257</a>)</li>
<li><a
href="https://github.com/Kludex/python-multipart/commit/3a757d7cf209e654eb17cf7b7af868eed469f680"><code>3a757d7</code></a>
Ignore local Claude state (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/255">#255</a>)</li>
<li><a
href="https://github.com/Kludex/python-multipart/commit/55e739617db7c40e2cd04c5ad8c7acf2ed0a1d19"><code>55e7396</code></a>
fuzz: Add cifuzz (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/186">#186</a>)</li>
<li><a
href="https://github.com/Kludex/python-multipart/commit/d6d1d111e7de9ce3d3f8623fe5f5e4201c0a5fd1"><code>d6d1d11</code></a>
Bump the github-actions group with 2 updates (<a
href="https://redirect.github.com/Kludex/python-multipart/issues/249">#249</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/Kludex/python-multipart/compare/0.0.22...0.0.26">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=python-multipart&package-manager=uv&previous-version=0.0.22&new-version=0.0.26)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/microsoft/teams.py/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This pull request significantly refactors the MCP server Teams bot
sample to provide a robust, human-in-the-loop toolkit using the official
`mcp` SDK, with clear separation of concerns and improved documentation.
The changes introduce a modular structure, new tool definitions for user
interaction and approvals, and improved state management. The README and
project metadata are also updated for clarity and accuracy.

**Key changes:**

**1. Major refactor and modularization**
- The codebase is split into clear modules: `app.py` (Teams bot
handlers), `mcp_tools.py` (MCP tool definitions), `state.py` (in-memory
state), and `main.py` (entry point and server wiring), replacing the
previous monolithic and example-based implementation.

**2. New MCP tools for human-in-the-loop workflows**
- Defines five MCP tools in `mcp_tools.py`: `notify`, `ask`,
`get_reply`, `request_approval`, and `get_approval`, enabling agents to
notify users, ask questions, and request approvals through Teams with
polling for responses.
- Introduces helper models for tool inputs and outputs in `models.py`.

**3. Improved state management**
- Implements in-memory dictionaries in `state.py` to track
conversations, pending asks, approvals, and user-to-request mappings,
supporting the new tools and workflows.

**4. Documentation and metadata updates**
- Overhauls the `README.md` to explain setup, architecture, tool usage,
workflows, and limitations, providing a much clearer guide for users and
developers.
- Updates `pyproject.toml` to accurately describe the project and its
dependencies, switching to the `mcp` SDK.

**5. Removal of legacy/demo code**
- Removes the previous example tools (`echo`, `get_weather`,
`calculate`, `alert`) and the old MCP server plugin setup, replacing
them with the new modular and production-like approach.
@heyitsaamir heyitsaamir requested a review from lilyydu April 16, 2026 21:50
@heyitsaamir heyitsaamir merged commit ba4affe into alpha/v2.0.0 Apr 16, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants