Skip to content

Add PingMiddleware for keepalive connections#2838

Merged
jlowin merged 4 commits intomainfrom
jlowin/ping-middleware
Jan 10, 2026
Merged

Add PingMiddleware for keepalive connections#2838
jlowin merged 4 commits intomainfrom
jlowin/ping-middleware

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Jan 10, 2026

Adds PingMiddleware that sends periodic server-to-client pings to keep long-lived connections alive. This addresses the issue where clients drop connections unnecessarily when servers don't periodically ping.

The middleware starts a background ping task on first message from each session, using the session's existing task group for automatic cleanup when the session ends.

from fastmcp import FastMCP
from fastmcp.server.middleware import PingMiddleware

mcp = FastMCP("MyServer")
mcp.add_middleware(PingMiddleware(interval_ms=5000))

Closes #1212

@marvin-context-protocol marvin-context-protocol Bot added enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality. labels Jan 10, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 10, 2026

Walkthrough

This PR adds a new PingMiddleware class to send periodic pings and keep client connections alive. The middleware accepts a configurable interval parameter (defaulting to 30,000 milliseconds) and validates that the interval is positive. Upon receiving the first message from a session, it initiates a background ping task using the session's subscription task group. The ping loop executes repeatedly at the configured interval until the session terminates, at which point the session is removed from active tracking. Thread-safe initialization is ensured via an internal lock. The implementation is exported in the middleware module's \__init__.py\\ file, and corresponding documentation is added to the middleware guide.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding PingMiddleware for keepalive functionality.
Description check ✅ Passed The description includes a clear explanation of the feature, code example, linked issue reference, and covers the repository's development workflow checklist.
Linked Issues check ✅ Passed The implementation addresses issue #1212 by providing configurable server-to-client pings via PingMiddleware with customizable interval_ms parameter to keep connections alive.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing PingMiddleware: new middleware class, exports, and documentation. No extraneous modifications detected.
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.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between daa2dac and 339e103.

⛔ Files ignored due to path filters (1)
  • tests/server/middleware/test_ping.py is excluded by none and included by none
📒 Files selected for processing (3)
  • docs/servers/middleware.mdx
  • src/fastmcp/server/middleware/__init__.py
  • src/fastmcp/server/middleware/ping.py
🧰 Additional context used
📓 Path-based instructions (4)
docs/**/*.mdx

📄 CodeRabbit inference engine (docs/.cursor/rules/mintlify.mdc)

docs/**/*.mdx: Use clear, direct language appropriate for technical audiences
Write in second person ('you') for instructions and procedures in MDX documentation
Use active voice over passive voice in MDX technical documentation
Employ present tense for current states and future tense for outcomes in MDX documentation
Maintain consistent terminology throughout all MDX documentation
Keep sentences concise while providing necessary context in MDX documentation
Use parallel structure in lists, headings, and procedures in MDX documentation
Lead with the most important information using inverted pyramid structure in MDX documentation
Use progressive disclosure in MDX documentation: present basic concepts before advanced ones
Break complex procedures into numbered steps in MDX documentation
Include prerequisites and context before instructions in MDX documentation
Provide expected outcomes for each major step in MDX documentation
End sections with next steps or related information in MDX documentation
Use descriptive, keyword-rich headings for navigation and SEO in MDX documentation
Focus on user goals and outcomes rather than system features in MDX documentation
Anticipate common questions and address them proactively in MDX documentation
Include troubleshooting for likely failure points in MDX documentation
Provide multiple pathways (beginner vs advanced) but offer an opinionated path to avoid overwhelming users in MDX documentation
Always include complete, runnable code examples that users can copy and execute in MDX documentation
Show proper error handling and edge case management in MDX code examples
Use realistic data instead of placeholder values in MDX code examples
Include expected outputs and results for verification in MDX code examples
Test all code examples thoroughly before publishing in MDX documentation
Specify language and include filename when relevant in MDX code examples
Add explanatory comments for complex logic in MDX code examples
Document all API...

Files:

  • docs/servers/middleware.mdx
docs/**/*.{md,mdx,json}

📄 CodeRabbit inference engine (AGENTS.md)

Documentation uses Mintlify framework. Files must be in docs.json to be included. Never modify docs/python-sdk/** (auto-generated)

Files:

  • docs/servers/middleware.mdx
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

docs/**/*.{md,mdx}: Code examples in documentation must explain before showing code and make blocks fully runnable (include imports)
Documentation structure: Headers form navigation guide with logical H2/H3 hierarchy. Content should be user-focused with sections motivating features (why) before mechanics (how). Use prose over code comments for important information
Never use 'This isn't...' or 'not just...' constructions in writing - state what something IS directly. Avoid defensive writing patterns

Files:

  • docs/servers/middleware.mdx
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/server/middleware/ping.py
  • src/fastmcp/server/middleware/__init__.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 : 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/server/middleware/__init__.py
🧬 Code graph analysis (2)
src/fastmcp/server/middleware/ping.py (2)
src/fastmcp/server/middleware/middleware.py (3)
  • CallNext (42-43)
  • Middleware (79-205)
  • MiddlewareContext (47-63)
src/fastmcp/server/context.py (2)
  • request_context (223-249)
  • session_id (403-452)
src/fastmcp/server/middleware/__init__.py (2)
src/fastmcp/client/client.py (1)
  • ping (753-756)
src/fastmcp/server/middleware/ping.py (1)
  • PingMiddleware (10-70)
🪛 Ruff (0.14.10)
src/fastmcp/server/middleware/ping.py

37-37: Avoid specifying long messages outside the exception class

(TRY003)

⏰ 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 (5)
src/fastmcp/server/middleware/ping.py (3)

27-40: LGTM! Clean initialization with proper validation.

The constructor correctly validates the interval parameter, initializes thread-safe tracking structures, and uses an async lock to prevent race conditions during concurrent session initialization.


42-61: LGTM! Robust session initialization with proper guards.

The implementation correctly:

  • Guards against sessions without an established MCP context
  • Uses a lock to prevent duplicate ping tasks from concurrent messages
  • Leverages the session's existing task group for automatic cleanup
  • Handles the edge case where _subscription_task_group might be None

The use of id(session) for tracking is appropriate since it provides unique identification for the object's lifetime within the process.


63-70: LGTM! Clean ping loop with proper cleanup.

The loop correctly:

  • Converts the interval from milliseconds to seconds
  • Runs indefinitely until the session ends or the task is cancelled
  • Uses a finally block to ensure cleanup regardless of how the task exits
  • Uses discard() to safely remove the session without raising if already removed

If send_ping() raises an exception, the task will exit gracefully and clean up, which is appropriate behavior since a failed ping likely indicates a dead connection.

src/fastmcp/server/middleware/__init__.py (1)

6-6: LGTM! Appropriate re-export of core middleware.

The re-export of PingMiddleware is intentional and correct. As a core middleware feature that users will directly import (e.g., from fastmcp.server.middleware import PingMiddleware), it belongs in the public API of the middleware package.

Based on learnings, this follows the guideline to only re-export core types that define a module's purpose.

Also applies to: 12-12

docs/servers/middleware.mdx (1)

620-648: LGTM! Comprehensive and well-structured documentation.

The Ping Middleware section follows all MDX documentation guidelines:

  • Uses clear, direct language appropriate for technical audiences
  • Presents basic concepts before configuration details (progressive disclosure)
  • Includes complete, runnable code examples with imports
  • Provides multiple usage patterns (default and custom intervals)
  • Adds helpful context about when ping middleware is most useful
  • Includes proper version badge for the new feature
  • Maintains consistent terminology and parameter naming with the implementation

The documentation effectively explains the behavior, configuration options, and appropriate use cases for the middleware.

As per coding guidelines, the documentation structure and examples meet all requirements for technical MDX content.


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

@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: 30b7c74bf3

ℹ️ 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/server/middleware/ping.py Outdated
Comment on lines +52 to +56
if session_id not in self._active_sessions:
# _subscription_task_group is added by MiddlewareServerSession
tg = session._subscription_task_group # type: ignore[attr-defined]
if tg is not None:
self._active_sessions.add(session_id)
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 Guard ping-loop creation against concurrent messages

Incoming messages are handled concurrently (see LowLevelServer.run starting each message in its own task), so two overlapping requests from the same session can both pass this session_id not in _active_sessions check before the set is updated, spawning multiple ping loops and sending duplicate pings for that session. This can happen when a client issues concurrent requests on a single connection. Consider synchronizing this check/add (e.g., an anyio.Lock or a per-session flag) so only one ping task starts.

Useful? React with 👍 / 👎.

@jlowin jlowin merged commit 4765f57 into main Jan 10, 2026
11 checks passed
@jlowin jlowin deleted the jlowin/ping-middleware branch January 10, 2026 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FastMCP Server should allow a configurable ping

1 participant