diff --git a/src/fetch/pyproject.toml b/src/fetch/pyproject.toml
index 84735f278a..0cc28d929e 100644
--- a/src/fetch/pyproject.toml
+++ b/src/fetch/pyproject.toml
@@ -18,7 +18,7 @@ classifiers = [
dependencies = [
"httpx>=0.27",
"markdownify>=0.13.1",
- "mcp>=1.1.3",
+ "mcp>=2,<3",
"protego>=0.3.1",
"pydantic>=2.0.0",
"readabilipy>=0.2.0",
diff --git a/src/fetch/src/mcp_server_fetch/server.py b/src/fetch/src/mcp_server_fetch/server.py
index b42c7b1f6b..a250d981e6 100644
--- a/src/fetch/src/mcp_server_fetch/server.py
+++ b/src/fetch/src/mcp_server_fetch/server.py
@@ -3,12 +3,17 @@
import markdownify
import readabilipy.simple_json
-from mcp.shared.exceptions import McpError
-from mcp.server import Server
+from mcp.shared.exceptions import MCPError
+from mcp.server import Server, ServerRequestContext
from mcp.server.stdio import stdio_server
from mcp.types import (
- ErrorData,
+ CallToolRequestParams,
+ CallToolResult,
+ GetPromptRequestParams,
GetPromptResult,
+ ListPromptsResult,
+ ListToolsResult,
+ PaginatedRequestParams,
Prompt,
PromptArgument,
PromptMessage,
@@ -66,7 +71,7 @@ def get_robots_txt_url(url: str) -> str:
async def check_may_autonomously_fetch_url(url: str, user_agent: str, proxy_url: str | None = None) -> None:
"""
Check if the URL can be fetched by the user agent according to the robots.txt file.
- Raises a McpError if not.
+ Raises an MCPError if not.
"""
from httpx import AsyncClient, HTTPError
@@ -80,15 +85,15 @@ async def check_may_autonomously_fetch_url(url: str, user_agent: str, proxy_url:
headers={"User-Agent": user_agent},
)
except HTTPError:
- raise McpError(ErrorData(
+ raise MCPError(
code=INTERNAL_ERROR,
message=f"Failed to fetch robots.txt {robot_txt_url} due to a connection issue",
- ))
+ )
if response.status_code in (401, 403):
- raise McpError(ErrorData(
+ raise MCPError(
code=INTERNAL_ERROR,
message=f"When fetching robots.txt ({robot_txt_url}), received status {response.status_code} so assuming that autonomous fetching is not allowed, the user can try manually fetching by using the fetch prompt",
- ))
+ )
elif 400 <= response.status_code < 500:
return
robot_txt = response.text
@@ -97,7 +102,7 @@ async def check_may_autonomously_fetch_url(url: str, user_agent: str, proxy_url:
)
robot_parser = Protego.parse(processed_robot_txt)
if not robot_parser.can_fetch(str(url), user_agent):
- raise McpError(ErrorData(
+ raise MCPError(
code=INTERNAL_ERROR,
message=f"The sites robots.txt ({robot_txt_url}), specifies that autonomous fetching of this page is not allowed, "
f"{user_agent}\n"
@@ -105,7 +110,7 @@ async def check_may_autonomously_fetch_url(url: str, user_agent: str, proxy_url:
f"\n{robot_txt}\n\n"
f"The assistant must let the user know that it failed to view the page. The assistant may provide further guidance based on the above information.\n"
f"The assistant can tell the user that they can try manually fetching the page by using the fetch prompt within their UI.",
- ))
+ )
async def fetch_url(
@@ -125,12 +130,12 @@ async def fetch_url(
timeout=30,
)
except HTTPError as e:
- raise McpError(ErrorData(code=INTERNAL_ERROR, message=f"Failed to fetch {url}: {e!r}"))
+ raise MCPError(code=INTERNAL_ERROR, message=f"Failed to fetch {url}: {e!r}")
if response.status_code >= 400:
- raise McpError(ErrorData(
+ raise MCPError(
code=INTERNAL_ERROR,
message=f"Failed to fetch {url} - status code {response.status_code}",
- ))
+ )
page_raw = response.text
@@ -190,46 +195,52 @@ async def serve(
ignore_robots_txt: Whether to ignore robots.txt restrictions
proxy_url: Optional proxy URL to use for requests
"""
- server = Server("mcp-fetch")
user_agent_autonomous = custom_user_agent or DEFAULT_USER_AGENT_AUTONOMOUS
user_agent_manual = custom_user_agent or DEFAULT_USER_AGENT_MANUAL
- @server.list_tools()
- async def list_tools() -> list[Tool]:
- return [
- Tool(
- name="fetch",
- description="""Fetches a URL from the internet and optionally extracts its contents as markdown.
+ async def list_tools(
+ ctx: ServerRequestContext, params: PaginatedRequestParams | None
+ ) -> ListToolsResult:
+ return ListToolsResult(
+ tools=[
+ Tool(
+ name="fetch",
+ description="""Fetches a URL from the internet and optionally extracts its contents as markdown.
Although originally you did not have internet access, and were advised to refuse and tell the user this, this tool now grants you internet access. Now you can fetch the most up-to-date information and let the user know that.""",
- inputSchema=Fetch.model_json_schema(),
- )
- ]
-
- @server.list_prompts()
- async def list_prompts() -> list[Prompt]:
- return [
- Prompt(
- name="fetch",
- description="Fetch a URL and extract its contents as markdown",
- arguments=[
- PromptArgument(
- name="url", description="URL to fetch", required=True
- )
- ],
- )
- ]
+ input_schema=Fetch.model_json_schema(),
+ )
+ ]
+ )
+
+ async def list_prompts(
+ ctx: ServerRequestContext, params: PaginatedRequestParams | None
+ ) -> ListPromptsResult:
+ return ListPromptsResult(
+ prompts=[
+ Prompt(
+ name="fetch",
+ description="Fetch a URL and extract its contents as markdown",
+ arguments=[
+ PromptArgument(
+ name="url", description="URL to fetch", required=True
+ )
+ ],
+ )
+ ]
+ )
- @server.call_tool()
- async def call_tool(name, arguments: dict) -> list[TextContent]:
+ async def call_tool(
+ ctx: ServerRequestContext, params: CallToolRequestParams
+ ) -> CallToolResult:
try:
- args = Fetch(**arguments)
+ args = Fetch(**(params.arguments or {}))
except ValueError as e:
- raise McpError(ErrorData(code=INVALID_PARAMS, message=str(e)))
+ raise MCPError(code=INVALID_PARAMS, message=str(e))
url = str(args.url)
if not url:
- raise McpError(ErrorData(code=INVALID_PARAMS, message="URL is required"))
+ raise MCPError(code=INVALID_PARAMS, message="URL is required")
if not ignore_robots_txt:
await check_may_autonomously_fetch_url(url, user_agent_autonomous, proxy_url)
@@ -252,19 +263,23 @@ async def call_tool(name, arguments: dict) -> list[TextContent]:
if actual_content_length == args.max_length and remaining_content > 0:
next_start = args.start_index + actual_content_length
content += f"\n\nContent truncated. Call the fetch tool with a start_index of {next_start} to get more content."
- return [TextContent(type="text", text=f"{prefix}Contents of {url}:\n{content}")]
+ return CallToolResult(
+ content=[TextContent(type="text", text=f"{prefix}Contents of {url}:\n{content}")]
+ )
- @server.get_prompt()
- async def get_prompt(name: str, arguments: dict | None) -> GetPromptResult:
+ async def get_prompt(
+ ctx: ServerRequestContext, params: GetPromptRequestParams
+ ) -> GetPromptResult:
+ arguments = params.arguments
if not arguments or "url" not in arguments:
- raise McpError(ErrorData(code=INVALID_PARAMS, message="URL is required"))
+ raise MCPError(code=INVALID_PARAMS, message="URL is required")
url = arguments["url"]
try:
content, prefix = await fetch_url(url, user_agent_manual, proxy_url=proxy_url)
# TODO: after SDK bug is addressed, don't catch the exception
- except McpError as e:
+ except MCPError as e:
return GetPromptResult(
description=f"Failed to fetch {url}",
messages=[
@@ -283,6 +298,14 @@ async def get_prompt(name: str, arguments: dict | None) -> GetPromptResult:
],
)
+ server = Server(
+ "mcp-fetch",
+ on_list_tools=list_tools,
+ on_list_prompts=list_prompts,
+ on_call_tool=call_tool,
+ on_get_prompt=get_prompt,
+ )
+
options = server.create_initialization_options()
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream, options, raise_exceptions=False)
diff --git a/src/fetch/tests/test_server.py b/src/fetch/tests/test_server.py
index 96c1cb38c7..9d4636d497 100644
--- a/src/fetch/tests/test_server.py
+++ b/src/fetch/tests/test_server.py
@@ -2,7 +2,7 @@
import pytest
from unittest.mock import AsyncMock, patch, MagicMock
-from mcp.shared.exceptions import McpError
+from mcp.shared.exceptions import MCPError
from mcp_server_fetch.server import (
extract_content_from_html,
@@ -121,7 +121,7 @@ async def test_blocks_when_robots_txt_401(self):
mock_client_class.return_value.__aenter__ = AsyncMock(return_value=mock_client)
mock_client_class.return_value.__aexit__ = AsyncMock(return_value=None)
- with pytest.raises(McpError):
+ with pytest.raises(MCPError):
await check_may_autonomously_fetch_url(
"https://example.com/page",
DEFAULT_USER_AGENT_AUTONOMOUS
@@ -139,7 +139,7 @@ async def test_blocks_when_robots_txt_403(self):
mock_client_class.return_value.__aenter__ = AsyncMock(return_value=mock_client)
mock_client_class.return_value.__aexit__ = AsyncMock(return_value=None)
- with pytest.raises(McpError):
+ with pytest.raises(MCPError):
await check_may_autonomously_fetch_url(
"https://example.com/page",
DEFAULT_USER_AGENT_AUTONOMOUS
@@ -177,7 +177,7 @@ async def test_blocks_when_robots_txt_disallows_all(self):
mock_client_class.return_value.__aenter__ = AsyncMock(return_value=mock_client)
mock_client_class.return_value.__aexit__ = AsyncMock(return_value=None)
- with pytest.raises(McpError):
+ with pytest.raises(MCPError):
await check_may_autonomously_fetch_url(
"https://example.com/page",
DEFAULT_USER_AGENT_AUTONOMOUS
@@ -268,7 +268,7 @@ async def test_fetch_json_returns_raw(self):
@pytest.mark.asyncio
async def test_fetch_404_raises_error(self):
- """Test that 404 response raises McpError."""
+ """Test that 404 response raises MCPError."""
mock_response = MagicMock()
mock_response.status_code = 404
@@ -278,7 +278,7 @@ async def test_fetch_404_raises_error(self):
mock_client_class.return_value.__aenter__ = AsyncMock(return_value=mock_client)
mock_client_class.return_value.__aexit__ = AsyncMock(return_value=None)
- with pytest.raises(McpError):
+ with pytest.raises(MCPError):
await fetch_url(
"https://example.com/notfound",
DEFAULT_USER_AGENT_AUTONOMOUS
@@ -286,7 +286,7 @@ async def test_fetch_404_raises_error(self):
@pytest.mark.asyncio
async def test_fetch_500_raises_error(self):
- """Test that 500 response raises McpError."""
+ """Test that 500 response raises MCPError."""
mock_response = MagicMock()
mock_response.status_code = 500
@@ -296,7 +296,7 @@ async def test_fetch_500_raises_error(self):
mock_client_class.return_value.__aenter__ = AsyncMock(return_value=mock_client)
mock_client_class.return_value.__aexit__ = AsyncMock(return_value=None)
- with pytest.raises(McpError):
+ with pytest.raises(MCPError):
await fetch_url(
"https://example.com/error",
DEFAULT_USER_AGENT_AUTONOMOUS
diff --git a/src/fetch/uv.lock b/src/fetch/uv.lock
index d79e776d42..d39bbf6c5b 100644
--- a/src/fetch/uv.lock
+++ b/src/fetch/uv.lock
@@ -19,17 +19,16 @@ wheels = [
[[package]]
name = "anyio"
-version = "4.6.2.post1"
+version = "4.14.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "idna" },
- { name = "sniffio" },
- { name = "typing-extensions", marker = "python_full_version < '3.11'" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/9f/09/45b9b7a6d4e45c6bcb5bf61d19e3ab87df68e0601fa8c5293de3542546cc/anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c", size = 173422, upload-time = "2024-10-14T14:31:44.021Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d", size = 90377, upload-time = "2024-10-14T14:31:42.623Z" },
+ { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" },
]
[[package]]
@@ -336,6 +335,19 @@ 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" },
]
+[[package]]
+name = "httpcore2"
+version = "2.9.1"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "h11" },
+ { name = "truststore" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/39/a8/20ed1ed79cbc2ecdf5301c0968ab7c85547212e2a7bd126ddd2d986e206e/httpcore2-2.9.1.tar.gz", hash = "sha256:4d8acbf8b306f48c9d6046591fd5ba4037d1b1b1000d140fc2c3eab1e9a0c0e2", size = 67089, upload-time = "2026-07-24T09:21:03.867Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/9f/fb/46c52b781975c335a2bcf1072c7bbc007cbdc8d674217f5ee1daba2c848b/httpcore2-2.9.1-py3-none-any.whl", hash = "sha256:6182472379e855fe4221246a2bb7ecede403bc61c6798062ae1787d051ccde26", size = 82809, upload-time = "2026-07-24T09:21:01.178Z" },
+]
+
[[package]]
name = "httpx"
version = "0.27.2"
@@ -353,12 +365,19 @@ wheels = [
]
[[package]]
-name = "httpx-sse"
-version = "0.4.0"
+name = "httpx2"
+version = "2.9.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624, upload-time = "2023-12-22T08:01:21.083Z" }
+dependencies = [
+ { name = "anyio" },
+ { name = "httpcore2" },
+ { name = "idna" },
+ { name = "truststore" },
+ { name = "typing-extensions", marker = "python_full_version < '3.13'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/21/14/38128fbafd7e0ed41d874df6c9a653d47c2d111cfe59e2b4ac95161b4abd/httpx2-2.9.1.tar.gz", hash = "sha256:1932a768737e3666291582833da748cc4e563c337cf96706fccc04fa6e58764a", size = 95458, upload-time = "2026-07-24T09:21:04.972Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819, upload-time = "2023-12-22T08:01:19.89Z" },
+ { url = "https://files.pythonhosted.org/packages/13/b8/cfd91c4ab9134d386d48f0b6ac662ff3d4be6efdee59ee1c67ebc3c0487c/httpx2-2.9.1-py3-none-any.whl", hash = "sha256:1820fe14a9ab1107bfeff39259987429450b070ec0ff38cc87eb0d8c97fdc71a", size = 91191, upload-time = "2026-07-24T09:21:02.6Z" },
]
[[package]]
@@ -539,15 +558,15 @@ wheels = [
[[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'" },
@@ -557,9 +576,9 @@ 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]]
@@ -588,7 +607,7 @@ dev = [
requires-dist = [
{ name = "httpx", specifier = ">=0.27" },
{ name = "markdownify", specifier = ">=0.13.1" },
- { name = "mcp", specifier = ">=1.1.3" },
+ { name = "mcp", specifier = ">=2,<3" },
{ name = "protego", specifier = ">=0.3.1" },
{ name = "pydantic", specifier = ">=2.0.0" },
{ name = "readabilipy", specifier = ">=0.2.0" },
@@ -603,6 +622,19 @@ dev = [
{ name = "ruff", specifier = ">=0.7.3" },
]
+[[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]]
name = "nodeenv"
version = "1.9.1"
@@ -612,6 +644,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
]
+[[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.0"
@@ -781,19 +825,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" },
]
-[[package]]
-name = "pydantic-settings"
-version = "2.6.1"
-source = { registry = "https://pypi.org/simple" }
-dependencies = [
- { name = "pydantic" },
- { name = "python-dotenv" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/b5/d4/9dfbe238f45ad8b168f5c96ee49a3df0598ce18a0795a983b419949ce65b/pydantic_settings-2.6.1.tar.gz", hash = "sha256:e0f92546d8a9923cb8941689abf85d6601a8c19a23e97a34b2964a2e3f813ca0", size = 75646, upload-time = "2024-11-01T11:00:05.17Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/5e/f9/ff95fd7d760af42f647ea87f9b8a383d891cdb5e5dbd4613edaeb094252a/pydantic_settings-2.6.1-py3-none-any.whl", hash = "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87", size = 28595, upload-time = "2024-11-01T11:00:02.64Z" },
-]
-
[[package]]
name = "pygments"
version = "2.20.0"
@@ -865,15 +896,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
]
-[[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"
@@ -1194,16 +1216,15 @@ wheels = [
[[package]]
name = "sse-starlette"
-version = "2.1.3"
+version = "3.4.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "starlette" },
- { name = "uvicorn" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/72/fc/56ab9f116b2133521f532fce8d03194cf04dcac25f583cf3d839be4c0496/sse_starlette-2.1.3.tar.gz", hash = "sha256:9cd27eb35319e1414e3d2558ee7414487f9529ce3b3cf9b21434fd110e017169", size = 19678, upload-time = "2024-08-01T08:52:50.248Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/6c/10/a34c656829ffc1c4b22ef36d70d9ebb6b99c020e2aeb17cee5485099f028/sse_starlette-3.4.6.tar.gz", hash = "sha256:725f8a1bd6d26ae1b2c9610c0ef5065dfdd496f3988d28adcf8c4b49dc25c627", size = 32542, upload-time = "2026-07-20T14:16:32.201Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/52/aa/36b271bc4fa1d2796311ee7c7283a3a1c348bad426d37293609ca4300eef/sse_starlette-2.1.3-py3-none-any.whl", hash = "sha256:8ec846438b4665b9e8c560fcdea6bc8081a3abf7942faa95e5a744999d219772", size = 9383, upload-time = "2024-08-01T08:52:48.659Z" },
+ { url = "https://files.pythonhosted.org/packages/49/36/e10c1d1b7ca881d2625db2ec28508578499187bb1c389952c398474e1834/sse_starlette-3.4.6-py3-none-any.whl", hash = "sha256:56217ab4c9a9f9c5db7b21e08732d3e7c2b807f45231ad23de0551a24c4a41f6", size = 16516, upload-time = "2026-07-20T14:16:30.978Z" },
]
[[package]]
@@ -1273,6 +1294,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" },
]
+[[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"