---
title: "SIGPIPE Analysis"
description: "Critical SIGPIPE signal handling requirement for MCP servers on Unix/Linux"
---
# SIGPIPE Analysis: Critical MCP Server Requirement
**Date**: 2026-01-31
**Discovery Phase**: Phase 4.6 - Technical Debt Removal
**Impact**: CRITICAL for Unix/Linux MCP server stability
---
## Executive Summary
**SIGPIPE handling is NOT optional for production MCP servers on Unix/Linux!**
- **Without SIGPIPE handling**: Server process terminates when client disconnects
- **With SIGPIPE handling**: Server gracefully detects disconnect and cleans up
- **Windows**: No SIGPIPE exists, errors returned directly (already handled)
---
## What is SIGPIPE?
### Unix/Linux Behavior
**SIGPIPE = "Broken Pipe" Signal**
- **Sent when**: Process writes to a pipe/socket with closed reading end
- **Default action**: **TERMINATE THE PROCESS** (no core dump)
- **MCP Context**: Client dies/disconnects → server tries to write → SIGPIPE → server crashes
### Real-World MCP Scenario
```
1. MCP client launches server subprocess (stdio transport)
2. Client and server communicate via stdin/stdout pipes
3. Client crashes or user closes editor
4. Server tries to write response to stdout
5. WITHOUT SIGPIPE HANDLER: Server process terminates immediately
6. WITH SIGPIPE HANDLER: Server detects disconnect, cleans up, exits gracefully
```
---
## Impact Analysis
### Unix/Linux Servers (CRITICAL)
| Scenario | Without SIGPIPE Handling | With SIGPIPE Handling |
|----------|-------------------------|----------------------|
| **Client disconnect** | Server crashes | Graceful shutdown |
| **Network issues** | Process termination | Error detection |
| **Docker containers** | Abrupt exit | Clean exit |
| **Resource cleanup** | NOT EXECUTED | Properly executed |
| **Logs** | Incomplete/corrupted | Flushed properly |
**Real Impact**:
- Database connections not closed
- Temp files not cleaned up
- Logs not flushed
- Parent process left in zombie state
### Windows Servers (NO IMPACT)
- Windows does **NOT** implement POSIX SIGPIPE
- Socket write failures return error codes directly (WSAECONNRESET, etc.)
- Our code already checks return values and handles errors
- **Conclusion**: Silent logging on Windows is appropriate
---
## MCP Protocol Best Practices
From official MCP specification and community best practices:
### Required: SIGPIPE Handling
```python
# REQUIRED for robust MCP servers on Unix/Linux
import signal
# Option 1: Ignore SIGPIPE (get EPIPE error instead)
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
# Option 2: Handle SIGPIPE for graceful shutdown
signal.signal(signal.SIGPIPE, graceful_shutdown_handler)
```
### MCP Server Transport Requirements
1. **stdio communication**: Client ↔ Server via stdin/stdout
2. **JSON-RPC messages**: One per line, newline-delimited
3. **stdout MUST ONLY contain protocol messages** (no logs, no debug output)
4. **stderr for logs only**
5. **SIGPIPE handling REQUIRED** for graceful disconnect detection
---
## Current Implementation Analysis
### What the Code Does Now
```python
# src/mcp_atlassian/utils/lifecycle.py (line 40-45)
if hasattr(signal, "SIGPIPE"):
signal.signal(signal.SIGPIPE, signal_handler)
logger.debug("SIGPIPE handler registered")
else:
logger.debug("SIGPIPE not available on this platform")
```
**Behavior**:
- **Unix/Linux**: Registers graceful shutdown handler → Server survives client disconnect
- **Windows**: Logs that SIGPIPE isn't available → No issue (Windows doesn't need it)
### Signal Handler Implementation
```python
def signal_handler(signum: int, frame: Any) -> None:
"""Handle shutdown signals gracefully."""
_shutdown_event.set() # Thread-safe shutdown trigger
```
**What this achieves**:
- SIGPIPE triggers shutdown event
- Main loop detects shutdown event
- `ensure_clean_exit()` flushes streams
- Resources cleaned up properly
- Process exits cleanly
---
## Decision: Is Silent Logging Appropriate?
### Option 1: Keep Debug-Level Logging (CURRENT)
```python
logger.debug("SIGPIPE not available on this platform")
```
**Pros**:
- No noise in production logs
- Developers see it when debugging (debug level)
- Appropriate for platform difference (not an error)
**Cons**:
- Silent on Windows (but Windows doesn't need it)
### Option 2: Upgrade to Info-Level Logging
```python
logger.info("SIGPIPE not available on this platform (Windows detected)")
```
**Pros**:
- More visible in logs
- Makes cross-platform behavior explicit
**Cons**:
- Log noise on every Windows startup
- Not actionable (Windows users can't "fix" this)
### Option 3: Warning-Level Logging
```python
logger.warning("SIGPIPE not available - client disconnects may not be detected cleanly")
```
**Pros**:
- Alerts to potential issue
**Cons**:
- **FALSE WARNING** - Windows handles disconnects differently (via error codes)
- Creates unnecessary alarm
---
## Recommendation
### ✅ KEEP DEBUG-LEVEL LOGGING (Current Implementation)
**Rationale**:
1. **Windows doesn't need SIGPIPE** - It handles disconnects via error codes (WSAECONNRESET)
2. **Not an error or warning** - It's expected platform behavior
3. **Debug visibility** - Developers debugging can see the platform detection
4. **No action needed** - Users can't and shouldn't do anything about it
5. **Follows Python conventions** - Debug for informational platform differences
### Enhancement: Improve Documentation
Update the docstring to make the platform behavior explicit:
```python
def setup_signal_handlers() -> None:
"""Set up signal handlers for graceful shutdown.
Registers handlers for SIGTERM, SIGINT, and SIGPIPE (Unix/Linux only) to ensure
the application shuts down cleanly when receiving termination signals.
Platform Behavior:
- Unix/Linux: SIGPIPE handled to prevent process termination on client disconnect
- Windows: SIGPIPE not available (socket errors returned directly instead)
This is particularly important for:
- MCP stdio transport (client disconnect detection)
- Docker containers running with the -i flag
- Long-running server processes with unreliable clients
"""
```
---
## Celebration: What We Fixed
### 🎉 Mypy Error FIXED
**Before**: `Module has no attribute "SIGPIPE"`
**After**: Type-safe check with `hasattr(signal, "SIGPIPE")`
### 🎉 Pre-commit Hooks ENABLED
**Before**: Had to use `--no-verify` to commit
**After**: All checks pass cleanly!
### 🎉 Technical Debt REMOVED
**Before**: Pre-existing error blocking all contributors
**After**: Clean codebase for everyone!
### 🎉 Platform Support VERIFIED
**Before**: Assumed SIGPIPE was optional
**After**: Confirmed it's CRITICAL for Unix/Linux MCP servers
---
## Testing Coverage
### Current Tests ✅
1. **test_setup_signal_handlers_all_platforms** - SIGPIPE available (Unix/Linux)
2. **test_setup_signal_handlers_no_sigpipe** - SIGPIPE not available (Windows)
3. **test_signal_handler_function** - Handler sets shutdown event correctly
All tests passing with new `hasattr()` implementation!
---
## References
1. [MCP Specification - Transports](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)
2. [MCP Lifecycle & Graceful Shutdown](https://modelcontextprotocol.io/specification/2024-11-05/basic/lifecycle)
3. [SIGPIPE Best Practices for Servers](https://blog.erratasec.com/2018/10/tcpip-sockets-and-sigpipe.html)
4. [Handling SIGPIPE Properly (Stack Overflow)](https://stackoverflow.com/questions/108183/how-to-prevent-sigpipes-or-handle-them-properly)
5. [Building MCP Servers with stdio](https://dev.to/elsayed85/building-model-context-protocol-mcp-servers-with-stdio-a-complete-guide-513k)
---
## Conclusion
**Silent debug logging for missing SIGPIPE is the RIGHT choice:**
✅ Windows doesn't need SIGPIPE (different error handling)
✅ Not an error or warning (expected platform difference)
✅ Debug visibility for developers
✅ No actionable information for users
✅ Follows Python logging best practices
**The implementation is CORRECT and CRITICAL for production MCP servers!**