Skip to content

Add CLI update notifications#2839

Merged
jlowin merged 3 commits intorelease/2.xfrom
cli-update-notification
Jan 10, 2026
Merged

Add CLI update notifications#2839
jlowin merged 3 commits intorelease/2.xfrom
cli-update-notification

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Jan 10, 2026

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
╭──────────────────────────────────────────────────────────────────────╮
│                 🎉 Update available: 2.14.3                          │
│                Run: uv pip install --upgrade fastmcp                 │
╰──────────────────────────────────────────────────────────────────────╯

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)
@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
@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 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 @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 3d425c8 and a11e6a3.

⛔ 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

Walkthrough

This PR adds version-checking to FastMCP. It introduces a new module src/fastmcp/utilities/version_check.py that fetches the latest PyPI release (with optional prerelease support), caches results for 12 hours, and exposes get_latest_version and check_for_newer_version. A new Settings field check_for_updates ("stable" | "prerelease" | "off", default "stable") is added. The CLI version command and the server banner now invoke the version checker and conditionally display a non-blocking update notification when a newer version is detected.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is incomplete; it lacks the required Contributors and Review Checklist sections specified in the repository template. Add the required checklist items from the template: Contributors Checklist (issue reference, workflow compliance, testing, documentation) and Review Checklist (self-review and readiness confirmation).
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add CLI update notifications' clearly and concisely describes the main change: adding update notifications to the CLI interface.
Docstring Coverage ✅ Passed Docstring coverage is 90.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.

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 (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 Exception catch 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}")
                 continue

This 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 Exception catch 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}")
         pass

This would help diagnose any issues with version string parsing.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3df8826 and 45cd586.

⛔ 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/cli/cli.py
  • src/fastmcp/utilities/cli.py
  • src/fastmcp/settings.py
  • src/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.py
  • src/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_updates setting 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 --copy output 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.assemble for 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:

  1. Use fresh cache if available
  2. Fetch from PyPI if cache is stale
  3. Fall back to stale cache if fetch fails

This ensures the feature degrades gracefully without blocking.

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: 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".

Comment on lines +9 to +13
import httpx
from packaging.version import Version

import fastmcp
from fastmcp.utilities.logging import get_logger
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 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 👍 / 👎.

@marvin-context-protocol
Copy link
Copy Markdown
Contributor

Test Failure Analysis

Summary: Integration test test_call_tool_list_commits failed due to GitHub API rate limiting (HTTP 429), causing a timeout. This failure is unrelated to the PR changes.

Root Cause: The test hit the GitHub Copilot MCP API rate limit during the list_commits tool call. The API returned a 429 status code, which caused the test to hang and timeout after 30 seconds. The actual error was revealed during teardown: httpx.HTTPStatusError: Client error '429 Too Many Requests' for url 'https://api.githubcopilot.com/mcp/'

Suggested Solution: This is an intermittent external API issue, not a code problem. The existing rate-limit detection hook in tests/integration_tests/conftest.py should have converted this to a skip, but didn't catch it because the timeout exception occurred during the call phase while the 429 error only appeared in teardown logs.

Options:

  1. Retry the workflow - The rate limit should reset and tests should pass
  2. Improve rate limit detection - Update tests/integration_tests/conftest.py:38-43 to better detect timeout+429 scenarios by checking teardown exceptions more thoroughly

The PR changes (CLI update notifications in cli.py, settings.py, utilities/cli.py, utilities/version_check.py) don't touch any code paths related to GitHub integration tests or HTTP client behavior, confirming this is an unrelated infrastructure issue.

Detailed Analysis

Failed Test

  • File: tests/integration_tests/test_github_mcp_remote.py:99
  • Test: test_call_tool_list_commits
  • Timeout: 30 seconds
  • API Endpoint: https://api.githubcopilot.com/mcp/

Error Timeline

  1. Test calls client.call_tool("list_commits", {"owner": "jlowin", "repo": "fastmcp"})
  2. Request sent to GitHub Copilot MCP API
  3. API returns 429 Too Many Requests
  4. Test hangs waiting for response
  5. Timeout exception after 30s
  6. During teardown, the 429 error surfaces: httpx.HTTPStatusError: Client error '429 Too Many Requests'

Why Rate Limit Hook Didn't Catch It

The existing hook in tests/integration_tests/conftest.py:53-73 has logic to detect rate limits, including:

  • Line 38-43: Checks for timeout exceptions with "429" in captured output
  • Line 46-48: Checks captured stdout/stderr sections

However, in this case the timeout occurred during the call phase but the 429 error only appeared in the teardown phase captured output, which may not have been available to the hook at the time it processed the timeout exception.

Related Files
  • tests/integration_tests/test_github_mcp_remote.py: Contains the failing test that calls the GitHub Copilot MCP API
  • tests/integration_tests/conftest.py: Contains rate-limit detection hook that should (but didn't) catch this failure
  • src/fastmcp/cli/cli.py: Modified in this PR for update notifications (unrelated to failure)
  • src/fastmcp/utilities/version_check.py: New file in this PR for version checking (unrelated to failure)

- Move fastmcp import inside functions to avoid circular imports
- Filter prereleases from releases list instead of using info.version
@jlowin jlowin merged commit 7628c43 into release/2.x Jan 10, 2026
7 of 8 checks passed
@jlowin jlowin deleted the cli-update-notification branch January 10, 2026 23:11
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