This is an automated email from the ASF dual-hosted git repository.
yzheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris-tools.git
The following commit(s) were added to refs/heads/main by this push:
new 49461c0 [MCP] feat: add retry for HTTP requests (#72)
49461c0 is described below
commit 49461c08f9e240e4970bd742530cbc7defed1ff2
Author: Yong Zheng <[email protected]>
AuthorDate: Tue Nov 25 01:33:58 2025 -0600
[MCP] feat: add retry for HTTP requests (#72)
* Added retry for HTTP requests
* Variable renaming for consistency
* Fix lint
* Fix conflict
---
mcp-server/README.md | 3 ++-
mcp-server/polaris_mcp/server.py | 19 ++++++++++++++++++-
mcp-server/tests/test_server.py | 40 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 60 insertions(+), 2 deletions(-)
diff --git a/mcp-server/README.md b/mcp-server/README.md
index 2dbb1bd..7d87637 100644
--- a/mcp-server/README.md
+++ b/mcp-server/README.md
@@ -77,7 +77,8 @@ Please note: `--directory` specifies a local directory. It is
not needed when we
| `POLARIS_HTTP_TIMEOUT_SECONDS` | Default
timeout in seconds for all HTTP requests. | `30.0`
|
| `POLARIS_HTTP_CONNECT_TIMEOUT_SECONDS` | Timeout in
seconds for establishing HTTP connections. | `30.0`
|
| `POLARIS_HTTP_READ_TIMEOUT_SECONDS` | Timeout in
seconds for reading HTTP responses. | `30.0`
|
-
+| `POLARIS_HTTP_RETRIES_TOTAL` | Total
number of retries for HTTP requests. | `3`
|
+| `POLARIS_HTTP_RETRIES_BACKOFF_FACTOR` | Factor for
exponential backoff between retries. | `0.5`
|
When OAuth variables are supplied, the server automatically acquires and
refreshes tokens using the client credentials flow; otherwise a static bearer
token is used if provided.
diff --git a/mcp-server/polaris_mcp/server.py b/mcp-server/polaris_mcp/server.py
index 5e5ebdc..02b37a0 100644
--- a/mcp-server/polaris_mcp/server.py
+++ b/mcp-server/polaris_mcp/server.py
@@ -63,6 +63,9 @@ OUTPUT_SCHEMA = {
}
DEFAULT_TOKEN_REFRESH_BUFFER_SECONDS = 60.0
DEFAULT_HTTP_TIMEOUT = 30.0
+DEFAULT_HTTP_RETRIES_TOTAL = 3
+DEFAULT_HTTP_RETRIES_BACKOFF_FACTOR = 0.5
+HTTP_RETRIES_STATUS_FORCELIST = [401, 409, 429]
LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
@@ -87,7 +90,21 @@ def create_server() -> FastMCP:
"""Construct a FastMCP server with Polaris tools."""
base_url = _resolve_base_url()
timeout = _resolve_http_timeout()
- http = urllib3.PoolManager()
+ total_retries = int(
+ os.getenv("POLARIS_HTTP_RETRIES_TOTAL", DEFAULT_HTTP_RETRIES_TOTAL)
+ )
+ backoff_factor = float(
+ os.getenv(
+ "POLARIS_HTTP_RETRIES_BACKOFF_FACTOR",
+ DEFAULT_HTTP_RETRIES_BACKOFF_FACTOR,
+ )
+ )
+ retry_strategy = urllib3.Retry(
+ total=total_retries,
+ backoff_factor=backoff_factor,
+ status_forcelist=HTTP_RETRIES_STATUS_FORCELIST,
+ )
+ http = urllib3.PoolManager(retries=retry_strategy)
authorization_provider = _resolve_authorization_provider(base_url, http,
timeout)
catalog_rest = PolarisRestTool(
name="polaris.rest.catalog",
diff --git a/mcp-server/tests/test_server.py b/mcp-server/tests/test_server.py
index 906908c..0ba55e0 100644
--- a/mcp-server/tests/test_server.py
+++ b/mcp-server/tests/test_server.py
@@ -319,3 +319,43 @@ class TestServerConfiguration:
timeout = server._resolve_http_timeout()
assert timeout.connect_timeout == server.DEFAULT_HTTP_TIMEOUT
assert timeout.read_timeout == server.DEFAULT_HTTP_TIMEOUT
+
+
+class TestServerRetry:
+ def test_create_server_default_retry(self) -> None:
+ """Verify that the HTTP client is created with a default retry
strategy."""
+ with (
+ mock.patch("polaris_mcp.server.urllib3.PoolManager") as
mock_pool_manager,
+ mock.patch("polaris_mcp.server.urllib3.Retry") as mock_retry,
+ ):
+ server.create_server()
+
+ mock_retry.assert_called_once_with(
+ total=3,
+ backoff_factor=0.5,
+ status_forcelist=server.HTTP_RETRIES_STATUS_FORCELIST,
+ )
+
mock_pool_manager.assert_called_once_with(retries=mock_retry.return_value)
+
+ def test_create_server_custom_retry(self) -> None:
+ """Verify that the HTTP client is created with a custom retry strategy
from environment variables."""
+ with (
+ mock.patch("polaris_mcp.server.urllib3.PoolManager") as
mock_pool_manager,
+ mock.patch("polaris_mcp.server.urllib3.Retry") as mock_retry,
+ mock.patch.dict(
+ os.environ,
+ {
+ "POLARIS_HTTP_RETRIES_TOTAL": "10",
+ "POLARIS_HTTP_RETRIES_BACKOFF_FACTOR": "1.0",
+ },
+ clear=True,
+ ),
+ ):
+ server.create_server()
+
+ mock_retry.assert_called_once_with(
+ total=10,
+ backoff_factor=1.0,
+ status_forcelist=server.HTTP_RETRIES_STATUS_FORCELIST,
+ )
+
mock_pool_manager.assert_called_once_with(retries=mock_retry.return_value)