Add CLI update notifications#2839
Conversation
Show update availability in server banner and `fastmcp version` command. Checks PyPI with 12-hour cache and 2-second timeout. New setting: FASTMCP_CHECK_FOR_UPDATES (stable/prerelease/off)
|
Warning Rate limit exceeded@jlowin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 47 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
WalkthroughThis PR adds version-checking to FastMCP. It introduces a new module Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/fastmcp/utilities/version_check.py (2)
58-94: Consider logging version parsing failures at debug level.The function correctly fetches from PyPI with timeout and error handling. However, the bare
Exceptioncatch at lines 85-86 silently ignores version parsing failures, which could make debugging difficult.📝 Optional improvement for debuggability
for version_str in releases: try: versions.append(Version(version_str)) - except Exception: + except Exception: + logger.debug(f"Skipping invalid version string: {version_str}") continueThis would help with troubleshooting if PyPI returns unexpected version strings.
123-148: Consider logging version comparison failures at debug level.The function correctly checks settings and compares versions. However, the bare
Exceptioncatch at lines 144-146 silently ignores version comparison failures.📝 Optional improvement for debuggability
try: current = Version(fastmcp.__version__) latest = Version(latest_version) if latest > current: return latest_version - except Exception: + except Exception as e: # Invalid version strings, ignore + logger.debug(f"Failed to compare versions: {e}") passThis would help diagnose any issues with version string parsing.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
tests/utilities/test_version_check.pyis excluded by none and included by none
📒 Files selected for processing (4)
src/fastmcp/cli/cli.pysrc/fastmcp/settings.pysrc/fastmcp/utilities/cli.pysrc/fastmcp/utilities/version_check.py
🧰 Additional context used
📓 Path-based instructions (1)
src/fastmcp/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/fastmcp/**/*.py: Python ≥ 3.10 with full type annotations required
Prioritize readable, understandable code - clarity over cleverness. Avoid obfuscated or confusing patterns even if shorter
Follow existing patterns and maintain consistency in code implementation
Be intentional about re-exports - don't blindly re-export everything to parent namespaces. Core types defining a module's purpose should be exported. Specialized features can live in submodules. Only re-export to fastmcp.* for most fundamental types
Never use bare except - be specific with exception types
Files:
src/fastmcp/cli/cli.pysrc/fastmcp/utilities/cli.pysrc/fastmcp/settings.pysrc/fastmcp/utilities/version_check.py
🧠 Learnings (1)
📚 Learning: 2025-12-25T15:53:07.656Z
Learnt from: CR
Repo: jlowin/fastmcp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-25T15:53:07.656Z
Learning: Applies to src/fastmcp/**/*.py : Python ≥ 3.10 with full type annotations required
Applied to files:
src/fastmcp/utilities/cli.pysrc/fastmcp/settings.py
🧬 Code graph analysis (2)
src/fastmcp/cli/cli.py (1)
src/fastmcp/utilities/version_check.py (1)
check_for_newer_version(123-148)
src/fastmcp/utilities/cli.py (1)
src/fastmcp/utilities/version_check.py (1)
check_for_newer_version(123-148)
🪛 Ruff (0.14.10)
src/fastmcp/utilities/version_check.py
85-86: try-except-continue detected, consider logging the exception
(S112)
85-85: Do not catch blind exception: Exception
(BLE001)
144-146: try-except-pass detected, consider logging the exception
(S110)
144-144: Do not catch blind exception: Exception
(BLE001)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Run tests: Python 3.10 on windows-latest
- GitHub Check: Run tests: Python 3.13 on ubuntu-latest
- GitHub Check: Run tests: Python 3.10 on ubuntu-latest
- GitHub Check: Run tests with lowest-direct dependencies
🔇 Additional comments (10)
src/fastmcp/settings.py (1)
395-408: LGTM! Well-defined configuration field.The
check_for_updatessetting is properly typed with a Literal union, has clear documentation, and a sensible default. The field integrates cleanly with the existing settings pattern.src/fastmcp/cli/cli.py (2)
31-31: LGTM! Clean import.
126-132: LGTM! Well-integrated update notification.The update check is appropriately excluded from
--copyoutput and the messaging is clear and actionable. The walrus operator usage is clean and idiomatic for Python 3.10+.src/fastmcp/utilities/cli.py (4)
20-20: LGTM! Clean import.
204-205: LGTM! Non-blocking update check.The update check is correctly performed at the start of banner generation with appropriate silent failure handling.
238-242: LGTM! Improved text composition.Using
Text.assemblefor inline styling is the proper Rich API approach and makes the code more maintainable than concatenating styled fragments.
260-281: LGTM! Clean dynamic output building.The conditional update notice is well-integrated. Building the output elements dynamically allows for clean composition without affecting the core banner when no update is available.
src/fastmcp/utilities/version_check.py (3)
1-20: LGTM! Well-structured module with clear constants.The module organization is clean with appropriate imports, clear constants for PyPI URL, cache TTL (12 hours), and request timeout (2 seconds). These values are reasonable for a non-critical background feature.
22-55: LGTM! Robust cache implementation.The cache functions handle errors gracefully:
- Separate cache files for stable vs. prerelease checks
- Silent fallback on read failures (returns None, 0)
- Silent ignore on write failures (appropriate for non-critical cache)
- Proper parent directory creation
97-120: LGTM! Excellent caching strategy.The function implements a smart three-tier approach:
- Use fresh cache if available
- Fetch from PyPI if cache is stale
- Fall back to stale cache if fetch fails
This ensures the feature degrades gracefully without blocking.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 45cd58652d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| import httpx | ||
| from packaging.version import Version | ||
|
|
||
| import fastmcp | ||
| from fastmcp.utilities.logging import get_logger |
There was a problem hiding this comment.
Avoid circular import from module-level fastmcp import
Importing fastmcp.utilities.version_check directly now executes import fastmcp at module load time. That triggers fastmcp.__init__ → fastmcp.server.server fastmcp.utilities.cli, which does from fastmcp.utilities.version_check import check_for_newer_version while version_check is still initializing. In that scenario, the attribute is not defined yet and Python raises ImportError from a partially initialized module. This breaks direct imports (including the new tests) and any downstream usage that wants version-check utilities without importing fastmcp first. Move the import fastmcp into the functions that need it or otherwise break the module-level dependency to avoid the cycle.
Useful? React with 👍 / 👎.
Test Failure AnalysisSummary: Integration test Root Cause: The test hit the GitHub Copilot MCP API rate limit during the Suggested Solution: This is an intermittent external API issue, not a code problem. The existing rate-limit detection hook in Options:
The PR changes (CLI update notifications in Detailed AnalysisFailed Test
Error Timeline
Why Rate Limit Hook Didn't Catch ItThe existing hook in
However, in this case the timeout occurred during the Related Files
|
- Move fastmcp import inside functions to avoid circular imports - Filter prereleases from releases list instead of using info.version
The CLI now notifies users when a newer version of FastMCP is available on PyPI.
Where it shows:
fastmcp versioncommand (simple text, excluded from--copy)Setting:
FASTMCP_CHECK_FOR_UPDATES"stable"- Check for stable releases (default)"prerelease"- Include alpha/beta/rc versions"off"- Disable12-hour cache, 2-second timeout, fails silently on network errors.