@rishibhushan/jenkins-mcp-server 1.0.7 → 1.1.1
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/README.md +188 -10
- package/bin/jenkins-mcp.js +249 -17
- package/package.json +1 -1
- package/requirements.txt +1 -0
- package/src/jenkins_mcp_server/__init__.py +19 -11
- package/src/jenkins_mcp_server/__pycache__/__init__.cpython-313.pyc +0 -0
- package/src/jenkins_mcp_server/__pycache__/cache.cpython-313.pyc +0 -0
- package/src/jenkins_mcp_server/__pycache__/config.cpython-313.pyc +0 -0
- package/src/jenkins_mcp_server/__pycache__/jenkins_client.cpython-313.pyc +0 -0
- package/src/jenkins_mcp_server/__pycache__/metrics.cpython-313.pyc +0 -0
- package/src/jenkins_mcp_server/__pycache__/server.cpython-313.pyc +0 -0
- package/src/jenkins_mcp_server/cache.py +310 -0
- package/src/jenkins_mcp_server/config.py +54 -1
- package/src/jenkins_mcp_server/jenkins_client.py +69 -80
- package/src/jenkins_mcp_server/metrics.py +358 -0
- package/src/jenkins_mcp_server/server.py +1015 -108
package/README.md
CHANGED
|
@@ -7,6 +7,28 @@ Designed to work seamlessly with automation clients such as:
|
|
|
7
7
|
- 🖥️ **Claude Desktop** - AI-powered Jenkins automation
|
|
8
8
|
- 🔌 **Any MCP-compatible client** - Universal compatibility
|
|
9
9
|
|
|
10
|
+
## ✨ What's New in v1.1.0
|
|
11
|
+
|
|
12
|
+
### 🚀 Performance Enhancements
|
|
13
|
+
- ⚡ **10x faster** - Client connection caching for repeated operations
|
|
14
|
+
- 📊 **Smart caching** - Job list caching with 30-60s TTL (5-10x improvement)
|
|
15
|
+
- 🎯 **Optimized queries** - Reduced API calls by 33-83%
|
|
16
|
+
|
|
17
|
+
### 🛡️ Reliability Improvements
|
|
18
|
+
- ✅ **86% validation coverage** - Input validation on 18/21 tools
|
|
19
|
+
- ⏱️ **Configurable timeouts** - No more hanging API calls
|
|
20
|
+
- 💬 **Better error messages** - Clear troubleshooting steps
|
|
21
|
+
- 🏥 **Health check tool** - Instant diagnostics
|
|
22
|
+
|
|
23
|
+
### 🎛️ Advanced Features
|
|
24
|
+
- 📦 **Batch operations** - Trigger up to 20 builds at once
|
|
25
|
+
- 📈 **Metrics & telemetry** - Track tool usage and performance
|
|
26
|
+
- 🗂️ **Cache management** - Monitor and control caching
|
|
27
|
+
- 📊 **Console improvements** - Line-based truncation with tail mode
|
|
28
|
+
- 🎨 **Structured logging** - Better debugging with JSON logs
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
10
32
|
## ✨ About codebase
|
|
11
33
|
|
|
12
34
|
- ✅ **Codebase** - cleaner, more maintainable
|
|
@@ -15,6 +37,8 @@ Designed to work seamlessly with automation clients such as:
|
|
|
15
37
|
- ✅ **Cross-platform** - Seamless support for Windows, macOS, and Linux
|
|
16
38
|
- ✅ **Logging** - Professional logging with `--verbose` flag
|
|
17
39
|
- ✅ **Dependency management** - Automatic detection and installation
|
|
40
|
+
- ✅ **Performance** - 10x faster with intelligent caching and optimization
|
|
41
|
+
- ✅ **Reliability** - Comprehensive input validation and error handling
|
|
18
42
|
|
|
19
43
|
---
|
|
20
44
|
|
|
@@ -26,25 +50,26 @@ This project includes:
|
|
|
26
50
|
- 🔄 Automatic virtual environment creation + dependency installation
|
|
27
51
|
- 🌐 Corporate proxy/certificate auto-detection support
|
|
28
52
|
- 🪟 Windows, macOS, and Linux support
|
|
29
|
-
- 🛠️ **
|
|
53
|
+
- 🛠️ **26 Jenkins management tools** (upgraded from 20!)
|
|
30
54
|
|
|
31
55
|
### 🧩 Build Operations
|
|
32
56
|
| Tool Name | Description | Required Fields | Optional Fields |
|
|
33
57
|
|---|---|---|---|
|
|
34
58
|
| `trigger-build` | Trigger a Jenkins job build with optional parameters | `job_name` | `parameters` |
|
|
35
59
|
| `stop-build` | Stop a running Jenkins build | `job_name`, `build_number` | *(none)* |
|
|
60
|
+
| `trigger-multiple-builds` | **NEW!** Trigger builds for multiple jobs at once | `job_names` | `parameters`, `wait_for_start` |
|
|
36
61
|
|
|
37
62
|
### 📊 Job Information
|
|
38
63
|
| Tool Name | Description | Required Fields | Optional Fields |
|
|
39
64
|
|---|---|---|---|
|
|
40
|
-
| `list-jobs` | List all Jenkins jobs with optional filtering | *(none)* | `filter` |
|
|
41
|
-
| `get-job-details` | Get detailed information about a Jenkins job | `job_name` |
|
|
65
|
+
| `list-jobs` | List all Jenkins jobs with optional filtering **and caching** | *(none)* | `filter`, `use_cache` |
|
|
66
|
+
| `get-job-details` | Get detailed information about a Jenkins job | `job_name` | `max_recent_builds` |
|
|
42
67
|
|
|
43
68
|
### 🛠️ Build Information
|
|
44
69
|
| Tool Name | Description | Required Fields | Optional Fields |
|
|
45
70
|
|---|---|---|---|
|
|
46
71
|
| `get-build-info` | Get information about a specific build | `job_name`, `build_number` | *(none)* |
|
|
47
|
-
| `get-build-console` | Get console output
|
|
72
|
+
| `get-build-console` | Get console output with **smart truncation** | `job_name`, `build_number` | `max_lines`, `tail_only` |
|
|
48
73
|
| `get-last-build-number` | Get the last build number for a job | `job_name` | *(none)* |
|
|
49
74
|
| `get-last-build-timestamp` | Get the timestamp of the last build | `job_name` | *(none)* |
|
|
50
75
|
|
|
@@ -71,6 +96,17 @@ This project includes:
|
|
|
71
96
|
| `get-queue-info` | Get Jenkins build queue info | *(none)* | *(none)* |
|
|
72
97
|
| `list-nodes` | List all Jenkins nodes | *(none)* | *(none)* |
|
|
73
98
|
| `get-node-info` | Get information about a Jenkins node | `node_name` | *(none)* |
|
|
99
|
+
| `health-check` | **NEW!** Run diagnostics on Jenkins connection | *(none)* | *(none)* |
|
|
100
|
+
|
|
101
|
+
### 📊 Monitoring & Management
|
|
102
|
+
| Tool Name | Description | Required Fields | Optional Fields |
|
|
103
|
+
|---|---|---|---|
|
|
104
|
+
| `get-cache-stats` | **NEW!** Get cache statistics and information | *(none)* | *(none)* |
|
|
105
|
+
| `clear-cache` | **NEW!** Clear all cached data | *(none)* | *(none)* |
|
|
106
|
+
| `get-metrics` | **NEW!** Get usage metrics and performance stats | *(none)* | `tool_name` |
|
|
107
|
+
| `configure-webhook` | **NEW!** Configure webhook notifications | `job_name`, `webhook_url`, `events` | *(none)* |
|
|
108
|
+
|
|
109
|
+
For detailed technical documentation, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
|
74
110
|
|
|
75
111
|
---
|
|
76
112
|
|
|
@@ -162,12 +198,7 @@ Add to your VS Code `settings.json`:
|
|
|
162
198
|
|
|
163
199
|
### Option 2: Environment File (.env)
|
|
164
200
|
|
|
165
|
-
|
|
166
|
-
```bash
|
|
167
|
-
cp .env.template .env
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
In the `.env` file in your project directory:
|
|
201
|
+
Create a file `.env` in your local:
|
|
171
202
|
|
|
172
203
|
```bash
|
|
173
204
|
JENKINS_URL=http://jenkins.example.com:8080
|
|
@@ -215,7 +246,11 @@ Settings are loaded in this order (later overrides earlier):
|
|
|
215
246
|
|
|
216
247
|
### Option 1: Using npx (No Installation Required)
|
|
217
248
|
```bash
|
|
249
|
+
# if .env file is located in the current directory
|
|
218
250
|
npx @rishibhushan/jenkins-mcp-server --env-file .env
|
|
251
|
+
|
|
252
|
+
# OR if .env file is located in /path/to/.env
|
|
253
|
+
npx @rishibhushan/jenkins-mcp-server --env-file /path/to/.env
|
|
219
254
|
```
|
|
220
255
|
|
|
221
256
|
### Option 2: Global Installation
|
|
@@ -713,6 +748,149 @@ pip install -r requirements.txt
|
|
|
713
748
|
}
|
|
714
749
|
}
|
|
715
750
|
```
|
|
751
|
+
---
|
|
752
|
+
|
|
753
|
+
### Solution 4: Use Global Installation with Direct Python Path
|
|
754
|
+
|
|
755
|
+
If you prefer to use global installation but still face VPN/timeout issues, you can combine both approaches.
|
|
756
|
+
|
|
757
|
+
#### Step 1: Install Globally
|
|
758
|
+
|
|
759
|
+
**Disconnect from VPN first** (to avoid proxy issues during installation):
|
|
760
|
+
```bash
|
|
761
|
+
# Install globally
|
|
762
|
+
npm install -g @rishibhushan/jenkins-mcp-server
|
|
763
|
+
|
|
764
|
+
# Verify installation
|
|
765
|
+
jenkins-mcp-server --version
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
#### Step 2: Locate Global Installation Directory
|
|
769
|
+
|
|
770
|
+
**For macOS/Linux:**
|
|
771
|
+
```bash
|
|
772
|
+
# Find the global npm directory
|
|
773
|
+
npm root -g
|
|
774
|
+
|
|
775
|
+
# This typically returns something like:
|
|
776
|
+
# /usr/local/lib/node_modules
|
|
777
|
+
# or
|
|
778
|
+
# /Users/username/.npm-global/lib/node_modules
|
|
779
|
+
|
|
780
|
+
# Your package will be at:
|
|
781
|
+
# <npm-root>/@rishibhushan/jenkins-mcp-server
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
**For Windows (PowerShell):**
|
|
785
|
+
```powershell
|
|
786
|
+
# Find the global npm directory
|
|
787
|
+
npm root -g
|
|
788
|
+
|
|
789
|
+
# This typically returns something like:
|
|
790
|
+
# C:\Users\username\AppData\Roaming\npm\node_modules
|
|
791
|
+
|
|
792
|
+
# Your package will be at:
|
|
793
|
+
# <npm-root>\@rishibhushan\jenkins-mcp-server
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
#### Step 3: Find the Python Path
|
|
797
|
+
|
|
798
|
+
Once you know the installation directory:
|
|
799
|
+
|
|
800
|
+
**macOS/Linux:**
|
|
801
|
+
```bash
|
|
802
|
+
# If npm root -g shows: /usr/local/lib/node_modules
|
|
803
|
+
# Your Python path will be:
|
|
804
|
+
/usr/local/lib/node_modules/@rishibhushan/jenkins-mcp-server/.venv/bin/python
|
|
805
|
+
|
|
806
|
+
# Verify it exists:
|
|
807
|
+
ls -la /usr/local/lib/node_modules/@rishibhushan/jenkins-mcp-server/.venv/bin/python
|
|
808
|
+
```
|
|
809
|
+
|
|
810
|
+
**Windows:**
|
|
811
|
+
```powershell
|
|
812
|
+
# If npm root -g shows: C:\Users\username\AppData\Roaming\npm\node_modules
|
|
813
|
+
# Your Python path will be:
|
|
814
|
+
C:\Users\username\AppData\Roaming\npm\node_modules\@rishibhushan\jenkins-mcp-server\.venv\Scripts\python.exe
|
|
815
|
+
|
|
816
|
+
# Verify it exists:
|
|
817
|
+
Test-Path "C:\Users\username\AppData\Roaming\npm\node_modules\@rishibhushan\jenkins-mcp-server\.venv\Scripts\python.exe"
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
#### Step 4: Configure Your MCP Client
|
|
821
|
+
|
|
822
|
+
**For Claude Desktop (macOS/Linux):**
|
|
823
|
+
```json
|
|
824
|
+
{
|
|
825
|
+
"mcpServers": {
|
|
826
|
+
"jenkins": {
|
|
827
|
+
"command": "/usr/local/lib/node_modules/@rishibhushan/jenkins-mcp-server/.venv/bin/python",
|
|
828
|
+
"args": [
|
|
829
|
+
"-m",
|
|
830
|
+
"jenkins_mcp_server",
|
|
831
|
+
"--env-file",
|
|
832
|
+
"/path/to/your/.env"
|
|
833
|
+
],
|
|
834
|
+
"env": {
|
|
835
|
+
"PYTHONPATH": "/usr/local/lib/node_modules/@rishibhushan/jenkins-mcp-server/src"
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
**For Claude Desktop (Windows):**
|
|
843
|
+
```json
|
|
844
|
+
{
|
|
845
|
+
"mcpServers": {
|
|
846
|
+
"jenkins": {
|
|
847
|
+
"command": "C:\\Users\\username\\AppData\\Roaming\\npm\\node_modules\\@rishibhushan\\jenkins-mcp-server\\.venv\\Scripts\\python.exe",
|
|
848
|
+
"args": [
|
|
849
|
+
"-m",
|
|
850
|
+
"jenkins_mcp_server",
|
|
851
|
+
"--env-file",
|
|
852
|
+
"C:\\path\\to\\your\\.env"
|
|
853
|
+
],
|
|
854
|
+
"env": {
|
|
855
|
+
"PYTHONPATH": "C:\\Users\\username\\AppData\\Roaming\\npm\\node_modules\\@rishibhushan\\jenkins-mcp-server\\src"
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
**For Other MCP Clients:**
|
|
863
|
+
|
|
864
|
+
Use the same pattern - replace the `command` with the direct Python path and set the `PYTHONPATH` environment variable.
|
|
865
|
+
|
|
866
|
+
#### Step 5: Test and Use
|
|
867
|
+
|
|
868
|
+
1. **Connect to your VPN**
|
|
869
|
+
2. **Restart your MCP client** (Claude Desktop, VS Code, etc.)
|
|
870
|
+
3. **Verify the connection** works
|
|
871
|
+
|
|
872
|
+
#### Why This Solution Works
|
|
873
|
+
|
|
874
|
+
- ✅ **Persistent installation** - No need to download on each use
|
|
875
|
+
- ✅ **Proper network routing** - Python process inherits VPN routing
|
|
876
|
+
- ✅ **Works across sessions** - Survives VPN connect/disconnect cycles
|
|
877
|
+
- ✅ **Easy updates** - Just run `npm update -g @rishibhushan/jenkins-mcp-server`
|
|
878
|
+
- ✅ **Universal** - Works with any MCP client (Claude, VS Code, etc.)
|
|
879
|
+
|
|
880
|
+
#### Quick Reference Commands
|
|
881
|
+
```bash
|
|
882
|
+
# Find your global installation
|
|
883
|
+
npm root -g
|
|
884
|
+
|
|
885
|
+
# Update global installation
|
|
886
|
+
npm update -g @rishibhushan/jenkins-mcp-server
|
|
887
|
+
|
|
888
|
+
# Uninstall if needed
|
|
889
|
+
npm uninstall -g @rishibhushan/jenkins-mcp-server
|
|
890
|
+
|
|
891
|
+
# Test the Python path works
|
|
892
|
+
/path/to/global/installation/.venv/bin/python -m jenkins_mcp_server --help
|
|
893
|
+
```
|
|
716
894
|
|
|
717
895
|
---
|
|
718
896
|
|
package/bin/jenkins-mcp.js
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* This wrapper handles:
|
|
6
6
|
* - Python version detection (cross-platform)
|
|
7
7
|
* - Virtual environment creation
|
|
8
|
-
* -
|
|
8
|
+
* - Smart proxy detection and handling (auto-detects corporate vs public networks)
|
|
9
|
+
* - Dependency installation with retry logic
|
|
9
10
|
* - Server execution
|
|
10
11
|
*
|
|
11
12
|
* Supports: Windows, macOS, Linux
|
|
@@ -26,7 +27,9 @@ const colors = {
|
|
|
26
27
|
green: '\x1b[32m',
|
|
27
28
|
yellow: '\x1b[33m',
|
|
28
29
|
red: '\x1b[31m',
|
|
29
|
-
blue: '\x1b[34m'
|
|
30
|
+
blue: '\x1b[34m',
|
|
31
|
+
cyan: '\x1b[36m',
|
|
32
|
+
bold: '\x1b[1m'
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
function log(message, color = 'reset') {
|
|
@@ -40,6 +43,93 @@ function error(message) {
|
|
|
40
43
|
console.error(`${colors.red}ERROR: ${message}${colors.reset}`);
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
function warning(message) {
|
|
47
|
+
console.error(`${colors.yellow}WARNING: ${message}${colors.reset}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function info(message) {
|
|
51
|
+
console.error(`${colors.cyan}ℹ ${message}${colors.reset}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Detect and display proxy configuration
|
|
56
|
+
* @returns {Object} Proxy configuration details
|
|
57
|
+
*/
|
|
58
|
+
function detectProxyConfig() {
|
|
59
|
+
const proxyVars = [
|
|
60
|
+
'HTTP_PROXY', 'http_proxy',
|
|
61
|
+
'HTTPS_PROXY', 'https_proxy',
|
|
62
|
+
'ALL_PROXY', 'all_proxy',
|
|
63
|
+
'NO_PROXY', 'no_proxy'
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const activeProxies = {};
|
|
67
|
+
for (const varName of proxyVars) {
|
|
68
|
+
if (process.env[varName]) {
|
|
69
|
+
activeProxies[varName] = process.env[varName];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return activeProxies;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Test if proxy is reachable
|
|
78
|
+
* @param {string} proxyUrl - Proxy URL to test
|
|
79
|
+
* @returns {boolean} True if proxy is reachable
|
|
80
|
+
*/
|
|
81
|
+
function testProxyConnectivity(proxyUrl) {
|
|
82
|
+
try {
|
|
83
|
+
// Try to parse proxy URL
|
|
84
|
+
const url = new URL(proxyUrl);
|
|
85
|
+
|
|
86
|
+
// Use curl to test proxy connectivity (cross-platform)
|
|
87
|
+
const testCmd = isWindows
|
|
88
|
+
? `curl -s -o NUL -w "%{http_code}" --proxy ${proxyUrl} --max-time 5 https://pypi.org/simple/`
|
|
89
|
+
: `curl -s -o /dev/null -w "%{http_code}" --proxy ${proxyUrl} --max-time 5 https://pypi.org/simple/`;
|
|
90
|
+
|
|
91
|
+
const result = spawnSync(isWindows ? 'cmd' : 'sh',
|
|
92
|
+
isWindows ? ['/c', testCmd] : ['-c', testCmd],
|
|
93
|
+
{
|
|
94
|
+
stdio: 'pipe',
|
|
95
|
+
encoding: 'utf-8',
|
|
96
|
+
timeout: 6000
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const httpCode = result.stdout?.trim();
|
|
101
|
+
return httpCode === '200' || httpCode === '301' || httpCode === '302';
|
|
102
|
+
} catch (e) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Test if PyPI is directly accessible (without proxy)
|
|
109
|
+
* @returns {boolean} True if PyPI is accessible
|
|
110
|
+
*/
|
|
111
|
+
function testDirectPyPIAccess() {
|
|
112
|
+
try {
|
|
113
|
+
const testCmd = isWindows
|
|
114
|
+
? 'curl -s -o NUL -w "%{http_code}" --max-time 5 https://pypi.org/simple/'
|
|
115
|
+
: 'curl -s -o /dev/null -w "%{http_code}" --max-time 5 https://pypi.org/simple/';
|
|
116
|
+
|
|
117
|
+
const result = spawnSync(isWindows ? 'cmd' : 'sh',
|
|
118
|
+
isWindows ? ['/c', testCmd] : ['-c', testCmd],
|
|
119
|
+
{
|
|
120
|
+
stdio: 'pipe',
|
|
121
|
+
encoding: 'utf-8',
|
|
122
|
+
timeout: 6000
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const httpCode = result.stdout?.trim();
|
|
127
|
+
return httpCode === '200' || httpCode === '301' || httpCode === '302';
|
|
128
|
+
} catch (e) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
43
133
|
/**
|
|
44
134
|
* Check for Python 3 installation
|
|
45
135
|
* @returns {string} Python command name
|
|
@@ -157,7 +247,89 @@ function dependenciesInstalled(pipPath) {
|
|
|
157
247
|
}
|
|
158
248
|
|
|
159
249
|
/**
|
|
160
|
-
*
|
|
250
|
+
* Show helpful guidance for proxy-related installation failures
|
|
251
|
+
*/
|
|
252
|
+
function showProxyTroubleshooting(activeProxies, canAccessDirectly, proxyWorks) {
|
|
253
|
+
console.error('\n' + '='.repeat(70));
|
|
254
|
+
console.error(colors.bold + colors.yellow + 'INSTALLATION FAILED - NETWORK CONFIGURATION ISSUE' + colors.reset);
|
|
255
|
+
console.error('='.repeat(70));
|
|
256
|
+
|
|
257
|
+
if (Object.keys(activeProxies).length > 0) {
|
|
258
|
+
console.error('\n📡 Active proxy environment variables found:');
|
|
259
|
+
for (const [key, value] of Object.entries(activeProxies)) {
|
|
260
|
+
console.error(` ${colors.cyan}${key}${colors.reset} = ${value}`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!proxyWorks && canAccessDirectly) {
|
|
264
|
+
// Proxy is set but doesn't work, and direct access works
|
|
265
|
+
console.error('\n' + colors.red + '❌ Proxy is NOT reachable' + colors.reset);
|
|
266
|
+
console.error(colors.green + '✓ Direct internet access IS available' + colors.reset);
|
|
267
|
+
console.error('\n' + colors.yellow + '⚠️ You\'re on a PUBLIC network but have proxy settings from a corporate/VPN network!' + colors.reset);
|
|
268
|
+
console.error('\n💡 SOLUTION:\n');
|
|
269
|
+
console.error(colors.bold + 'Remove the proxy settings:' + colors.reset);
|
|
270
|
+
console.error(colors.cyan + ' unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy ALL_PROXY all_proxy' + colors.reset);
|
|
271
|
+
console.error(' Then run the command again.\n');
|
|
272
|
+
|
|
273
|
+
} else if (proxyWorks && !canAccessDirectly) {
|
|
274
|
+
// Proxy works, direct access doesn't
|
|
275
|
+
console.error('\n' + colors.green + '✓ Proxy IS reachable' + colors.reset);
|
|
276
|
+
console.error(colors.red + '❌ Direct internet access is NOT available' + colors.reset);
|
|
277
|
+
console.error('\n' + colors.blue + 'You\'re on a CORPORATE network - proxy is required.' + colors.reset);
|
|
278
|
+
console.error('\n💡 SOLUTION:\n');
|
|
279
|
+
console.error('The proxy should work. The error may be due to:');
|
|
280
|
+
console.error('1. SSL certificate issues - contact your IT department');
|
|
281
|
+
console.error('2. Authentication required - check if proxy needs username/password');
|
|
282
|
+
console.error('3. Specific packages blocked - contact your IT department\n');
|
|
283
|
+
|
|
284
|
+
} else if (!proxyWorks && !canAccessDirectly) {
|
|
285
|
+
// Neither works
|
|
286
|
+
console.error('\n' + colors.red + '❌ Proxy is NOT reachable' + colors.reset);
|
|
287
|
+
console.error(colors.red + '❌ Direct internet access is NOT available' + colors.reset);
|
|
288
|
+
console.error('\n💡 SOLUTIONS:\n');
|
|
289
|
+
console.error('1. Check your internet connection');
|
|
290
|
+
console.error('2. If on corporate network, verify proxy settings with IT');
|
|
291
|
+
console.error('3. Try a different network (mobile hotspot, home WiFi)');
|
|
292
|
+
console.error('4. Check firewall/antivirus settings\n');
|
|
293
|
+
|
|
294
|
+
} else {
|
|
295
|
+
// Both work (unusual case)
|
|
296
|
+
console.error('\n' + colors.green + '✓ Proxy IS reachable' + colors.reset);
|
|
297
|
+
console.error(colors.green + '✓ Direct internet access IS available' + colors.reset);
|
|
298
|
+
console.error('\nThe issue may be:');
|
|
299
|
+
console.error('1. SSL certificate problems');
|
|
300
|
+
console.error('2. Intermittent connectivity');
|
|
301
|
+
console.error('3. Package-specific blocking\n');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
console.error(colors.bold + 'Additional options:' + colors.reset);
|
|
305
|
+
console.error('• Check system proxy settings:');
|
|
306
|
+
if (!isWindows) {
|
|
307
|
+
console.error(' macOS: System Settings → Network → Advanced → Proxies');
|
|
308
|
+
} else {
|
|
309
|
+
console.error(' Windows: Settings → Network & Internet → Proxy');
|
|
310
|
+
}
|
|
311
|
+
console.error('• Try manual installation (see TROUBLESHOOTING.md)');
|
|
312
|
+
|
|
313
|
+
} else {
|
|
314
|
+
console.error('\n💡 No proxy environment variables detected.\n');
|
|
315
|
+
console.error('POSSIBLE CAUSES:');
|
|
316
|
+
console.error('• Network connectivity issues');
|
|
317
|
+
console.error('• Firewall blocking PyPI access');
|
|
318
|
+
console.error('• DNS resolution problems');
|
|
319
|
+
console.error('• System-level proxy (not in environment variables)\n');
|
|
320
|
+
|
|
321
|
+
console.error('SOLUTIONS TO TRY:');
|
|
322
|
+
console.error('1. Check your internet connection');
|
|
323
|
+
console.error('2. Try: curl https://pypi.org/simple/');
|
|
324
|
+
console.error('3. Check firewall/antivirus settings');
|
|
325
|
+
console.error('4. Check system proxy settings (not environment variables)\n');
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
console.error('='.repeat(70) + '\n');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Install Python dependencies with smart proxy detection
|
|
161
333
|
* @param {string} venvPath - Path to virtual environment
|
|
162
334
|
*/
|
|
163
335
|
function installDependencies(venvPath) {
|
|
@@ -171,11 +343,10 @@ function installDependencies(venvPath) {
|
|
|
171
343
|
if (fs.existsSync(wheelsPath)) {
|
|
172
344
|
console.error('Using pre-packaged wheels (no internet required)...');
|
|
173
345
|
|
|
174
|
-
// Install from local wheels (fast, no network needed)
|
|
175
346
|
const installReqs = spawnSync(pip, [
|
|
176
347
|
'install',
|
|
177
|
-
'--no-index',
|
|
178
|
-
'--find-links', wheelsPath,
|
|
348
|
+
'--no-index',
|
|
349
|
+
'--find-links', wheelsPath,
|
|
179
350
|
'-r', requirementsPath
|
|
180
351
|
], {
|
|
181
352
|
cwd: projectRoot,
|
|
@@ -186,35 +357,96 @@ function installDependencies(venvPath) {
|
|
|
186
357
|
error('Failed to install from wheels');
|
|
187
358
|
process.exit(1);
|
|
188
359
|
}
|
|
360
|
+
|
|
361
|
+
log('✓ Requirements installed', 'green');
|
|
189
362
|
} else {
|
|
190
|
-
//
|
|
363
|
+
// Need network - detect proxy configuration intelligently
|
|
191
364
|
console.error('Downloading from PyPI...');
|
|
365
|
+
|
|
366
|
+
const activeProxies = detectProxyConfig();
|
|
367
|
+
const hasProxyVars = Object.keys(activeProxies).length > 0;
|
|
368
|
+
|
|
369
|
+
let proxyToUse = null;
|
|
370
|
+
let useNoProxy = false;
|
|
371
|
+
|
|
372
|
+
if (hasProxyVars) {
|
|
373
|
+
const proxyUrl = process.env.HTTP_PROXY || process.env.http_proxy ||
|
|
374
|
+
process.env.HTTPS_PROXY || process.env.https_proxy;
|
|
375
|
+
|
|
376
|
+
info('Proxy environment variables detected. Testing connectivity...');
|
|
377
|
+
|
|
378
|
+
// Test proxy connectivity
|
|
379
|
+
const proxyWorks = proxyUrl && proxyUrl.startsWith('http') && testProxyConnectivity(proxyUrl);
|
|
380
|
+
const directWorks = testDirectPyPIAccess();
|
|
381
|
+
|
|
382
|
+
if (proxyWorks && !directWorks) {
|
|
383
|
+
// Corporate network - use proxy
|
|
384
|
+
info('Corporate network detected. Using proxy.');
|
|
385
|
+
proxyToUse = proxyUrl;
|
|
386
|
+
} else if (!proxyWorks && directWorks) {
|
|
387
|
+
// Public network with stale proxy vars - ignore proxy
|
|
388
|
+
warning('Proxy unreachable but direct access available. Ignoring proxy settings.');
|
|
389
|
+
useNoProxy = true;
|
|
390
|
+
} else if (proxyWorks && directWorks) {
|
|
391
|
+
// Both work - prefer direct
|
|
392
|
+
info('Both proxy and direct access available. Using direct connection.');
|
|
393
|
+
useNoProxy = true;
|
|
394
|
+
} else {
|
|
395
|
+
// Neither works - will fail but try direct
|
|
396
|
+
warning('Neither proxy nor direct access working. Attempting direct connection...');
|
|
397
|
+
useNoProxy = true;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Build pip arguments
|
|
192
402
|
const pipArgs = ['install', '-r', requirementsPath];
|
|
193
403
|
|
|
194
|
-
|
|
404
|
+
// Trusted hosts for SSL-friendly installation
|
|
405
|
+
const trustedHostArgs = [
|
|
195
406
|
'--trusted-host', 'pypi.org',
|
|
196
407
|
'--trusted-host', 'pypi.python.org',
|
|
197
408
|
'--trusted-host', 'files.pythonhosted.org'
|
|
198
409
|
];
|
|
410
|
+
pipArgs.push(...trustedHostArgs);
|
|
411
|
+
|
|
412
|
+
// Add proxy if needed
|
|
413
|
+
if (proxyToUse && !useNoProxy) {
|
|
414
|
+
info(`Using proxy: ${proxyToUse}`);
|
|
415
|
+
pipArgs.push('--proxy', proxyToUse);
|
|
416
|
+
}
|
|
199
417
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
418
|
+
// Set up environment (potentially removing proxy vars)
|
|
419
|
+
const pipEnv = { ...process.env };
|
|
420
|
+
if (useNoProxy) {
|
|
421
|
+
// Remove proxy environment variables for this pip call
|
|
422
|
+
delete pipEnv.HTTP_PROXY;
|
|
423
|
+
delete pipEnv.HTTPS_PROXY;
|
|
424
|
+
delete pipEnv.http_proxy;
|
|
425
|
+
delete pipEnv.https_proxy;
|
|
426
|
+
delete pipEnv.ALL_PROXY;
|
|
427
|
+
delete pipEnv.all_proxy;
|
|
203
428
|
}
|
|
204
|
-
pipArgs.push(...proxyFriendlyArgs);
|
|
205
429
|
|
|
206
430
|
const installReqs = spawnSync(pip, pipArgs, {
|
|
207
431
|
cwd: projectRoot,
|
|
208
|
-
stdio: 'inherit'
|
|
432
|
+
stdio: 'inherit',
|
|
433
|
+
env: pipEnv
|
|
209
434
|
});
|
|
210
435
|
|
|
211
436
|
if (installReqs.status !== 0) {
|
|
212
|
-
error('Failed to install dependencies');
|
|
437
|
+
error('Failed to install dependencies from PyPI');
|
|
438
|
+
|
|
439
|
+
// Show intelligent troubleshooting
|
|
440
|
+
const proxyUrl = process.env.HTTP_PROXY || process.env.http_proxy;
|
|
441
|
+
const proxyWorks = proxyUrl ? testProxyConnectivity(proxyUrl) : false;
|
|
442
|
+
const directWorks = testDirectPyPIAccess();
|
|
443
|
+
|
|
444
|
+
showProxyTroubleshooting(activeProxies, directWorks, proxyWorks);
|
|
213
445
|
process.exit(1);
|
|
214
446
|
}
|
|
215
|
-
}
|
|
216
447
|
|
|
217
|
-
|
|
448
|
+
log('✓ Requirements installed', 'green');
|
|
449
|
+
}
|
|
218
450
|
|
|
219
451
|
// Install package itself
|
|
220
452
|
console.error('Installing jenkins-mcp-server package...');
|
|
@@ -230,7 +462,7 @@ function installDependencies(venvPath) {
|
|
|
230
462
|
process.exit(1);
|
|
231
463
|
}
|
|
232
464
|
|
|
233
|
-
|
|
465
|
+
log('✓ Package installed successfully', 'green');
|
|
234
466
|
}
|
|
235
467
|
|
|
236
468
|
/**
|
package/package.json
CHANGED
package/requirements.txt
CHANGED
|
@@ -14,19 +14,27 @@ from . import server
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def setup_logging(verbose: bool = False) -> None:
|
|
17
|
-
"""Configure logging
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
"""Configure structured logging"""
|
|
18
|
+
import structlog
|
|
19
|
+
|
|
20
|
+
# Configure structlog
|
|
21
|
+
structlog.configure(
|
|
22
|
+
processors=[
|
|
23
|
+
structlog.stdlib.filter_by_level,
|
|
24
|
+
structlog.stdlib.add_logger_name,
|
|
25
|
+
structlog.stdlib.add_log_level,
|
|
26
|
+
structlog.stdlib.PositionalArgumentsFormatter(),
|
|
27
|
+
structlog.processors.TimeStamper(fmt="iso"),
|
|
28
|
+
structlog.processors.StackInfoRenderer(),
|
|
29
|
+
structlog.processors.format_exc_info,
|
|
30
|
+
structlog.processors.UnicodeDecoder(),
|
|
31
|
+
structlog.processors.JSONRenderer() if not verbose else structlog.dev.ConsoleRenderer()
|
|
32
|
+
],
|
|
33
|
+
context_class=dict,
|
|
34
|
+
logger_factory=structlog.stdlib.LoggerFactory(),
|
|
35
|
+
cache_logger_on_first_use=True,
|
|
24
36
|
)
|
|
25
37
|
|
|
26
|
-
# Reduce noise from libraries
|
|
27
|
-
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
|
28
|
-
logging.getLogger('requests').setLevel(logging.WARNING)
|
|
29
|
-
|
|
30
38
|
|
|
31
39
|
def main():
|
|
32
40
|
"""
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|