claudepod 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/config/claude/mcp.json +77 -0
- package/.devcontainer/config/claude/mcp.json.backup +77 -0
- package/.devcontainer/config/claude/mcp.json.template +118 -0
- package/.devcontainer/config/claude/output-styles/strict-development.md +158 -0
- package/.devcontainer/config/claude/settings.json +4 -173
- package/.devcontainer/config/claude/system-prompt.md +3 -0
- package/.devcontainer/config/searxng/ods_config.json +16 -0
- package/.devcontainer/config/searxng/searxng_env_template +71 -0
- package/.devcontainer/config/serena/serena_config.yml +72 -0
- package/.devcontainer/config/taskmaster/config.json +37 -0
- package/.devcontainer/ods_config.json +21 -0
- package/.devcontainer/post-create.sh +832 -155
- package/.devcontainer/post-start.sh +413 -187
- package/.devcontainer/sanitize-system-prompt.sh +31 -0
- package/.devcontainer/scripts/config/claude-core.sh +210 -0
- package/.devcontainer/scripts/config/searxng.sh +143 -0
- package/.devcontainer/scripts/config/serena.sh +47 -0
- package/.devcontainer/scripts/config/taskmaster.sh +41 -0
- package/.devcontainer/scripts/generate-mcp-config.js +205 -0
- package/.devcontainer/scripts/install/claude-code.sh +112 -0
- package/.devcontainer/scripts/shell/zsh-config.sh +271 -0
- package/.devcontainer/scripts/utils.sh +44 -0
- package/.devcontainer/setup-zsh.sh +51 -202
- package/README.md +166 -117
- package/package.json +16 -6
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Sanitize system prompt markdown for Claude CLI usage
|
|
3
|
+
# This script converts a markdown file to a format suitable for --append-system-prompt
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
if [ $# -eq 0 ]; then
|
|
8
|
+
echo "Usage: $0 <markdown-file>"
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
MARKDOWN_FILE="$1"
|
|
13
|
+
|
|
14
|
+
if [ ! -f "$MARKDOWN_FILE" ]; then
|
|
15
|
+
echo "Error: File '$MARKDOWN_FILE' not found"
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Read the markdown file and perform sanitization
|
|
20
|
+
cat "$MARKDOWN_FILE" | \
|
|
21
|
+
# Remove leading/trailing whitespace from each line
|
|
22
|
+
sed 's/^[[:space:]]*//; s/[[:space:]]*$//' | \
|
|
23
|
+
# Escape double quotes
|
|
24
|
+
sed 's/"/\\"/g' | \
|
|
25
|
+
# Escape backslashes
|
|
26
|
+
sed 's/\\/\\\\/g' | \
|
|
27
|
+
# Join all lines with literal \n
|
|
28
|
+
tr '\n' '\001' | \
|
|
29
|
+
sed 's/\001/\\n/g' | \
|
|
30
|
+
# Remove any trailing \n
|
|
31
|
+
sed 's/\\n$//'
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Claude Core Configuration Module
|
|
4
|
+
# Handles Claude directory creation and core configuration files
|
|
5
|
+
|
|
6
|
+
setup_claude_core_config() {
|
|
7
|
+
echo "🔧 Setting up Claude core configuration..."
|
|
8
|
+
|
|
9
|
+
# Claude Code uses multiple possible config locations
|
|
10
|
+
local claude_dirs=(
|
|
11
|
+
"/home/node/.claude"
|
|
12
|
+
"/home/node/.config/claude"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
for dir in "${claude_dirs[@]}"; do
|
|
16
|
+
if [ ! -d "$dir" ]; then
|
|
17
|
+
mkdir -p "$dir"
|
|
18
|
+
echo "📁 Created configuration directory: $dir"
|
|
19
|
+
fi
|
|
20
|
+
# Set proper permissions
|
|
21
|
+
chown -R node:node "$dir"
|
|
22
|
+
chmod -R 700 "$dir"
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
# Copy optimized settings.json from devcontainer config
|
|
26
|
+
setup_claude_settings
|
|
27
|
+
|
|
28
|
+
# Copy MCP configuration
|
|
29
|
+
setup_claude_mcp_config
|
|
30
|
+
|
|
31
|
+
# Copy system prompt
|
|
32
|
+
setup_claude_system_prompt
|
|
33
|
+
|
|
34
|
+
# Copy output styles
|
|
35
|
+
setup_claude_output_styles
|
|
36
|
+
|
|
37
|
+
echo "📋 Claude core configuration processing completed"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setup_claude_settings() {
|
|
41
|
+
local settings_source="/workspace/.devcontainer/config/claude/settings.json"
|
|
42
|
+
local settings_target_home="/home/node/.claude/settings.json"
|
|
43
|
+
local settings_target_workspace="/workspace/.claude/settings.json"
|
|
44
|
+
|
|
45
|
+
if [ ! -f "$settings_source" ]; then
|
|
46
|
+
echo "⚠️ Claude settings template not found, using defaults"
|
|
47
|
+
return 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Check if we should override existing files or only create if missing
|
|
51
|
+
local copy_settings=false
|
|
52
|
+
if [ "${OVERRIDE_CLAUDE_SETTINGS:-false}" = "true" ]; then
|
|
53
|
+
echo "📝 OVERRIDE_CLAUDE_SETTINGS=true: Forcing overwrite of Claude settings"
|
|
54
|
+
copy_settings=true
|
|
55
|
+
elif [ ! -f "$settings_target_home" ] && [ ! -f "$settings_target_workspace" ]; then
|
|
56
|
+
echo "📝 Creating Claude settings (no existing files found)"
|
|
57
|
+
copy_settings=true
|
|
58
|
+
else
|
|
59
|
+
echo "📁 Preserving existing Claude settings (set OVERRIDE_CLAUDE_SETTINGS=true to overwrite)"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if [ "$copy_settings" = "true" ]; then
|
|
63
|
+
# Create backups of existing configurations
|
|
64
|
+
create_config_backup "$settings_target_home"
|
|
65
|
+
create_config_backup "$settings_target_workspace"
|
|
66
|
+
|
|
67
|
+
# Copy to home directory (will be bind-mounted to workspace)
|
|
68
|
+
cp "$settings_source" "$settings_target_home"
|
|
69
|
+
chown node:node "$settings_target_home"
|
|
70
|
+
chmod 600 "$settings_target_home"
|
|
71
|
+
|
|
72
|
+
# Also copy directly to workspace for redundancy
|
|
73
|
+
cp "$settings_source" "$settings_target_workspace"
|
|
74
|
+
chown node:node "$settings_target_workspace"
|
|
75
|
+
chmod 600 "$settings_target_workspace"
|
|
76
|
+
|
|
77
|
+
echo "📋 Claude settings configured"
|
|
78
|
+
fi
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
setup_claude_mcp_config() {
|
|
82
|
+
local mcp_source="/workspace/.devcontainer/config/claude/mcp.json"
|
|
83
|
+
local mcp_target_home="/home/node/.claude/mcp.json"
|
|
84
|
+
local mcp_target_workspace="/workspace/.claude/mcp.json"
|
|
85
|
+
|
|
86
|
+
if [ ! -f "$mcp_source" ]; then
|
|
87
|
+
echo "📝 MCP configuration template not found, will be generated in post-start phase"
|
|
88
|
+
return 0
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
local copy_mcp=false
|
|
92
|
+
if [ "${OVERRIDE_CLAUDE_MCP:-false}" = "true" ]; then
|
|
93
|
+
echo "📝 OVERRIDE_CLAUDE_MCP=true: Forcing overwrite of Claude MCP configuration"
|
|
94
|
+
copy_mcp=true
|
|
95
|
+
elif [ ! -f "$mcp_target_home" ] && [ ! -f "$mcp_target_workspace" ]; then
|
|
96
|
+
echo "📝 Creating Claude MCP configuration (no existing files found)"
|
|
97
|
+
copy_mcp=true
|
|
98
|
+
else
|
|
99
|
+
echo "📁 Preserving existing Claude MCP configuration (set OVERRIDE_CLAUDE_MCP=true to overwrite)"
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
if [ "$copy_mcp" = "true" ]; then
|
|
103
|
+
# Create backups of existing MCP configurations
|
|
104
|
+
create_config_backup "$mcp_target_home"
|
|
105
|
+
create_config_backup "$mcp_target_workspace"
|
|
106
|
+
|
|
107
|
+
cp "$mcp_source" "$mcp_target_home"
|
|
108
|
+
cp "$mcp_source" "$mcp_target_workspace"
|
|
109
|
+
chown node:node "$mcp_target_home" "$mcp_target_workspace"
|
|
110
|
+
chmod 600 "$mcp_target_home" "$mcp_target_workspace"
|
|
111
|
+
|
|
112
|
+
echo "📋 Claude MCP configuration set up"
|
|
113
|
+
fi
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
setup_claude_system_prompt() {
|
|
117
|
+
local prompt_source="/workspace/.devcontainer/config/claude/system-prompt.md"
|
|
118
|
+
local prompt_target_home="/home/node/.claude/system-prompt.md"
|
|
119
|
+
local prompt_target_workspace="/workspace/.claude/system-prompt.md"
|
|
120
|
+
|
|
121
|
+
if [ ! -f "$prompt_source" ]; then
|
|
122
|
+
echo "📝 System prompt template not found, skipping"
|
|
123
|
+
return 0
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
local copy_prompt=false
|
|
127
|
+
if [ "${OVERRIDE_CLAUDE_SYSTEM_PROMPT:-false}" = "true" ]; then
|
|
128
|
+
echo "📝 OVERRIDE_CLAUDE_SYSTEM_PROMPT=true: Forcing overwrite of Claude system prompt"
|
|
129
|
+
copy_prompt=true
|
|
130
|
+
elif [ ! -f "$prompt_target_home" ] && [ ! -f "$prompt_target_workspace" ]; then
|
|
131
|
+
echo "📝 Creating Claude system prompt (no existing files found)"
|
|
132
|
+
copy_prompt=true
|
|
133
|
+
else
|
|
134
|
+
echo "📁 Preserving existing Claude system prompt (set OVERRIDE_CLAUDE_SYSTEM_PROMPT=true to overwrite)"
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
if [ "$copy_prompt" = "true" ]; then
|
|
138
|
+
# Create backups of existing system prompt configurations
|
|
139
|
+
create_config_backup "$prompt_target_home"
|
|
140
|
+
create_config_backup "$prompt_target_workspace"
|
|
141
|
+
|
|
142
|
+
cp "$prompt_source" "$prompt_target_home"
|
|
143
|
+
cp "$prompt_source" "$prompt_target_workspace"
|
|
144
|
+
chown node:node "$prompt_target_home" "$prompt_target_workspace"
|
|
145
|
+
chmod 600 "$prompt_target_home" "$prompt_target_workspace"
|
|
146
|
+
|
|
147
|
+
echo "📋 Claude system prompt configured"
|
|
148
|
+
fi
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
setup_claude_output_styles() {
|
|
152
|
+
local styles_source="/workspace/.devcontainer/config/claude/output-styles"
|
|
153
|
+
local styles_target_home="/home/node/.claude/output-styles"
|
|
154
|
+
local styles_target_workspace="/workspace/.claude/output-styles"
|
|
155
|
+
|
|
156
|
+
if [ ! -d "$styles_source" ]; then
|
|
157
|
+
echo "📝 Output styles directory not found, skipping"
|
|
158
|
+
return 0
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
local copy_styles=false
|
|
162
|
+
if [ "${OVERRIDE_CLAUDE_OUTPUT_STYLES:-false}" = "true" ]; then
|
|
163
|
+
echo "📝 OVERRIDE_CLAUDE_OUTPUT_STYLES=true: Forcing overwrite of Claude output styles"
|
|
164
|
+
copy_styles=true
|
|
165
|
+
elif [ ! -d "$styles_target_home" ] && [ ! -d "$styles_target_workspace" ]; then
|
|
166
|
+
echo "📝 Creating Claude output styles (no existing directories found)"
|
|
167
|
+
copy_styles=true
|
|
168
|
+
else
|
|
169
|
+
echo "📁 Preserving existing Claude output styles (set OVERRIDE_CLAUDE_OUTPUT_STYLES=true to overwrite)"
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
if [ "$copy_styles" = "true" ]; then
|
|
173
|
+
# Create backups of existing output styles directories
|
|
174
|
+
if [ -d "$styles_target_home" ]; then
|
|
175
|
+
local backup_dir="/workspace/.devcontainer/config/backups"
|
|
176
|
+
mkdir -p "$backup_dir"
|
|
177
|
+
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
|
178
|
+
local backup_home="${backup_dir}/home_node_.claude_output-styles.${timestamp}.backup"
|
|
179
|
+
cp -r "$styles_target_home" "$backup_home"
|
|
180
|
+
chown -R node:node "$backup_home"
|
|
181
|
+
chmod -R 600 "$backup_home"
|
|
182
|
+
echo "📦 Created backup: $backup_home"
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
if [ -d "$styles_target_workspace" ]; then
|
|
186
|
+
local backup_dir="/workspace/.devcontainer/config/backups"
|
|
187
|
+
mkdir -p "$backup_dir"
|
|
188
|
+
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
|
189
|
+
local backup_workspace="${backup_dir}/workspace_.claude_output-styles.${timestamp}.backup"
|
|
190
|
+
cp -r "$styles_target_workspace" "$backup_workspace"
|
|
191
|
+
chown -R node:node "$backup_workspace"
|
|
192
|
+
chmod -R 600 "$backup_workspace"
|
|
193
|
+
echo "📦 Created backup: $backup_workspace"
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# Copy to home directory (will be bind-mounted to workspace)
|
|
197
|
+
mkdir -p "$styles_target_home"
|
|
198
|
+
cp -r "$styles_source"/* "$styles_target_home"/
|
|
199
|
+
chown -R node:node "$styles_target_home"
|
|
200
|
+
chmod -R 600 "$styles_target_home"
|
|
201
|
+
|
|
202
|
+
# Also copy directly to workspace for redundancy
|
|
203
|
+
mkdir -p "$styles_target_workspace"
|
|
204
|
+
cp -r "$styles_source"/* "$styles_target_workspace"/
|
|
205
|
+
chown -R node:node "$styles_target_workspace"
|
|
206
|
+
chmod -R 600 "$styles_target_workspace"
|
|
207
|
+
|
|
208
|
+
echo "📋 Claude output styles configured"
|
|
209
|
+
fi
|
|
210
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# SearXNG MCP Enhanced Configuration Module
|
|
4
|
+
# Handles MCP SearXNG Enhanced installation and configuration setup
|
|
5
|
+
|
|
6
|
+
install_searxng_mcp() {
|
|
7
|
+
echo "🔧 Installing MCP SearXNG Enhanced..."
|
|
8
|
+
|
|
9
|
+
# Define installation paths
|
|
10
|
+
local mcp_install_dir="/usr/local/mcp-servers/searxng"
|
|
11
|
+
local temp_dir="/tmp/mcp-searxng-enhanced"
|
|
12
|
+
local repo_url="https://github.com/OvertliDS/mcp-searxng-enhanced"
|
|
13
|
+
|
|
14
|
+
# Create installation directory
|
|
15
|
+
sudo mkdir -p "$mcp_install_dir"
|
|
16
|
+
|
|
17
|
+
# Clone repository to temporary location
|
|
18
|
+
echo "📥 Cloning MCP SearXNG Enhanced repository..."
|
|
19
|
+
if [ -d "$temp_dir" ]; then
|
|
20
|
+
rm -rf "$temp_dir"
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
git clone "$repo_url" "$temp_dir" 2>/dev/null || {
|
|
24
|
+
echo "❌ Failed to clone repository from $repo_url"
|
|
25
|
+
return 1
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Install Python dependencies using uv with the correct Python version
|
|
29
|
+
echo "📦 Installing Python dependencies..."
|
|
30
|
+
sudo /home/node/.local/bin/uv pip install --python /usr/local/python/current/bin/python3 --system -r "$temp_dir/requirements.txt" || {
|
|
31
|
+
echo "❌ Failed to install Python dependencies"
|
|
32
|
+
return 1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# Copy MCP server files
|
|
36
|
+
echo "📋 Copying MCP server files..."
|
|
37
|
+
sudo cp "$temp_dir/mcp_server.py" "$mcp_install_dir/"
|
|
38
|
+
sudo chmod +x "$mcp_install_dir/mcp_server.py"
|
|
39
|
+
|
|
40
|
+
# Copy additional files if they exist
|
|
41
|
+
if [ -f "$temp_dir/README.md" ]; then
|
|
42
|
+
sudo cp "$temp_dir/README.md" "$mcp_install_dir/"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Clean up temporary directory
|
|
46
|
+
rm -rf "$temp_dir"
|
|
47
|
+
|
|
48
|
+
echo "✅ MCP SearXNG Enhanced installed successfully"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setup_searxng_config() {
|
|
52
|
+
echo "🔧 Setting up SearXNG configuration..."
|
|
53
|
+
|
|
54
|
+
local config_source="/workspace/.devcontainer/config/searxng/ods_config.json"
|
|
55
|
+
local config_target="/home/node/.claude/ods_config.json"
|
|
56
|
+
|
|
57
|
+
if [ ! -f "$config_source" ]; then
|
|
58
|
+
echo "⚠️ SearXNG configuration template not found, using defaults"
|
|
59
|
+
return 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# Ensure Claude directory exists
|
|
63
|
+
mkdir -p "/home/node/.claude"
|
|
64
|
+
|
|
65
|
+
local copy_config=false
|
|
66
|
+
if [ "${OVERRIDE_SEARXNG_CONFIG:-false}" = "true" ]; then
|
|
67
|
+
echo "📝 OVERRIDE_SEARXNG_CONFIG=true: Forcing overwrite of SearXNG configuration"
|
|
68
|
+
copy_config=true
|
|
69
|
+
elif [ ! -f "$config_target" ]; then
|
|
70
|
+
echo "📝 Creating SearXNG configuration (no existing file found)"
|
|
71
|
+
copy_config=true
|
|
72
|
+
else
|
|
73
|
+
echo "📁 Preserving existing SearXNG configuration (set OVERRIDE_SEARXNG_CONFIG=true to overwrite)"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
if [ "$copy_config" = "true" ]; then
|
|
77
|
+
# Copy configuration file
|
|
78
|
+
cp "$config_source" "$config_target"
|
|
79
|
+
chown node:node "$config_target"
|
|
80
|
+
chmod 600 "$config_target"
|
|
81
|
+
|
|
82
|
+
echo "📋 Copied optimized SearXNG configuration"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
echo "✅ SearXNG configuration ready"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
validate_searxng_connection() {
|
|
89
|
+
echo "🔍 Validating SearXNG connection..."
|
|
90
|
+
|
|
91
|
+
local searxng_url="${SEARXNG_ENGINE_API_BASE_URL:-http://localhost:8080/search}"
|
|
92
|
+
|
|
93
|
+
# Test connection to SearXNG instance
|
|
94
|
+
if command -v curl >/dev/null 2>&1; then
|
|
95
|
+
if curl -s --connect-timeout 10 --max-time 30 "${searxng_url%/search}" >/dev/null; then
|
|
96
|
+
echo "✅ SearXNG instance at $searxng_url is accessible"
|
|
97
|
+
return 0
|
|
98
|
+
else
|
|
99
|
+
echo "⚠️ Warning: SearXNG instance at $searxng_url may not be accessible"
|
|
100
|
+
echo " MCP server will still be configured but may fail during runtime"
|
|
101
|
+
return 1
|
|
102
|
+
fi
|
|
103
|
+
else
|
|
104
|
+
echo "⚠️ curl not found, skipping connection validation"
|
|
105
|
+
return 1
|
|
106
|
+
fi
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
setup_searxng_environment() {
|
|
110
|
+
echo "🌍 Setting up SearXNG environment variables..."
|
|
111
|
+
|
|
112
|
+
# Set default environment variables if not already set
|
|
113
|
+
export ODS_CONFIG_PATH="${ODS_CONFIG_PATH:-/home/node/.claude/ods_config.json}"
|
|
114
|
+
export SEARXNG_ENGINE_API_BASE_URL="${SEARXNG_ENGINE_API_BASE_URL:-http://localhost:8080/search}"
|
|
115
|
+
export DESIRED_TIMEZONE="${DESIRED_TIMEZONE:-America/New_York}"
|
|
116
|
+
|
|
117
|
+
echo "📋 Environment variables configured:"
|
|
118
|
+
echo " ODS_CONFIG_PATH=$ODS_CONFIG_PATH"
|
|
119
|
+
echo " SEARXNG_ENGINE_API_BASE_URL=$SEARXNG_ENGINE_API_BASE_URL"
|
|
120
|
+
echo " DESIRED_TIMEZONE=$DESIRED_TIMEZONE"
|
|
121
|
+
|
|
122
|
+
echo "✅ SearXNG environment ready"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
# Main setup function
|
|
126
|
+
setup_searxng() {
|
|
127
|
+
if [ "${ENABLE_SEARXNG_ENHANCED_MCP:-true}" != "true" ]; then
|
|
128
|
+
echo "⏭️ SearXNG Enhanced MCP is disabled (set ENABLE_SEARXNG_ENHANCED_MCP=true to enable)"
|
|
129
|
+
return 0
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
echo "🔄 Setting up MCP SearXNG Enhanced..."
|
|
133
|
+
|
|
134
|
+
# Run installation and configuration steps
|
|
135
|
+
install_searxng_mcp || return 1
|
|
136
|
+
setup_searxng_config || return 1
|
|
137
|
+
setup_searxng_environment
|
|
138
|
+
validate_searxng_connection || echo " (Connection validation failed, but continuing with setup)"
|
|
139
|
+
|
|
140
|
+
echo "✅ MCP SearXNG Enhanced setup complete"
|
|
141
|
+
|
|
142
|
+
return 0
|
|
143
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Serena Configuration Module
|
|
4
|
+
# Handles Serena MCP server configuration setup
|
|
5
|
+
|
|
6
|
+
setup_serena_config() {
|
|
7
|
+
echo "🔧 Setting up Serena configuration..."
|
|
8
|
+
|
|
9
|
+
local serena_source="/workspace/.devcontainer/config/serena/serena_config.yml"
|
|
10
|
+
local serena_target_home="/home/node/.serena/serena_config.yml"
|
|
11
|
+
local serena_target_workspace="/workspace/.serena/serena_config.yml"
|
|
12
|
+
|
|
13
|
+
if [ ! -f "$serena_source" ]; then
|
|
14
|
+
echo "⚠️ Serena configuration template not found, using defaults"
|
|
15
|
+
return 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Ensure Serena directories exist
|
|
19
|
+
mkdir -p "/home/node/.serena" "/workspace/.serena"
|
|
20
|
+
|
|
21
|
+
local copy_serena=false
|
|
22
|
+
if [ "${OVERRIDE_SERENA_CONFIG:-false}" = "true" ]; then
|
|
23
|
+
echo "📝 OVERRIDE_SERENA_CONFIG=true: Forcing overwrite of Serena configuration"
|
|
24
|
+
copy_serena=true
|
|
25
|
+
elif [ ! -f "$serena_target_home" ] && [ ! -f "$serena_target_workspace" ]; then
|
|
26
|
+
echo "📝 Creating Serena configuration (no existing files found)"
|
|
27
|
+
copy_serena=true
|
|
28
|
+
else
|
|
29
|
+
echo "📁 Preserving existing Serena configuration (set OVERRIDE_SERENA_CONFIG=true to overwrite)"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
if [ "$copy_serena" = "true" ]; then
|
|
33
|
+
# Copy to home directory (will be bind-mounted to workspace)
|
|
34
|
+
cp "$serena_source" "$serena_target_home"
|
|
35
|
+
chown node:node "$serena_target_home"
|
|
36
|
+
chmod 600 "$serena_target_home"
|
|
37
|
+
|
|
38
|
+
# Also copy directly to workspace for redundancy
|
|
39
|
+
cp "$serena_source" "$serena_target_workspace"
|
|
40
|
+
chown node:node "$serena_target_workspace"
|
|
41
|
+
chmod 600 "$serena_target_workspace"
|
|
42
|
+
|
|
43
|
+
echo "📋 Copied optimized Serena configuration"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
echo "✅ Serena configuration ready"
|
|
47
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# TaskMaster Configuration Module
|
|
4
|
+
# Handles TaskMaster AI configuration setup
|
|
5
|
+
|
|
6
|
+
setup_taskmaster_config() {
|
|
7
|
+
echo "🔧 Setting up TaskMaster configuration..."
|
|
8
|
+
|
|
9
|
+
local taskmaster_source="/workspace/.devcontainer/config/taskmaster/config.json"
|
|
10
|
+
local taskmaster_target_workspace="/workspace/.taskmaster/config.json"
|
|
11
|
+
|
|
12
|
+
if [ ! -f "$taskmaster_source" ]; then
|
|
13
|
+
echo "⚠️ TaskMaster configuration template not found, using defaults"
|
|
14
|
+
return 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# Ensure TaskMaster directory exists
|
|
18
|
+
mkdir -p "/workspace/.taskmaster"
|
|
19
|
+
|
|
20
|
+
local copy_taskmaster=false
|
|
21
|
+
if [ "${OVERRIDE_TASKMASTER_CONFIG:-false}" = "true" ]; then
|
|
22
|
+
echo "📝 OVERRIDE_TASKMASTER_CONFIG=true: Forcing overwrite of TaskMaster configuration"
|
|
23
|
+
copy_taskmaster=true
|
|
24
|
+
elif [ ! -f "$taskmaster_target_workspace" ]; then
|
|
25
|
+
echo "📝 Creating TaskMaster configuration (no existing files found)"
|
|
26
|
+
copy_taskmaster=true
|
|
27
|
+
else
|
|
28
|
+
echo "📁 Preserving existing TaskMaster configuration (set OVERRIDE_TASKMASTER_CONFIG=true to overwrite)"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
if [ "$copy_taskmaster" = "true" ]; then
|
|
32
|
+
# Copy to workspace
|
|
33
|
+
cp "$taskmaster_source" "$taskmaster_target_workspace"
|
|
34
|
+
chown node:node "$taskmaster_target_workspace"
|
|
35
|
+
chmod 600 "$taskmaster_target_workspace"
|
|
36
|
+
|
|
37
|
+
echo "📋 Copied optimized TaskMaster configuration"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
echo "✅ TaskMaster configuration ready"
|
|
41
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate MCP configuration from template based on environment variables
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
// Load environment variables from .env file (industry standard approach)
|
|
10
|
+
const ENV_FILE_PATH = '/workspace/.devcontainer/.env';
|
|
11
|
+
try {
|
|
12
|
+
require('dotenv').config({ path: ENV_FILE_PATH, override: true });
|
|
13
|
+
console.log(`[MCP Config Generator] Loaded environment from ${ENV_FILE_PATH}`);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
console.log(`[MCP Config Generator] dotenv not available, using process.env: ${error.message}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const CONFIG_DIR = '/workspace/.devcontainer/config/claude';
|
|
19
|
+
const TEMPLATE_PATH = path.join(CONFIG_DIR, 'mcp.json.template');
|
|
20
|
+
const OUTPUT_PATH = path.join(CONFIG_DIR, 'mcp.json');
|
|
21
|
+
const BACKUP_PATH = path.join(CONFIG_DIR, 'mcp.json.backup');
|
|
22
|
+
|
|
23
|
+
function log(message) {
|
|
24
|
+
console.log(`[MCP Config Generator] ${message}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseBoolean(value, defaultValue = false) {
|
|
28
|
+
if (!value) return defaultValue;
|
|
29
|
+
const lowerValue = value.toLowerCase().trim();
|
|
30
|
+
return lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function expandEnvironmentVariables(text, env) {
|
|
34
|
+
return text.replace(/\$\{([^}:-]+)(:-([^}]*))?\}/g, (match, varName, _, defaultValue) => {
|
|
35
|
+
const envValue = env[varName];
|
|
36
|
+
if (envValue !== undefined) {
|
|
37
|
+
return envValue;
|
|
38
|
+
}
|
|
39
|
+
return defaultValue || '';
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function hasRequiredApiKeys(requires, env) {
|
|
44
|
+
if (!requires || requires.length === 0) return true;
|
|
45
|
+
|
|
46
|
+
return requires.every(key => {
|
|
47
|
+
const value = env[key];
|
|
48
|
+
return value && value.trim() !== '';
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function validateMcpConfiguration(config) {
|
|
53
|
+
try {
|
|
54
|
+
// Validate top-level structure
|
|
55
|
+
if (!config || typeof config !== 'object') {
|
|
56
|
+
log('Validation Error: Configuration must be an object');
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!config.mcpServers || typeof config.mcpServers !== 'object') {
|
|
61
|
+
log('Validation Error: Configuration must have mcpServers object');
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Validate each server configuration
|
|
66
|
+
for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) {
|
|
67
|
+
if (!validateServerConfig(serverName, serverConfig)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
log('MCP configuration validation passed');
|
|
73
|
+
return true;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
log(`Validation Error: ${error.message}`);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function validateServerConfig(serverName, config) {
|
|
81
|
+
// Check for required fields based on server type
|
|
82
|
+
if (config.command) {
|
|
83
|
+
// Command-based server
|
|
84
|
+
if (!Array.isArray(config.args)) {
|
|
85
|
+
log(`Validation Error: Server ${serverName} with command must have args array`);
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
} else if (config.type === 'http') {
|
|
89
|
+
// HTTP-based server
|
|
90
|
+
if (!config.url || typeof config.url !== 'string') {
|
|
91
|
+
log(`Validation Error: HTTP server ${serverName} must have valid url`);
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Validate URL format
|
|
96
|
+
try {
|
|
97
|
+
new URL(config.url);
|
|
98
|
+
} catch (urlError) {
|
|
99
|
+
log(`Validation Error: Server ${serverName} has invalid URL: ${config.url}`);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
log(`Validation Error: Server ${serverName} must have either 'command' or 'type' field`);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function generateMcpConfig() {
|
|
111
|
+
try {
|
|
112
|
+
// Check if template exists
|
|
113
|
+
if (!fs.existsSync(TEMPLATE_PATH)) {
|
|
114
|
+
log(`Template file not found: ${TEMPLATE_PATH}`);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Read template
|
|
119
|
+
const templateContent = fs.readFileSync(TEMPLATE_PATH, 'utf8');
|
|
120
|
+
let templateData;
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
// First expand environment variables in the template
|
|
124
|
+
const expandedTemplate = expandEnvironmentVariables(templateContent, process.env);
|
|
125
|
+
templateData = JSON.parse(expandedTemplate);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
log(`Failed to parse template: ${error.message}`);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Create backup of existing config if it exists
|
|
132
|
+
if (fs.existsSync(OUTPUT_PATH)) {
|
|
133
|
+
fs.copyFileSync(OUTPUT_PATH, BACKUP_PATH);
|
|
134
|
+
log(`Created backup: ${BACKUP_PATH}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Generate output configuration
|
|
138
|
+
const outputConfig = {
|
|
139
|
+
mcpServers: {}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
let enabledCount = 0;
|
|
143
|
+
let disabledCount = 0;
|
|
144
|
+
|
|
145
|
+
// Process each server
|
|
146
|
+
for (const [serverName, serverData] of Object.entries(templateData.servers)) {
|
|
147
|
+
const enabled = parseBoolean(serverData.enabled, false);
|
|
148
|
+
const hasApiKeys = hasRequiredApiKeys(serverData.requires, process.env);
|
|
149
|
+
|
|
150
|
+
if (enabled && hasApiKeys) {
|
|
151
|
+
// Server is enabled and has required API keys
|
|
152
|
+
// Flatten the config structure - remove the nested "config" wrapper
|
|
153
|
+
outputConfig.mcpServers[serverName] = serverData.config;
|
|
154
|
+
enabledCount++;
|
|
155
|
+
log(`✓ Enabled: ${serverName}`);
|
|
156
|
+
} else {
|
|
157
|
+
// Server is disabled or missing API keys
|
|
158
|
+
disabledCount++;
|
|
159
|
+
if (!enabled) {
|
|
160
|
+
log(`✗ Disabled: ${serverName} (ENABLE_${serverName.toUpperCase().replace(/-/g, '_')}_MCP=false)`);
|
|
161
|
+
} else {
|
|
162
|
+
log(`✗ Disabled: ${serverName} (missing required API keys: ${serverData.requires?.join(', ')})`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Keep environment variables as references, don't expand them
|
|
168
|
+
const finalConfigText = JSON.stringify(outputConfig, null, 2);
|
|
169
|
+
|
|
170
|
+
// Write output
|
|
171
|
+
fs.writeFileSync(OUTPUT_PATH, finalConfigText);
|
|
172
|
+
|
|
173
|
+
// Basic validation - ensure output is valid JSON
|
|
174
|
+
try {
|
|
175
|
+
JSON.parse(finalConfigText);
|
|
176
|
+
} catch (parseError) {
|
|
177
|
+
log(`Error: Generated configuration is not valid JSON: ${parseError.message}`);
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Advanced validation - validate MCP configuration structure
|
|
182
|
+
if (!validateMcpConfiguration(outputConfig)) {
|
|
183
|
+
log(`Error: Generated MCP configuration failed validation`);
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
log(`Configuration generated successfully!`);
|
|
188
|
+
log(`Servers enabled: ${enabledCount}, disabled: ${disabledCount}`);
|
|
189
|
+
log(`Output: ${OUTPUT_PATH}`);
|
|
190
|
+
|
|
191
|
+
return true;
|
|
192
|
+
|
|
193
|
+
} catch (error) {
|
|
194
|
+
log(`Error generating configuration: ${error.message}`);
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Main execution
|
|
200
|
+
if (require.main === module) {
|
|
201
|
+
const success = generateMcpConfig();
|
|
202
|
+
process.exit(success ? 0 : 1);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module.exports = { generateMcpConfig };
|