Skip to content

Add CLI update notifications#2840

Merged
jlowin merged 4 commits intomainfrom
cli-update-notification-main
Jan 10, 2026
Merged

Add CLI update notifications#2840
jlowin merged 4 commits intomainfrom
cli-update-notification-main

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Jan 10, 2026

Cherry-pick of #2839 for 3.x branch.

The CLI now notifies users when a newer version of FastMCP is available on PyPI.

Where it shows:

  • Server banner (in a blue box)
  • fastmcp version command (simple text, excluded from --copy)

Setting: FASTMCP_CHECK_FOR_UPDATES

  • "stable" - Check for stable releases (default)
  • "prerelease" - Include alpha/beta/rc versions
  • "off" - Disable

12-hour cache, 2-second timeout, fails silently on network errors.

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)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 10, 2026

Warning

Rate limit exceeded

@jlowin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 13 minutes and 25 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

📥 Commits

Reviewing files that changed from the base of the PR and between 84c7807 and 48fa6a2.

📒 Files selected for processing (2)
  • src/fastmcp/cli/cli.py
  • src/fastmcp/utilities/cli.py

Walkthrough

Adds a version-checking feature and UI to FastMCP: a new module provides cached PyPI lookups (stable/prerelease) and public functions get_latest_version and check_for_newer_version; Settings gains a check_for_updates option ("stable" | "prerelease" | "off"); the CLI banner and the version command call the version check and conditionally render an update notice when a newer version is detected.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description explains the feature, its locations, settings, and implementation details, but does not follow the required template structure with Contributors and Review checklists. Add the Contributors Checklist and Review Checklist sections from the template to confirm completion of required development workflow and testing steps.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly reflects the main change: adding CLI update notifications to inform users about newer FastMCP versions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@marvin-context-protocol marvin-context-protocol Bot added enhancement Improvement to existing functionality. For issues and smaller PR improvements. cli Related to FastMCP CLI commands (run, dev, install) or CLI functionality. labels Jan 10, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 95670b4bef

ℹ️ 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".

Comment thread src/fastmcp/utilities/version_check.py Outdated
Comment on lines +72 to +74
if not include_prereleases:
# Use the stable version from info
return data.get("info", {}).get("version")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter prereleases when stable-only is selected

With include_prereleases=False, _fetch_latest_version returns info.version directly. The PyPI JSON info.version reflects the most recently uploaded release, which can be a prerelease. When a prerelease is the latest upload, the default “stable” setting will still surface that prerelease, so users who opted into stable-only updates will get prerelease prompts. Consider selecting the highest non-prerelease version from releases instead of info.version when include_prereleases is false.

Useful? React with 👍 / 👎.

- Move fastmcp import inside functions to avoid circular imports
- Filter prereleases from releases list instead of using info.version
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/fastmcp/utilities/version_check.py (1)

204-205: Consider startup latency on first run.

On first run (no cache), this synchronous call adds up to 2 seconds of latency to banner display. While the timeout and caching mitigate this for subsequent runs, you may want to document this behavior or consider an async implementation if startup time is critical.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e1fd1d and 30aeb62.

⛔ Files ignored due to path filters (1)
  • tests/utilities/test_version_check.py is excluded by none and included by none
📒 Files selected for processing (4)
  • src/fastmcp/cli/cli.py
  • src/fastmcp/settings.py
  • src/fastmcp/utilities/cli.py
  • src/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/utilities/version_check.py
  • src/fastmcp/settings.py
  • src/fastmcp/cli/cli.py
  • src/fastmcp/utilities/cli.py
🧠 Learnings (2)
📚 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/version_check.py
  • src/fastmcp/utilities/cli.py
📚 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 : 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

Applied to files:

  • src/fastmcp/utilities/version_check.py
🧬 Code graph analysis (3)
src/fastmcp/utilities/version_check.py (2)
src/fastmcp/cli/cli.py (1)
  • version (93-131)
src/fastmcp/utilities/logging.py (1)
  • get_logger (14-26)
src/fastmcp/cli/cli.py (1)
src/fastmcp/utilities/version_check.py (1)
  • check_for_newer_version (124-151)
src/fastmcp/utilities/cli.py (1)
src/fastmcp/utilities/version_check.py (1)
  • check_for_newer_version (124-151)
⏰ 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 ubuntu-latest
  • GitHub Check: Run tests: Python 3.13 on ubuntu-latest
  • GitHub Check: Run tests with lowest-direct dependencies
  • GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (8)
src/fastmcp/utilities/version_check.py (2)

1-151: LGTM! Well-structured version checking implementation.

The module is well-designed with appropriate separation of concerns, proper error handling, and clean type annotations. The caching strategy and timeout configuration are sensible for a non-critical background feature.


67-70: Exception handling is correct and complete.

The current handler except (httpx.HTTPError, json.JSONDecodeError, KeyError) successfully catches all relevant httpx exceptions. Both httpx.TimeoutException and httpx.ConnectError are subclasses of httpx.HTTPError through the inheritance chain (TransportError → RequestError → HTTPError), so they are properly covered.

src/fastmcp/cli/cli.py (1)

32-32: LGTM!

Clean import addition following the existing import structure.

src/fastmcp/settings.py (1)

337-350: LGTM! Well-documented configuration option.

The new setting follows the established patterns in the Settings class with clear documentation and sensible defaults. The Literal type ensures type safety for the three allowed values.

src/fastmcp/utilities/cli.py (4)

20-20: LGTM!

Import follows the existing pattern and module structure.


204-205: LGTM! Non-blocking version check.

The version check is appropriately placed and will fail silently if network issues occur, ensuring the banner always displays regardless of PyPI availability.


238-242: LGTM! Enhanced text styling.

The change from a simple Text object to Text.assemble provides better visual emphasis on the version constraint, making the notice more scannable.


260-280: LGTM! Clean output composition.

The refactored output construction is well-structured with proper type annotations and clear conditional logic for the update notice. The update panel appears last for maximum visibility.

Comment thread src/fastmcp/cli/cli.py Outdated
@marvin-context-protocol
Copy link
Copy Markdown
Contributor

Test Failure Analysis

Summary: The test test_verify_token_validates_expiration failed due to a timing issue - the token expired slightly earlier than expected during the sleep period.

Root Cause: The test creates a JWT token with a 1-second expiration, verifies it immediately, then sleeps for 1.1 seconds and expects the token to be rejected as expired. However, the actual expiration check happened at exactly the boundary (token was issued at 22:53:51 with exp=1768085631, and validation failed at 22:53:51 showing "Token expired"), suggesting the token expired right at or slightly before the sleep completed.

This is a classic timing-based test flakiness issue - the test assumes time.sleep(1.1) will consistently provide enough buffer, but in CI environments with varying load, the token might expire fractionally earlier than expected.

Note: This failure is unrelated to the PR changes (CLI update notifications). The test is part of the JWT authentication system, while this PR only adds version checking functionality to the CLI.

Suggested Solution: Make the test more robust by using a larger expiration buffer:

# In tests/server/auth/test_jwt_issuer.py, line 171
expires_in=2,  # Changed from 1 to 2 seconds

And adjust the sleep accordingly:

# Line 179
time.sleep(2.5)  # Changed from 1.1 to 2.5 seconds
Detailed Analysis

Test Location: tests/server/auth/test_jwt_issuer.py::TestJWTIssuer::test_verify_token_validates_expiration (lines 164-183)

Failure Log:

DEBUG    Issued access token for client=client-abc jti=token-id exp=1768085631
DEBUG    Token expired
DEBUG    Token validation failed: Token has expired:
FAILED tests/server/auth/test_jwt_issuer.py::TestJWTIssuer::test_verify_token_validates_expiration

Why It Failed:

  1. Token issued at 22:53:51 (timestamp shows exp=1768085631)
  2. Test sleeps for 1.1 seconds
  3. Token verification happens, but timing boundaries cause the token to be considered expired

The issue is that time.sleep(1.1) doesn't guarantee the token will be rejected - there's not enough margin for clock precision and CI environment variations.

Related Files
  • tests/server/auth/test_jwt_issuer.py: Contains the flaky test
  • src/fastmcp/server/auth/jwt_issuer.py: JWT token verification logic (correctly checking expiration)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/fastmcp/utilities/version_check.py (1)

59-95: LGTM! Solid PyPI fetch logic.

The version fetching is well-implemented with appropriate error handling and version parsing. The synchronous httpx.get() call is correct for CLI usage.

One minor observation: KeyError in the except clause at line 94 may be unnecessary since dict.get("releases", {}) at line 73 won't raise KeyError. However, it's harmless defensive programming.

♻️ Optional: Simplify exception handling
-    except (httpx.HTTPError, json.JSONDecodeError, KeyError):
+    except (httpx.HTTPError, json.JSONDecodeError):
         return None
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30aeb62 and 84c7807.

⛔ Files ignored due to path filters (1)
  • tests/utilities/test_version_check.py is excluded by none and included by none
📒 Files selected for processing (1)
  • src/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/utilities/version_check.py
🧠 Learnings (2)
📚 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/version_check.py
📚 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 : 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

Applied to files:

  • src/fastmcp/utilities/version_check.py
🧬 Code graph analysis (1)
src/fastmcp/utilities/version_check.py (4)
src/fastmcp/cli/cli.py (1)
  • version (93-131)
src/fastmcp/utilities/logging.py (1)
  • get_logger (14-26)
src/fastmcp/server/server.py (1)
  • settings (414-422)
src/fastmcp/server/context.py (1)
  • debug (468-482)
⏰ 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). (1)
  • GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (3)
src/fastmcp/utilities/version_check.py (3)

1-18: LGTM! Clean module setup.

The imports, logger initialization, and constants are well-structured. The 12-hour cache TTL and 2-second network timeout are sensible defaults for a CLI update checker.


21-56: LGTM! Robust cache management.

The cache helper functions are well-implemented:

  • Circular import handling via late import is appropriate
  • Separate cache files for stable vs prerelease is clean
  • Error handling is comprehensive and fails gracefully
  • Silent OSError handling in _write_cache prevents permission issues from breaking functionality

98-153: LGTM! Public API is well-designed.

Both public functions are correctly implemented:

  • get_latest_version: Cache-first strategy with stale fallback is robust for network failures
  • check_for_newer_version: Settings integration and version comparison logic are sound

The implicit handling of unknown settings values (treating them as "stable") provides sensible default behavior. The circular import prevention through late binding is appropriate per your commit message.

@jlowin jlowin merged commit 797297a into main Jan 10, 2026
11 checks passed
@jlowin jlowin deleted the cli-update-notification-main branch January 10, 2026 23:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Related to FastMCP CLI commands (run, dev, install) or CLI functionality. enhancement Improvement to existing functionality. For issues and smaller PR improvements.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant