feat(opax-mcp): add internal Cloud Run v2 fetch helper
Some checks are pending
Check Python Version Consistency / Check Python Version (push) Waiting to run

- Add a private helper that performs an authenticated, read-only GET for the
  configured opax-mcp Cloud Run v2 service.
- Return only generation, observedGeneration, and updateTime on success, with
  sanitized reason codes for token, HTTP, network, timeout, and parsing errors.
- Add mocked unit coverage for successful responses and handled failure paths.
This commit is contained in:
Chris Christiansen 2026-09-23 04:29:03 +00:00
parent c3fde1e38c
commit f0039088c2
2 changed files with 188 additions and 1 deletions

View File

@ -116,6 +116,64 @@ def _sanitize_opax_deployment_status(service_data: dict) -> dict:
}
def _fetch_opax_deployment_service_data() -> dict:
"""
Fetches the raw Cloud Run v2 service object for OPAX-MCP, returning
only a minimal subset of fields or a sanitized failure reason.
"""
import json
import socket
import urllib.error
import urllib.request
try:
token = _get_access_token()
if not token:
return {"reason_code": "ADC_TOKEN_UNAVAILABLE"}
endpoint_url = (
f"https://run.googleapis.com/v2/projects/{PROJECT_ID}/"
f"locations/{_OPAX_MCP_REGION}/services/{_OPAX_MCP_SERVICE}"
)
req = urllib.request.Request(
endpoint_url, headers={"Authorization": f"Bearer {token}"}, method="GET"
)
with urllib.request.urlopen(req, timeout=10) as resp:
status = getattr(resp, "status", None)
if not isinstance(status, int):
return {"reason_code": "UNEXPECTED_LOCAL_FAILURE"}
if not (200 <= status < 300):
return {"reason_code": "UNEXPECTED_NON_2XX_STATUS"}
body_bytes = resp.read()
body = json.loads(body_bytes)
if not isinstance(body, dict):
return {"reason_code": "INVALID_RESPONSE_SHAPE"}
return {
"generation": body.get("generation"),
"observedGeneration": body.get("observedGeneration"),
"updateTime": body.get("updateTime"),
}
except json.JSONDecodeError:
return {"reason_code": "INVALID_JSON_RESPONSE"}
except urllib.error.HTTPError as e:
if e.code in (401, 403):
return {"reason_code": "CLOUD_RUN_UNAUTHORIZED"}
if e.code == 404:
return {"reason_code": "CLOUD_RUN_NOT_FOUND"}
return {"reason_code": "UNEXPECTED_NON_2XX_STATUS"}
except socket.timeout:
return {"reason_code": "REQUEST_TIMEOUT"}
except urllib.error.URLError:
return {"reason_code": "NETWORK_FAILURE"}
except Exception:
return {"reason_code": "UNEXPECTED_LOCAL_FAILURE"}
class PushStaticRequest(BaseModel):
file_path: str
content: str

View File

@ -1,5 +1,12 @@
import unittest
from agents.mcp_server.server import _sanitize_opax_deployment_status
import json
import socket
import urllib.error
from unittest.mock import patch, MagicMock
from agents.mcp_server.server import (
_fetch_opax_deployment_service_data,
_sanitize_opax_deployment_status,
)
class TestSanitizeOpaxDeploymentStatus(unittest.TestCase):
@ -69,5 +76,127 @@ class TestSanitizeOpaxDeploymentStatus(unittest.TestCase):
self.assertEqual(result["region"], "us-central1")
self.assertIsNone(result["reason_code"])
class TestFetchOpaxDeploymentServiceData(unittest.TestCase):
@patch('urllib.request.urlopen')
@patch('agents.mcp_server.server._get_access_token')
def test_success_filters_fields(self, mock_get_token, mock_urlopen):
mock_get_token.return_value = "fake-token"
mock_response = MagicMock()
mock_response.status = 200
mock_response.read.return_value = json.dumps({
"generation": 5, "observedGeneration": 5, "updateTime": "T",
"uri": "forbidden"
}).encode()
mock_urlopen.return_value.__enter__.return_value = mock_response
result = _fetch_opax_deployment_service_data()
self.assertEqual(result, {"generation": 5, "observedGeneration": 5, "updateTime": "T"})
self.assertEqual(set(result.keys()), {"generation", "observedGeneration", "updateTime"})
@patch('urllib.request.urlopen')
@patch('agents.mcp_server.server._get_access_token')
def test_success_missing_fields(self, mock_get_token, mock_urlopen):
mock_get_token.return_value = "fake-token"
mock_response = MagicMock()
mock_response.status = 200
mock_response.read.return_value = json.dumps({"generation": 1}).encode()
mock_urlopen.return_value.__enter__.return_value = mock_response
result = _fetch_opax_deployment_service_data()
self.assertEqual(result, {"generation": 1, "observedGeneration": None, "updateTime": None})
@patch('urllib.request.urlopen')
@patch('agents.mcp_server.server._get_access_token')
def test_falsy_token_skips_call(self, mock_get_token, mock_urlopen):
for token in [None, ""]:
with self.subTest(token=token):
mock_get_token.return_value = token
result = _fetch_opax_deployment_service_data()
self.assertEqual(result, {"reason_code": "ADC_TOKEN_UNAVAILABLE"})
mock_urlopen.assert_not_called()
@patch('urllib.request.urlopen')
@patch('agents.mcp_server.server._get_access_token')
def test_get_token_exception(self, mock_get_token, mock_urlopen):
mock_get_token.side_effect = Exception("local failure")
result = _fetch_opax_deployment_service_data()
self.assertEqual(result, {"reason_code": "UNEXPECTED_LOCAL_FAILURE"})
mock_urlopen.assert_not_called()
@patch('urllib.request.urlopen')
@patch('agents.mcp_server.server._get_access_token')
def test_absent_response_status(self, mock_get_token, mock_urlopen):
mock_get_token.return_value = "fake-token"
class MockResp:
def __init__(self):
self.read_called = False
def read(self):
self.read_called = True
return b""
def __enter__(self):
return self
def __exit__(self, *args):
return False
mock_response = MockResp()
mock_urlopen.return_value = mock_response
result = _fetch_opax_deployment_service_data()
self.assertEqual(result, {"reason_code": "UNEXPECTED_LOCAL_FAILURE"})
self.assertFalse(mock_response.read_called)
@patch('urllib.request.urlopen')
@patch('agents.mcp_server.server._get_access_token')
def test_non_2xx_status_skips_read(self, mock_get_token, mock_urlopen):
mock_get_token.return_value = "fake-token"
mock_response = MagicMock()
mock_response.status = 503
mock_urlopen.return_value.__enter__.return_value = mock_response
result = _fetch_opax_deployment_service_data()
self.assertEqual(result, {"reason_code": "UNEXPECTED_NON_2XX_STATUS"})
mock_response.read.assert_not_called()
@patch('urllib.request.urlopen')
@patch('agents.mcp_server.server._get_access_token')
def test_http_and_network_errors(self, mock_get_token, mock_urlopen):
mock_get_token.return_value = "fake-token"
errors_to_reasons = [
(urllib.error.HTTPError(None, 401, "", {}, None), "CLOUD_RUN_UNAUTHORIZED"),
(urllib.error.HTTPError(None, 403, "", {}, None), "CLOUD_RUN_UNAUTHORIZED"),
(urllib.error.HTTPError(None, 404, "", {}, None), "CLOUD_RUN_NOT_FOUND"),
(urllib.error.HTTPError(None, 500, "", {}, None), "UNEXPECTED_NON_2XX_STATUS"),
(socket.timeout(), "REQUEST_TIMEOUT"),
(urllib.error.URLError("DNS failure"), "NETWORK_FAILURE"),
]
for error, reason in errors_to_reasons:
with self.subTest(error=error.__class__.__name__, code=getattr(error, 'code', 'N/A')):
mock_urlopen.side_effect = error
result = _fetch_opax_deployment_service_data()
self.assertEqual(result, {"reason_code": reason})
mock_urlopen.side_effect = None
@patch('urllib.request.urlopen')
@patch('agents.mcp_server.server._get_access_token')
def test_invalid_json_body(self, mock_get_token, mock_urlopen):
mock_get_token.return_value = "fake-token"
mock_response = MagicMock()
mock_response.status = 200
mock_urlopen.return_value.__enter__.return_value = mock_response
# Malformed JSON
with self.subTest(case="malformed"):
mock_response.read.return_value = b'{"key":'
result = _fetch_opax_deployment_service_data()
self.assertEqual(result, {"reason_code": "INVALID_JSON_RESPONSE"})
# Valid JSON, but not a dictionary object
with self.subTest(case="non-object"):
mock_response.read.return_value = b'[1, 2, 3]'
result = _fetch_opax_deployment_service_data()
self.assertEqual(result, {"reason_code": "INVALID_RESPONSE_SHAPE"})
if __name__ == '__main__':
unittest.main()