From 0c8526125bb7b3afe001f3c9b7f88cfeca642247 Mon Sep 17 00:00:00 2001 From: Andrew Bruneel Date: Wed, 11 Mar 2026 14:31:57 -0700 Subject: [PATCH 1/2] Fix: updated boolean logic for endpoint registration --- cforge/commands/server/run.py | 4 +- tests/commands/server/test_run.py | 94 +++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/cforge/commands/server/run.py b/cforge/commands/server/run.py index 23f1060..eec2243 100644 --- a/cforge/commands/server/run.py +++ b/cforge/commands/server/run.py @@ -173,8 +173,8 @@ def run( # Register if requested if register: - # Default to SSE if no protocol specified - is_sse = expose_sse or expose_streamable_http or (not expose_sse and not expose_streamable_http) + # Use streamable HTTP only when it's explicitly enabled without SSE + is_sse = expose_sse or not expose_streamable_http registered_server_id: Optional[str] = None try: diff --git a/tests/commands/server/test_run.py b/tests/commands/server/test_run.py index 815fc7b..2ba4c54 100644 --- a/tests/commands/server/test_run.py +++ b/tests/commands/server/test_run.py @@ -735,3 +735,97 @@ def test_run_temporary_without_source_uses_fallback_name(self) -> None: # Verify cleanup was registered mock_atexit.register.assert_called_once() mock_process.assert_called_once() + + + def test_run_registration_with_streamable_http_only(self) -> None: + """Test that registration uses /mcp endpoint when only streamable HTTP is enabled.""" + with ( + patch("mcpgateway.translate.main") as mock_translate, + patch("multiprocessing.Process") as mock_process, + patch("cforge.commands.server.run.requests") as mock_requests, + patch("cforge.commands.server.run.make_authenticated_request") as mock_request, + ): + # Mock returning a 200 on health + mock_get_res = MagicMock() + mock_get_res.status_code = 200 + mock_requests.get = MagicMock(return_value=mock_get_res) + + mock_request.return_value = {"id": "streamable-http-server-id"} + + invoke_typer_command(run, stdio="uvx mcp-server-git", port=9005, expose_streamable_http=True, register=True) + + # Verify registration was attempted + mock_request.assert_called_once() + call_args = mock_request.call_args + json_data = call_args[1]["json_data"] + + # Verify correct endpoint and transport type for streamable HTTP + assert json_data["url"] == "http://127.0.0.1:9005/mcp" + assert json_data["transport"] == "STREAMABLEHTTP" + + # Verify translate_main was called via Process + mock_process.assert_called_once() + proc_call_args = mock_process.call_args[1] + assert proc_call_args.get("target") is mock_translate + + def test_run_registration_with_both_protocols_defaults_to_sse(self) -> None: + """Test that registration defaults to SSE when both protocols are enabled.""" + with ( + patch("mcpgateway.translate.main") as mock_translate, + patch("multiprocessing.Process") as mock_process, + patch("cforge.commands.server.run.requests") as mock_requests, + patch("cforge.commands.server.run.make_authenticated_request") as mock_request, + ): + # Mock returning a 200 on health + mock_get_res = MagicMock() + mock_get_res.status_code = 200 + mock_requests.get = MagicMock(return_value=mock_get_res) + + mock_request.return_value = {"id": "both-protocols-server-id"} + + invoke_typer_command(run, stdio="uvx mcp-server-git", port=9000, expose_sse=True, expose_streamable_http=True, register=True) + + # Verify registration was attempted + mock_request.assert_called_once() + call_args = mock_request.call_args + json_data = call_args[1]["json_data"] + + # Verify SSE is used when both protocols are enabled (SSE takes priority) + assert json_data["url"] == "http://127.0.0.1:9000/sse" + assert json_data["transport"] == "SSE" + + # Verify translate_main was called via Process + mock_process.assert_called_once() + proc_call_args = mock_process.call_args[1] + assert proc_call_args.get("target") is mock_translate + + def test_run_registration_without_protocol_flags_defaults_to_sse(self) -> None: + """Test that registration defaults to SSE when no protocol flags are specified.""" + with ( + patch("mcpgateway.translate.main") as mock_translate, + patch("multiprocessing.Process") as mock_process, + patch("cforge.commands.server.run.requests") as mock_requests, + patch("cforge.commands.server.run.make_authenticated_request") as mock_request, + ): + # Mock returning a 200 on health + mock_get_res = MagicMock() + mock_get_res.status_code = 200 + mock_requests.get = MagicMock(return_value=mock_get_res) + + mock_request.return_value = {"id": "default-server-id"} + + invoke_typer_command(run, stdio="uvx mcp-server-git", port=9000, register=True) + + # Verify registration was attempted + mock_request.assert_called_once() + call_args = mock_request.call_args + json_data = call_args[1]["json_data"] + + # Verify SSE is used by default when no protocol flags are specified + assert json_data["url"] == "http://127.0.0.1:9000/sse" + assert json_data["transport"] == "SSE" + + # Verify translate_main was called via Process + mock_process.assert_called_once() + proc_call_args = mock_process.call_args[1] + assert proc_call_args.get("target") is mock_translate From 13a994041f964b4ce4193a34cbe6cd7f84328c51 Mon Sep 17 00:00:00 2001 From: Andrew Bruneel Date: Fri, 13 Mar 2026 12:23:15 -0700 Subject: [PATCH 2/2] style: fix formatting in test_run.py (black auto-format) --- tests/commands/server/test_run.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/commands/server/test_run.py b/tests/commands/server/test_run.py index 2ba4c54..17036a4 100644 --- a/tests/commands/server/test_run.py +++ b/tests/commands/server/test_run.py @@ -736,7 +736,6 @@ def test_run_temporary_without_source_uses_fallback_name(self) -> None: mock_atexit.register.assert_called_once() mock_process.assert_called_once() - def test_run_registration_with_streamable_http_only(self) -> None: """Test that registration uses /mcp endpoint when only streamable HTTP is enabled.""" with (