Conversation
- Add visible stderr output during binary download (P0) - Fix CORE_MIN_VERSION 2.3.0 -> 2.4.0 to match SDK (P0) - Fix hardcoded version string in health check error message (P0) - Align cache directory to ~/.capiscio/bin/ (shared with SDK) (P1) - Remove unused platformdirs dependency (P1) - Add retry with exponential backoff for download (3 attempts) (P1)
|
✅ Integration tests passed! capiscio-core gRPC tests working. |
There was a problem hiding this comment.
Pull request overview
Updates capiscio-core compatibility requirements and binary lifecycle behavior to align with the current MCP SDK expectations, while simplifying cache-path handling and dependency set.
Changes:
- Removed
platformdirsdependency and switched binary cache location to~/.capiscio/bin. - Raised
CORE_MIN_VERSIONto2.4.0and updated version compatibility messaging to use constants. - Added retry/backoff logic and user-facing stderr output during binary download/install.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
pyproject.toml |
Drops platformdirs dependency after cache-dir implementation change. |
capiscio_mcp/_core/version.py |
Raises minimum compatible capiscio-core version to 2.4.0. |
capiscio_mcp/_core/lifecycle.py |
Changes cache dir path and adds download retry/backoff plus stderr output. |
capiscio_mcp/_core/health.py |
Uses version constants in the incompatibility error message (and expands imports). |
You can also share your feedback on Copilot code review. Take the survey.
capiscio_mcp/_core/lifecycle.py
Outdated
| sys.stderr.write( | ||
| f"capiscio-core v{version} not found. " | ||
| f"Downloading for {os_name}/{arch_name}...\n" | ||
| ) | ||
| sys.stderr.flush() |
There was a problem hiding this comment.
Avoid writing directly to sys.stderr from this library function. It produces unconfigurable output for SDK consumers (including tests/host apps). Prefer logger output (or gate the message behind an explicit verbose/debug flag).
| max_attempts = 3 | ||
| for attempt in range(1, max_attempts + 1): | ||
| try: | ||
| response = requests.get(url, stream=True, timeout=60) | ||
| response.raise_for_status() | ||
|
|
||
| # Write binary | ||
| with open(target_path, "wb") as f: | ||
| for chunk in response.iter_content(chunk_size=8192): | ||
| f.write(chunk) | ||
|
|
||
| # Make executable (Unix) | ||
| if os_name != "windows": | ||
| st = os.stat(target_path) | ||
| os.chmod(target_path, st.st_mode | stat.S_IEXEC) | ||
|
|
||
| sys.stderr.write(f"Installed capiscio-core v{version} at {target_path}\n") | ||
| sys.stderr.flush() | ||
| logger.info(f"Successfully installed capiscio-core v{version}") | ||
| return target_path | ||
|
|
||
| except Exception as e: | ||
| if target_path.exists(): | ||
| target_path.unlink() | ||
| if attempt < max_attempts: | ||
| delay = 2 ** (attempt - 1) | ||
| logger.warning( | ||
| f"Download attempt {attempt}/{max_attempts} failed: {e}. " | ||
| f"Retrying in {delay}s..." | ||
| ) | ||
| time.sleep(delay) | ||
| else: |
There was a problem hiding this comment.
This retry loop will also retry (and sleep) on permanent failures like HTTP 404/400 from raise_for_status(), which adds avoidable startup latency and slows the existing unit test that mocks a 404. Consider retrying only on transient errors (timeouts/connection errors/5xx), and fail fast on 4xx.
capiscio_mcp/_core/lifecycle.py
Outdated
| response = requests.get(url, stream=True, timeout=60) | ||
| response.raise_for_status() | ||
|
|
||
| # Write binary | ||
| with open(target_path, "wb") as f: | ||
| for chunk in response.iter_content(chunk_size=8192): | ||
| f.write(chunk) | ||
|
|
There was a problem hiding this comment.
The streaming download doesn't explicitly close the HTTP response. Using a context manager (or calling response.close()) helps avoid leaking connections across retries and keeps the requests connection pool healthy.
| response = requests.get(url, stream=True, timeout=60) | |
| response.raise_for_status() | |
| # Write binary | |
| with open(target_path, "wb") as f: | |
| for chunk in response.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
| with requests.get(url, stream=True, timeout=60) as response: | |
| response.raise_for_status() | |
| # Write binary | |
| with open(target_path, "wb") as f: | |
| for chunk in response.iter_content(chunk_size=8192): | |
| f.write(chunk) | |
capiscio_mcp/_core/lifecycle.py
Outdated
| sys.stderr.write(f"Installed capiscio-core v{version} at {target_path}\n") | ||
| sys.stderr.flush() | ||
| logger.info(f"Successfully installed capiscio-core v{version}") |
There was a problem hiding this comment.
Avoid writing directly to sys.stderr on successful install as well; this is noisy for library consumers. Prefer logging (or an opt-in progress callback/flag) so callers can control where output goes.
capiscio_mcp/_core/health.py
Outdated
| from typing import TYPE_CHECKING | ||
|
|
||
| from capiscio_mcp._core.version import is_core_compatible, PROTO_VERSION | ||
| from capiscio_mcp._core.version import is_core_compatible, PROTO_VERSION, CORE_MIN_VERSION, CORE_MAX_VERSION |
There was a problem hiding this comment.
This import line exceeds the configured 100-char line length and will be reformatted by Black. Please wrap it using parentheses to keep formatting consistent with the repo's Black configuration.
| from capiscio_mcp._core.version import is_core_compatible, PROTO_VERSION, CORE_MIN_VERSION, CORE_MAX_VERSION | |
| from capiscio_mcp._core.version import ( | |
| is_core_compatible, | |
| PROTO_VERSION, | |
| CORE_MIN_VERSION, | |
| CORE_MAX_VERSION, | |
| ) |
- Replace sys.stderr.write with logger.info for download messages - Use requests.get as context manager to prevent connection leaks - Only retry on transient errors (5xx/timeout/connection); fail fast on 4xx - Wrap long import line in health.py for Black compliance - Remove unused sys import - Update 404 test mock for context manager + fast-fail behavior
|
✅ Integration tests passed! capiscio-core gRPC tests working. |
Summary
Fixes binary install experience issues identified in the install audit. Companion to capiscio/capiscio-sdk-python#38.
P0 Fixes
CORE_MIN_VERSIONupdated from2.3.0to2.4.0to match SDKCORE_MIN_VERSION/CORE_MAX_VERSIONconstants instead of hardcoded2.5.0P1 Fixes
platformdirs.user_cache_dir("capiscio-mcp")to~/.capiscio/bin/so SDK and MCP share the same binary cacheplatformdirsdependency: No longer needed after cache dir changeFiles Changed
capiscio_mcp/_core/lifecycle.py— Download visibility, shared cache, retry logiccapiscio_mcp/_core/version.py— Version bumpcapiscio_mcp/_core/health.py— Dynamic error messagepyproject.toml— Remove platformdirs depTesting