@rishibhushan/jenkins-mcp-server 1.0.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.
@@ -0,0 +1,327 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Jenkins MCP Server - Node.js Wrapper
4
+ *
5
+ * This wrapper handles:
6
+ * - Python version detection (cross-platform)
7
+ * - Virtual environment creation
8
+ * - Dependency installation
9
+ * - Server execution
10
+ *
11
+ * Supports: Windows, macOS, Linux
12
+ */
13
+
14
+ const { spawn, spawnSync } = require('child_process');
15
+ const path = require('path');
16
+ const fs = require('fs');
17
+
18
+ // Configuration
19
+ const projectRoot = path.join(__dirname, '..');
20
+ const args = process.argv.slice(2);
21
+ const isWindows = process.platform === 'win32';
22
+
23
+ // Color codes for terminal output (if supported)
24
+ const colors = {
25
+ reset: '\x1b[0m',
26
+ green: '\x1b[32m',
27
+ yellow: '\x1b[33m',
28
+ red: '\x1b[31m',
29
+ blue: '\x1b[34m'
30
+ };
31
+
32
+ function log(message, color = 'reset') {
33
+ const colorCode = process.stdout.isTTY ? colors[color] : '';
34
+ const resetCode = process.stdout.isTTY ? colors.reset : '';
35
+ console.log(`${colorCode}${message}${resetCode}`);
36
+ }
37
+
38
+ function error(message) {
39
+ console.error(`${colors.red}ERROR: ${message}${colors.reset}`);
40
+ }
41
+
42
+ /**
43
+ * Check for Python 3 installation
44
+ * @returns {string} Python command name
45
+ */
46
+ function checkPython() {
47
+ const pythonCommands = isWindows
48
+ ? ['python', 'py', 'python3']
49
+ : ['python3', 'python'];
50
+
51
+ log('Checking for Python 3...', 'blue');
52
+
53
+ for (const cmd of pythonCommands) {
54
+ try {
55
+ const result = spawnSync(cmd, ['--version'], {
56
+ stdio: 'pipe',
57
+ encoding: 'utf-8'
58
+ });
59
+
60
+ if (result.status === 0) {
61
+ const output = result.stdout || result.stderr;
62
+ if (output.includes('Python 3')) {
63
+ const version = output.trim();
64
+ log(`✓ Found ${version}`, 'green');
65
+ return cmd;
66
+ }
67
+ }
68
+ } catch (e) {
69
+ // Command not found, try next
70
+ continue;
71
+ }
72
+ }
73
+
74
+ error('Python 3 is required but not found.');
75
+ console.error('\nPlease install Python 3.8 or higher from:');
76
+ console.error(' https://www.python.org/downloads/');
77
+ console.error('\nAfter installation, restart your terminal and try again.');
78
+ process.exit(1);
79
+ }
80
+
81
+ /**
82
+ * Get paths for virtual environment binaries
83
+ * @param {string} venvPath - Path to virtual environment
84
+ * @returns {Object} Paths to python and pip executables
85
+ */
86
+ function getVenvPaths(venvPath) {
87
+ if (isWindows) {
88
+ return {
89
+ python: path.join(venvPath, 'Scripts', 'python.exe'),
90
+ pip: path.join(venvPath, 'Scripts', 'pip.exe')
91
+ };
92
+ } else {
93
+ return {
94
+ python: path.join(venvPath, 'bin', 'python'),
95
+ pip: path.join(venvPath, 'bin', 'pip')
96
+ };
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Create virtual environment if it doesn't exist
102
+ * @param {string} pythonCmd - Python command to use
103
+ * @returns {string} Path to virtual environment
104
+ */
105
+ function ensureVenv(pythonCmd) {
106
+ const venvPath = path.join(projectRoot, '.venv');
107
+
108
+ if (fs.existsSync(venvPath)) {
109
+ log('✓ Virtual environment exists', 'green');
110
+ return venvPath;
111
+ }
112
+
113
+ log('Creating Python virtual environment...', 'yellow');
114
+
115
+ const result = spawnSync(pythonCmd, ['-m', 'venv', venvPath], {
116
+ cwd: projectRoot,
117
+ stdio: 'inherit'
118
+ });
119
+
120
+ if (result.status !== 0) {
121
+ error('Failed to create virtual environment');
122
+ console.error('\nTroubleshooting:');
123
+ console.error(' 1. Ensure Python venv module is installed');
124
+ console.error(' 2. Check disk space and permissions');
125
+ console.error(' 3. Try manually: python3 -m venv .venv');
126
+ process.exit(1);
127
+ }
128
+
129
+ log('✓ Virtual environment created', 'green');
130
+ return venvPath;
131
+ }
132
+
133
+ /**
134
+ * Check if dependencies are installed
135
+ * @param {string} pipPath - Path to pip executable
136
+ * @returns {boolean} True if dependencies are installed
137
+ */
138
+ function dependenciesInstalled(pipPath) {
139
+ try {
140
+ const result = spawnSync(pipPath, ['list'], {
141
+ stdio: 'pipe',
142
+ encoding: 'utf-8'
143
+ });
144
+
145
+ if (result.status === 0) {
146
+ const output = result.stdout || '';
147
+ // Check for key dependencies
148
+ return output.includes('python-jenkins') &&
149
+ output.includes('mcp') &&
150
+ output.includes('requests');
151
+ }
152
+ } catch (e) {
153
+ // If we can't check, assume not installed
154
+ }
155
+ return false;
156
+ }
157
+
158
+ /**
159
+ * Install Python dependencies
160
+ * @param {string} venvPath - Path to virtual environment
161
+ */
162
+ function installDependencies(venvPath) {
163
+ const { pip } = getVenvPaths(venvPath);
164
+ const requirementsPath = path.join(projectRoot, 'requirements.txt');
165
+
166
+ if (!fs.existsSync(requirementsPath)) {
167
+ error('requirements.txt not found');
168
+ console.error(`Expected at: ${requirementsPath}`);
169
+ process.exit(1);
170
+ }
171
+
172
+ log('Installing Python dependencies...', 'yellow');
173
+ log('This may take a minute...', 'blue');
174
+
175
+ const result = spawnSync(pip, ['install', '-r', requirementsPath], {
176
+ cwd: projectRoot,
177
+ stdio: 'inherit'
178
+ });
179
+
180
+ if (result.status !== 0) {
181
+ error('Failed to install dependencies');
182
+ console.error('\nTroubleshooting:');
183
+ console.error(' 1. Check your internet connection');
184
+ console.error(' 2. If behind a proxy, set HTTP_PROXY/HTTPS_PROXY env vars');
185
+ console.error(' 3. Try manually: .venv/bin/pip install -r requirements.txt');
186
+ process.exit(1);
187
+ }
188
+
189
+ log('✓ Dependencies installed', 'green');
190
+ }
191
+
192
+ /**
193
+ * Find the Python entry point
194
+ * @returns {Object} Entry point information
195
+ */
196
+ function findEntryPoint() {
197
+ const mainPyPath = path.join(projectRoot, 'src', 'jenkins_mcp_server', '__main__.py');
198
+
199
+ if (fs.existsSync(mainPyPath)) {
200
+ return {
201
+ type: 'module',
202
+ args: ['-m', 'jenkins_mcp_server']
203
+ };
204
+ }
205
+
206
+ // Fallback options
207
+ const fallbacks = [
208
+ path.join(projectRoot, 'src', 'jenkins_mcp_server', 'server.py'),
209
+ path.join(projectRoot, 'src', 'main.py'),
210
+ path.join(projectRoot, 'main.py')
211
+ ];
212
+
213
+ for (const filePath of fallbacks) {
214
+ if (fs.existsSync(filePath)) {
215
+ return {
216
+ type: 'script',
217
+ args: [filePath]
218
+ };
219
+ }
220
+ }
221
+
222
+ error('Could not find Python entry point');
223
+ console.error('\nExpected one of:');
224
+ console.error(' - src/jenkins_mcp_server/__main__.py (preferred)');
225
+ console.error(' - src/jenkins_mcp_server/server.py');
226
+ console.error(' - src/main.py');
227
+ process.exit(1);
228
+ }
229
+
230
+ /**
231
+ * Run the Jenkins MCP Server
232
+ * @param {string} venvPath - Path to virtual environment
233
+ */
234
+ function runServer(venvPath) {
235
+ const { python } = getVenvPaths(venvPath);
236
+ const entryPoint = findEntryPoint();
237
+
238
+ // Set up environment
239
+ const env = {
240
+ ...process.env,
241
+ PYTHONPATH: path.join(projectRoot, 'src'),
242
+ PYTHONUNBUFFERED: '1' // Ensure output is not buffered
243
+ };
244
+
245
+ const serverArgs = [...entryPoint.args, ...args];
246
+
247
+ log('Starting Jenkins MCP Server...', 'green');
248
+ log(`Command: ${python} ${serverArgs.join(' ')}`, 'blue');
249
+
250
+ const server = spawn(python, serverArgs, {
251
+ cwd: projectRoot,
252
+ stdio: 'inherit',
253
+ env: env,
254
+ shell: isWindows
255
+ });
256
+
257
+ server.on('error', (err) => {
258
+ error(`Failed to start server: ${err.message}`);
259
+ process.exit(1);
260
+ });
261
+
262
+ server.on('close', (code) => {
263
+ if (code !== 0 && code !== null) {
264
+ log(`Server exited with code ${code}`, 'yellow');
265
+ }
266
+ process.exit(code || 0);
267
+ });
268
+
269
+ // Handle graceful shutdown
270
+ const cleanup = () => {
271
+ log('\nShutting down...', 'yellow');
272
+ server.kill('SIGTERM');
273
+ };
274
+
275
+ process.on('SIGINT', cleanup);
276
+ process.on('SIGTERM', cleanup);
277
+
278
+ // Windows doesn't support SIGINT/SIGTERM the same way
279
+ if (isWindows) {
280
+ process.on('SIGBREAK', cleanup);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Main execution flow
286
+ */
287
+ function main() {
288
+ try {
289
+ // Check for help flag
290
+ if (args.includes('--help') || args.includes('-h')) {
291
+ console.log('Jenkins MCP Server - Node.js Wrapper');
292
+ console.log('\nUsage: jenkins-mcp-server [options]');
293
+ console.log('\nOptions:');
294
+ console.log(' --env-file PATH Path to custom .env file');
295
+ console.log(' --verbose, -v Enable verbose logging');
296
+ console.log(' --no-vscode Skip loading VS Code settings');
297
+ console.log(' --version Show version');
298
+ console.log(' --help, -h Show this help message');
299
+ process.exit(0);
300
+ }
301
+
302
+ // Check Python availability
303
+ const pythonCmd = checkPython();
304
+
305
+ // Ensure virtual environment exists
306
+ const venvPath = ensureVenv(pythonCmd);
307
+
308
+ // Check and install dependencies if needed
309
+ const { pip } = getVenvPaths(venvPath);
310
+ if (!dependenciesInstalled(pip)) {
311
+ installDependencies(venvPath);
312
+ } else {
313
+ log('✓ Dependencies already installed', 'green');
314
+ }
315
+
316
+ // Run the server
317
+ runServer(venvPath);
318
+
319
+ } catch (err) {
320
+ error(`Unexpected error: ${err.message}`);
321
+ console.error(err.stack);
322
+ process.exit(1);
323
+ }
324
+ }
325
+
326
+ // Run main function
327
+ main();
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@rishibhushan/jenkins-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "AI-enabled Jenkins automation via Model Context Protocol (MCP)",
5
+ "main": "bin/jenkins-mcp.js",
6
+ "bin": {
7
+ "jenkins-mcp-server": "bin/jenkins-mcp.js"
8
+ },
9
+ "scripts": {
10
+ "start": "node bin/jenkins-mcp.js",
11
+ "test": "python -m pytest tests/",
12
+ "build": "python -m build"
13
+ },
14
+ "files": [
15
+ "bin/",
16
+ "src/",
17
+ "requirements.txt",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "engines": {
22
+ "node": ">=14.0.0"
23
+ },
24
+ "keywords": [
25
+ "mcp",
26
+ "jenkins",
27
+ "ai",
28
+ "automation",
29
+ "ci-cd",
30
+ "devops",
31
+ "model-context-protocol",
32
+ "llm",
33
+ "chatgpt",
34
+ "claude"
35
+ ],
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/rishibhushan/jenkins_mcp_server.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/rishibhushan/jenkins_mcp_server/issues"
42
+ },
43
+ "homepage": "https://github.com/rishibhushan/jenkins_mcp_server#readme",
44
+ "author": "Rishi Bhushan",
45
+ "license": "MIT",
46
+ "os": [
47
+ "darwin",
48
+ "linux",
49
+ "win32"
50
+ ]
51
+ }
@@ -0,0 +1,12 @@
1
+ # Core dependencies
2
+ mcp>=1.0.0
3
+ python-jenkins>=1.8.0
4
+ requests>=2.28.0
5
+
6
+ # Settings management
7
+ pydantic>=2.0.0
8
+ pydantic-settings>=2.0.0
9
+ python-dotenv>=1.0.0
10
+
11
+ # HTTP improvements
12
+ urllib3>=2.0.0
@@ -0,0 +1,133 @@
1
+ """
2
+ Jenkins MCP Server Package
3
+
4
+ Main entry point for the Jenkins MCP Server.
5
+ """
6
+
7
+ import argparse
8
+ import asyncio
9
+ import logging
10
+ import sys
11
+
12
+ from .config import get_settings, get_default_settings
13
+ from . import server
14
+
15
+
16
+ def setup_logging(verbose: bool = False) -> None:
17
+ """Configure logging for the application"""
18
+ level = logging.DEBUG if verbose else logging.INFO
19
+
20
+ logging.basicConfig(
21
+ level=level,
22
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
23
+ stream=sys.stderr
24
+ )
25
+
26
+ # Reduce noise from libraries
27
+ logging.getLogger('urllib3').setLevel(logging.WARNING)
28
+ logging.getLogger('requests').setLevel(logging.WARNING)
29
+
30
+
31
+ def main():
32
+ """
33
+ Main entry point for the Jenkins MCP Server.
34
+
35
+ Parses command-line arguments, configures settings, and starts the server.
36
+ """
37
+ parser = argparse.ArgumentParser(
38
+ description='Jenkins MCP Server - AI-enabled Jenkins automation',
39
+ formatter_class=argparse.RawDescriptionHelpFormatter,
40
+ epilog="""
41
+ Examples:
42
+ %(prog)s # Use default .env file
43
+ %(prog)s --env-file /path/to/.env # Use custom .env file
44
+ %(prog)s --verbose # Enable debug logging
45
+ %(prog)s --env-file custom.env --verbose # Combined options
46
+
47
+ Environment Variables:
48
+ JENKINS_URL Jenkins server URL (e.g., http://localhost:8080)
49
+ JENKINS_USERNAME Jenkins username
50
+ JENKINS_TOKEN Jenkins API token (recommended)
51
+ JENKINS_PASSWORD Jenkins password (alternative to token)
52
+
53
+ Configuration Priority:
54
+ 1. Command-line arguments (--env-file)
55
+ 2. VS Code settings.json
56
+ 3. Environment variables
57
+ 4. Default .env file
58
+ """
59
+ )
60
+
61
+ parser.add_argument(
62
+ '--env-file',
63
+ metavar='PATH',
64
+ help='Path to custom .env file with Jenkins credentials'
65
+ )
66
+
67
+ parser.add_argument(
68
+ '--verbose', '-v',
69
+ action='store_true',
70
+ help='Enable verbose/debug logging'
71
+ )
72
+
73
+ parser.add_argument(
74
+ '--version',
75
+ action='version',
76
+ version='jenkins-mcp-server 1.0.0'
77
+ )
78
+
79
+ parser.add_argument(
80
+ '--no-vscode',
81
+ action='store_true',
82
+ help='Skip loading settings from VS Code (use only .env/environment)'
83
+ )
84
+
85
+ args = parser.parse_args()
86
+
87
+ # Setup logging first
88
+ setup_logging(args.verbose)
89
+ logger = logging.getLogger(__name__)
90
+
91
+ try:
92
+ # Load settings based on arguments
93
+ logger.info("Loading Jenkins configuration...")
94
+
95
+ settings = get_settings(
96
+ env_file=args.env_file,
97
+ load_vscode=not args.no_vscode
98
+ )
99
+
100
+ # Validate configuration
101
+ if not settings.is_configured:
102
+ logger.error("Jenkins configuration is incomplete!")
103
+ logger.error("Required: URL, username, and (token or password)")
104
+ logger.error("\nPlease configure via:")
105
+ logger.error(" 1. .env file (JENKINS_URL, JENKINS_USERNAME, JENKINS_TOKEN)")
106
+ logger.error(" 2. VS Code settings.json")
107
+ logger.error(" 3. Environment variables")
108
+ logger.error("\nFor help, run: %(prog)s --help" % {'prog': parser.prog})
109
+ sys.exit(1)
110
+
111
+ # Log configuration summary
112
+ logger.info(f"Jenkins server: {settings.url}")
113
+ logger.info(f"Username: {settings.username}")
114
+ logger.info(f"Authentication: {settings.auth_method}")
115
+
116
+ # Pass settings to server module
117
+ server.set_jenkins_settings(settings)
118
+
119
+ # Run the server
120
+ logger.info("Starting Jenkins MCP Server...")
121
+ asyncio.run(server.main())
122
+
123
+ except KeyboardInterrupt:
124
+ logger.info("Server stopped by user")
125
+ sys.exit(0)
126
+ except Exception as e:
127
+ logger.error(f"Failed to start server: {e}", exc_info=args.verbose)
128
+ sys.exit(1)
129
+
130
+
131
+ # Package metadata
132
+ __version__ = "1.0.0"
133
+ __all__ = ['main', 'server']
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Main entry point for running the Jenkins MCP Server as a module.
4
+
5
+ Usage:
6
+ python -m jenkins_mcp_server [options]
7
+
8
+ Options:
9
+ --env-file PATH Path to custom .env file
10
+ --verbose, -v Enable verbose logging
11
+ --no-vscode Skip loading VS Code settings
12
+ --version Show version and exit
13
+ --help, -h Show help message
14
+ """
15
+
16
+ from . import main
17
+
18
+ if __name__ == "__main__":
19
+ main()