From b5d7270feaabcae27ff6743483946b8ec4a9b5fb Mon Sep 17 00:00:00 2001 From: Bortlesboat Date: Sun, 15 Mar 2026 17:13:07 -0400 Subject: [PATCH 1/4] fix: suppress platform string output to stdout on macOS --- main.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/main.py b/main.py index 8fb97c5..adbd69d 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,4 @@ +import io import argparse import logging import os @@ -6,6 +7,13 @@ import sys from importlib import metadata, import_module from dotenv import load_dotenv +# Prevent any stray output (e.g. platform identifiers like "darwin" on macOS) +# from corrupting the MCP JSON-RPC handshake on stdout. We capture anything +# written to stdout during module-level initialisation and replay it to stderr +# so that diagnostic information is not lost. +_original_stdout = sys.stdout +sys.stdout = io.StringIO() + # Check for CLI mode early - before loading oauth_config # CLI mode requires OAuth 2.0 since there's no MCP session context _CLI_MODE = "--cli" in sys.argv @@ -117,12 +125,21 @@ def narrow_permissions_to_services( } +def _restore_stdout(): + """Restore the real stdout and replay any captured output to stderr.""" + captured = sys.stdout.getvalue() + sys.stdout = _original_stdout + if captured: + print(captured, end="", file=sys.stderr) + + def main(): """ Main entry point for the Google Workspace MCP server. Uses FastMCP's native streamable-http transport. Supports CLI mode for direct tool invocation without running the server. """ + _restore_stdout() # Check if CLI mode is enabled - suppress startup messages if _CLI_MODE: # Suppress logging output in CLI mode for clean output From 16ce566d887ffa326f3f6035c8d83dd4d672a9cf Mon Sep 17 00:00:00 2001 From: Taylor Wilsdon Date: Tue, 17 Mar 2026 10:21:58 -0400 Subject: [PATCH 2/4] refac --- main.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/main.py b/main.py index aa2910a..02b66e1 100644 --- a/main.py +++ b/main.py @@ -125,9 +125,22 @@ def narrow_permissions_to_services( } -def _restore_stdout(): +def _restore_stdout() -> None: """Restore the real stdout and replay any captured output to stderr.""" - captured = sys.stdout.getvalue() + captured_stdout = sys.stdout + + # Idempotent: if already restored, nothing to do. + if captured_stdout is _original_stdout: + return + + required_stringio_methods = ("getvalue", "write", "flush") + if not all( + callable(getattr(captured_stdout, method_name, None)) + for method_name in required_stringio_methods + ): + return + + captured = captured_stdout.getvalue() sys.stdout = _original_stdout if captured: print(captured, end="", file=sys.stderr) From 96df53c5e9b0a231309049ae42012d2c6704845a Mon Sep 17 00:00:00 2001 From: Taylor Wilsdon Date: Tue, 17 Mar 2026 10:34:30 -0400 Subject: [PATCH 3/4] refac --- main.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/main.py b/main.py index 02b66e1..5051ce2 100644 --- a/main.py +++ b/main.py @@ -133,15 +133,17 @@ def _restore_stdout() -> None: if captured_stdout is _original_stdout: return + captured = "" required_stringio_methods = ("getvalue", "write", "flush") - if not all( - callable(getattr(captured_stdout, method_name, None)) - for method_name in required_stringio_methods - ): - return + try: + if all( + callable(getattr(captured_stdout, method_name, None)) + for method_name in required_stringio_methods + ): + captured = captured_stdout.getvalue() + finally: + sys.stdout = _original_stdout - captured = captured_stdout.getvalue() - sys.stdout = _original_stdout if captured: print(captured, end="", file=sys.stderr) From ffcea58fdc83d571d25efb55d00c8413c5b9cf9c Mon Sep 17 00:00:00 2001 From: Taylor Wilsdon Date: Tue, 17 Mar 2026 10:42:32 -0400 Subject: [PATCH 4/4] refac --- main.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index 5051ce2..401a270 100644 --- a/main.py +++ b/main.py @@ -7,12 +7,13 @@ import sys from importlib import metadata, import_module from dotenv import load_dotenv -# Prevent any stray output (e.g. platform identifiers like "darwin" on macOS) -# from corrupting the MCP JSON-RPC handshake on stdout. We capture anything -# written to stdout during module-level initialisation and replay it to stderr -# so that diagnostic information is not lost. +# Prevent any stray startup output on macOS (e.g. platform identifiers) from +# corrupting the MCP JSON-RPC handshake on stdout. We capture anything written +# to stdout during module-level initialisation and replay it to stderr so that +# diagnostic information is not lost. _original_stdout = sys.stdout -sys.stdout = io.StringIO() +if sys.platform == "darwin": + sys.stdout = io.StringIO() # Check for CLI mode early - before loading oauth_config # CLI mode requires OAuth 2.0 since there's no MCP session context