forked from stickerdaniel/linkedin-mcp-server
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathrun_linkedin_mcp_resilient.sh
More file actions
executable file
·164 lines (138 loc) · 5.09 KB
/
run_linkedin_mcp_resilient.sh
File metadata and controls
executable file
·164 lines (138 loc) · 5.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/bin/bash
# LinkedIn MCP Server Resilient Launch Script for Claude Desktop
# Auto-restarts the server on crashes (like BrokenPipeError from FastMCP stdio transport)
# Provides detailed logging and intelligent restart behavior
#
# IMPORTANT: MCP Protocol Requirements
# - stdout: MUST contain ONLY MCP protocol messages (JSON-RPC)
# - stderr: Used for logs, debug output, and non-protocol messages
# - Never redirect stderr to stdout (no 2>&1) or it will break Claude Desktop
# Change to the script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# Configuration
MAX_RESTARTS=10
RESTART_DELAY=2
SUCCESS_THRESHOLD=60 # Seconds - reset counter after successful run
LOG_FILE="/home/demian/.config/Claude/logs/linkedin_mcp_resilient.log"
# Check if .env file exists and load it
if [[ -f ".env" ]]; then
echo "Loading environment from .env file..." >&2
set -a # automatically export all variables
source .env
set +a
fi
# Default arguments for Claude Desktop
DEFAULT_ARGS=""
# If no arguments provided, use stdio transport (default for Claude Desktop)
if [[ $# -eq 0 ]]; then
DEFAULT_ARGS="--transport stdio"
fi
# Logging function
log_message() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" >&2
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
}
# Function to analyze exit code and determine crash reason
analyze_crash() {
local exit_code="$1"
local run_duration="$2"
case $exit_code in
32)
return "BrokenPipeError (FastMCP stdio transport issue)"
;;
1)
if [[ $run_duration -lt 5 ]]; then
return "Quick failure - likely authentication or configuration issue"
else
return "Runtime error after startup"
fi
;;
127)
return "Command not found - check uv installation and path"
;;
*)
return "Unknown error (exit code: $exit_code)"
;;
esac
}
# Function to run the server with monitoring
run_server() {
local start_time=$(date +%s)
log_message "INFO" "Starting LinkedIn MCP Server (resilient mode) with args: $DEFAULT_ARGS $*"
# Run server - let stdout pass through cleanly for MCP protocol
# stderr will be visible in terminal/logs but won't interfere with MCP
uv run -m linkedin_mcp_server $DEFAULT_ARGS "$@"
local exit_code=$?
local end_time=$(date +%s)
local duration=$((end_time - start_time))
log_message "INFO" "Server exited with code $exit_code after ${duration}s"
# Analyze the crash if it wasn't a clean exit
if [[ $exit_code -ne 0 && $exit_code -ne 130 && $exit_code -ne 143 ]]; then
local crash_reason=$(analyze_crash $exit_code $duration)
log_message "WARN" "Crash analysis: $crash_reason"
fi
return $exit_code
}
# Cleanup function
cleanup() {
log_message "INFO" "Resilient launcher exiting (received signal or max attempts reached)"
exit $1
}
# Set up signal handlers
trap 'cleanup 0' INT TERM
# Initialize variables
RESTART_COUNT=0
LAST_START_TIME=0
log_message "INFO" "LinkedIn MCP Resilient Launcher started (max restarts: $MAX_RESTARTS)"
while true; do
START_TIME=$(date +%s)
run_server "$@"
EXIT_CODE=$?
END_TIME=$(date +%s)
RUN_DURATION=$((END_TIME - START_TIME))
# If server exited cleanly (code 0), exit the wrapper
if [[ $EXIT_CODE -eq 0 ]]; then
log_message "INFO" "Server exited cleanly"
exit 0
fi
# If interrupted by user (SIGINT/SIGTERM), exit
if [[ $EXIT_CODE -eq 130 ]] || [[ $EXIT_CODE -eq 143 ]]; then
log_message "INFO" "Server stopped by user (exit code: $EXIT_CODE)"
exit $EXIT_CODE
fi
# Reset restart counter if server ran successfully for more than threshold
if [[ $RUN_DURATION -gt $SUCCESS_THRESHOLD ]]; then
if [[ $RESTART_COUNT -gt 0 ]]; then
log_message "INFO" "Server ran successfully for ${RUN_DURATION}s, resetting restart counter"
RESTART_COUNT=0
fi
fi
# Increment restart counter
RESTART_COUNT=$((RESTART_COUNT + 1))
# Check if we've exceeded max restarts
if [[ $RESTART_COUNT -ge $MAX_RESTARTS ]]; then
log_message "ERROR" "Maximum restart attempts ($MAX_RESTARTS) reached. Giving up."
exit 1
fi
# Special handling for known issues
if [[ $EXIT_CODE -eq 32 ]]; then
log_message "WARN" "BrokenPipeError detected - this is expected when Claude Desktop disconnects"
elif [[ $RUN_DURATION -lt 5 ]]; then
log_message "WARN" "Quick failure detected - possible configuration issue"
# Increase delay for quick failures to avoid rapid restart loops
RESTART_DELAY=$((RESTART_DELAY * 2))
if [[ $RESTART_DELAY -gt 30 ]]; then
RESTART_DELAY=30
fi
else
# Reset delay for other types of failures
RESTART_DELAY=2
fi
# Log the restart
log_message "INFO" "Restarting server (attempt $RESTART_COUNT/$MAX_RESTARTS, delay: ${RESTART_DELAY}s)"
sleep $RESTART_DELAY
done