Comprehensive process management system for MCP remote server with support for:
- ✅ SSE streaming for background commands
- ✅ Process registry tracking (request_id ↔ PID mapping)
- ✅ MCP standard
notifications/cancelled - ✅ Graceful termination (SIGTERM → wait → SIGKILL)
- ✅
terminate_processMCP tool - ✅
list_processesMCP tool - ✅ Docker mode support
File: remote_server_lib/process_registry.py
The ProcessRegistry class maintains bidirectional mappings between MCP request IDs and process IDs:
class ProcessRegistry:
- register(request_id, pid, command) → ProcessInfo
- unregister(request_id/pid) → ProcessInfo
- get_by_request_id(request_id) → ProcessInfo
- get_by_pid(pid) → ProcessInfo
- list_all() → List[ProcessInfo]
- terminate_gracefully(request_id/pid, reason) → DictGraceful Termination Flow:
- Send SIGTERM to process
- Wait up to 30 seconds (configurable via
TERMINATION_TIMEOUT) - If still running, send SIGKILL
- Update registry and clean up
Local Mode (USE_DOCKER=False):
- Process registry runs in
mcp_remote_server.py - Direct process management via OS signals
- SSE streaming supported
Docker Mode (USE_DOCKER=True):
- Process registry runs in backend (
remote_server_lib/api/async_process.py) - Remote MCP server proxies to backend APIs
- SSE streaming not yet supported (falls back to executor)
Terminate a background process by PID (only processes started by this server).
Input Schema:
{
"pid": 12345
}Response:
Process 12345 terminated successfully
Request ID: 550e8400-e29b-41d4-a716-446655440000
Signal: SIGTERM
Reason: Manual termination via terminate_process tool
List all running background processes started by this server.
Input Schema:
{}Response:
Background Processes (2):
PID: 12345
Request ID: 550e8400-e29b-41d4-a716-446655440000
Command: sleep 60
Status: running
Started: 2025-12-13 10:30:15
PID: 12346
Request ID: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
Command: long-running-task.sh
Status: running
Started: 2025-12-13 10:31:20
Implements MCP protocol standard for cancelling in-progress requests.
Client sends:
{
"jsonrpc": "2.0",
"method": "notifications/cancelled",
"params": {
"requestId": "3",
"reason": "User cancelled operation"
}
}Server behavior:
- Looks up PID by request ID in registry
- Terminates process gracefully (SIGTERM → SIGKILL)
- Returns HTTP 204 (no content) per MCP spec
- Streaming response for original request will show termination
When execute_background_linux_shell_command is called (local mode):
1. Process Started Event:
event: process_started
data: {"pid": 12345, "status": "started", "request_id": "3"}
2. Stdout Events (real-time):
event: stdout
data: {"content": "Processing line 1..."}
event: stdout
data: {"content": "Processing line 2..."}
3. Stderr Events (real-time):
event: stderr
data: {"content": "Warning: deprecated feature"}
4. Final MCP Tool Result:
event: message
data: {
"jsonrpc": "2.0",
"id": 3,
"result": {
"content": [
{
"type": "text",
"text": "Processing line 1...\nProcessing line 2...\n\nSTDERR:\nWarning: deprecated feature"
}
],
"isError": false
}
}
# Termination timeout (seconds to wait before SIGKILL)
TERMINATION_TIMEOUT=30 # Default: 30
# Docker mode toggle
USE_DOCKER=False # True for Docker backend, False for local
# MCP server ports
MCP_REMOTE_PORT=8888 # Remote MCP server
MCP_PORT=8181 # Backend API (Docker mode)Terminate process by PID.
Request:
POST /api/async/process/terminate_by_pid/?pid=12345
Response:
{
"success": true,
"pid": 12345,
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"signal": "SIGTERM",
"message": "Process terminated with SIGTERM"
}List all tracked processes.
Response:
{
"processes": [
{
"pid": 12345,
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"command": "sleep 60",
"status": "running",
"started_at": "2025-12-13T10:30:15.123456",
"exit_code": null,
"terminated_at": null,
"termination_signal": null
}
],
"count": 1
}# Terminal 1: Start server (local mode)
USE_DOCKER=False MCP_REMOTE_PORT=8888 python mcp_remote_server.py
# Terminal 2: Run comprehensive test
python test_process_management.py# 1. Start a background process
curl -X POST http://localhost:8888/mcp \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "execute_background_linux_shell_command",
"arguments": {"cmd": "sleep 60"}
}
}'
# 2. List processes
curl -X POST http://localhost:8888/mcp \
-H "Content-Type: application/json" \
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "list_processes",
"arguments": {}
}
}'
# 3. Cancel via notifications/cancelled
curl -X POST http://localhost:8888/mcp \
-H "Content-Type: application/json" \
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
-d '{
"jsonrpc": "2.0",
"method": "notifications/cancelled",
"params": {
"requestId": "1",
"reason": "User requested"
}
}'
# 4. Or terminate by PID
curl -X POST http://localhost:8888/mcp \
-H "Content-Type: application/json" \
-H "Mcp-Session-Id: YOUR_SESSION_ID" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "terminate_process",
"arguments": {"pid": 12345}
}
}'remote_server_lib/process_registry.py- Process registry implementationtest_process_management.py- Comprehensive test suitePROCESS_MANAGEMENT.md- This documentation
-
mcp_remote_server.py:- Added process registry initialization
- Updated
stream_background_command()to register/unregister processes - Added
notifications/cancelledhandler - Implemented
terminate_processandlist_processestools - Added Docker mode support for new tools
-
remote_server_lib/api/async_process.py:- Added process registry to backend
- Updated
run_command()to register processes - Added
/api/async/process/terminate_by_pid/endpoint - Added
/api/async/processes/list/endpoint
- Process Isolation: Only processes started by this MCP server can be terminated
- PID Validation: PIDs are validated against the registry before termination
- Session-based: All operations require valid MCP session
- API Key: Optional API key authentication (set
MCP_API_KEY) - Origin Validation: CORS restricted to localhost
- Docker mode SSE streaming support
- Process output capture history
- Process timeout configuration per-command
- Process grouping/tagging
- Metrics and monitoring
- Persistent registry (survive server restarts)