#
tokens: 3486/50000 4/4 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── LICENSE
├── package.json
├── readme.md
├── src
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Jenkins Server MCP
  2 | 
  3 | A Model Context Protocol (MCP) server that provides tools for interacting with Jenkins CI/CD servers. This server enables AI assistants to check build statuses, trigger builds, and retrieve build logs through a standardized interface.
  4 | 
  5 | ## Installation
  6 | 
  7 | 1. Clone this repository:
  8 | ```bash
  9 | git clone https://github.com/hekmon8/jenkins-server-mcp.git
 10 | cd jenkins-server-mcp
 11 | ```
 12 | 
 13 | 2. Install dependencies:
 14 | ```bash
 15 | npm install
 16 | ```
 17 | 
 18 | 3. Build the project:
 19 | ```bash
 20 | npm run build
 21 | ```
 22 | 
 23 | ## Configuration
 24 | 
 25 | The server requires the following environment variables:
 26 | 
 27 | - `JENKINS_URL`: The URL of your Jenkins server
 28 | - `JENKINS_USER`: Jenkins username for authentication
 29 | - `JENKINS_TOKEN`: Jenkins API token for authentication
 30 | 
 31 | Configure these in your MCP settings file:
 32 | 
 33 | ### For Claude Desktop
 34 | 
 35 | MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
 36 | Windows: `%APPDATA%/Claude/claude_desktop_config.json`
 37 | 
 38 | ```json
 39 | {
 40 |   "mcpServers": {
 41 |     "jenkins-server": {
 42 |       "command": "node",
 43 |       "args": ["/path/to/jenkins-server-mcp/build/index.js"],
 44 |       "env": {
 45 |         "JENKINS_URL": "https://your-jenkins-server.com",
 46 |         "JENKINS_USER": "your-username",
 47 |         "JENKINS_TOKEN": "your-api-token"
 48 |       }
 49 |     }
 50 |   }
 51 | }
 52 | ```
 53 | 
 54 | ## Tools and Usage
 55 | 
 56 | ### 1. Get Build Status
 57 | 
 58 | Get the status of a Jenkins build:
 59 | 
 60 | ```typescript
 61 | // Example usage
 62 | const result = await mcpClient.useTool("jenkins-server", "get_build_status", {
 63 |   jobPath: "view/xxx_debug",
 64 |   buildNumber: "lastBuild"  // Optional, defaults to lastBuild
 65 | });
 66 | ```
 67 | 
 68 | Input Schema:
 69 | ```json
 70 | {
 71 |   "jobPath": "string",  // Path to Jenkins job
 72 |   "buildNumber": "string"  // Optional, build number or "lastBuild"
 73 | }
 74 | ```
 75 | 
 76 | ### 2. Trigger Build
 77 | 
 78 | Trigger a new Jenkins build with parameters:
 79 | 
 80 | ```typescript
 81 | // Example usage
 82 | const result = await mcpClient.useTool("jenkins-server", "trigger_build", {
 83 |   jobPath: "view/xxx_debug",
 84 |   parameters: {
 85 |     BRANCH: "main",
 86 |     BUILD_TYPE: "debug"
 87 |   }
 88 | });
 89 | ```
 90 | 
 91 | Input Schema:
 92 | ```json
 93 | {
 94 |   "jobPath": "string",  // Path to Jenkins job
 95 |   "parameters": {
 96 |     // Build parameters as key-value pairs
 97 |   }
 98 | }
 99 | ```
100 | 
101 | ### 3. Get Build Log
102 | 
103 | Retrieve the console output of a Jenkins build:
104 | 
105 | ```typescript
106 | // Example usage
107 | const result = await mcpClient.useTool("jenkins-server", "get_build_log", {
108 |   jobPath: "view/xxx_debug",
109 |   buildNumber: "lastBuild"
110 | });
111 | ```
112 | 
113 | Input Schema:
114 | ```json
115 | {
116 |   "jobPath": "string",  // Path to Jenkins job
117 |   "buildNumber": "string"  // Build number or "lastBuild"
118 | }
119 | ```
120 | 
121 | ## Development
122 | 
123 | For development with auto-rebuild:
124 | ```bash
125 | npm run watch
126 | ```
127 | 
128 | ### Debugging
129 | 
130 | Since MCP servers communicate over stdio, you can use the MCP Inspector for debugging:
131 | 
132 | ```bash
133 | npm run inspector
134 | ```
135 | 
136 | This will provide a URL to access debugging tools in your browser.
137 | 
138 | ## Thanks
139 | 
140 | Thanks AIMCP(https://www.aimcp.info).
141 | 
142 | ## License
143 | 
144 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
145 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "Node16",
 5 |     "moduleResolution": "Node16",
 6 |     "outDir": "./build",
 7 |     "rootDir": "./src",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true
12 |   },
13 |   "include": ["src/**/*"],
14 |   "exclude": ["node_modules"]
15 | }
16 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "jenkins-server",
 3 |   "version": "0.1.0",
 4 |   "description": "Jenkins Model Context Protocol server",
 5 |   "private": true,
 6 |   "type": "module",
 7 |   "bin": {
 8 |     "jenkins-server": "./build/index.js"
 9 |   },
10 |   "files": [
11 |     "build"
12 |   ],
13 |   "scripts": {
14 |     "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
15 |     "prepare": "npm run build",
16 |     "watch": "tsc --watch",
17 |     "inspector": "npx @modelcontextprotocol/inspector build/index.js"
18 |   },
19 |   "dependencies": {
20 |     "@modelcontextprotocol/sdk": "0.6.0",
21 |     "axios": "^1.7.9"
22 |   },
23 |   "devDependencies": {
24 |     "@types/node": "^20.11.24",
25 |     "typescript": "^5.3.3"
26 |   }
27 | }
28 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  4 | import {
  5 |   CallToolRequestSchema,
  6 |   ErrorCode,
  7 |   ListToolsRequestSchema,
  8 |   McpError,
  9 | } from '@modelcontextprotocol/sdk/types.js';
 10 | import axios from 'axios';
 11 | 
 12 | const JENKINS_URL = process.env.JENKINS_URL || '';
 13 | const JENKINS_USER = process.env.JENKINS_USER || '';
 14 | const JENKINS_TOKEN = process.env.JENKINS_TOKEN || '';
 15 | 
 16 | interface BuildStatus {
 17 |   building: boolean;
 18 |   result: string | null;
 19 |   timestamp: number;
 20 |   duration: number;
 21 |   url: string;
 22 | }
 23 | 
 24 | class JenkinsServer {
 25 |   private server: Server;
 26 |   private axiosInstance;
 27 | 
 28 |   constructor() {
 29 |     this.server = new Server(
 30 |       {
 31 |         name: 'jenkins-server',
 32 |         version: '0.1.0',
 33 |       },
 34 |       {
 35 |         capabilities: {
 36 |           tools: {},
 37 |         },
 38 |       }
 39 |     );
 40 | 
 41 |     this.axiosInstance = axios.create({
 42 |       baseURL: JENKINS_URL,
 43 |       auth: {
 44 |         username: JENKINS_USER,
 45 |         password: JENKINS_TOKEN,
 46 |       },
 47 |     });
 48 | 
 49 |     this.setupToolHandlers();
 50 |     
 51 |     // Error handling
 52 |     this.server.onerror = (error) => console.error('[MCP Error]', error);
 53 |     process.on('SIGINT', async () => {
 54 |       await this.server.close();
 55 |       process.exit(0);
 56 |     });
 57 |   }
 58 | 
 59 |   private setupToolHandlers() {
 60 |     this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
 61 |       tools: [
 62 |         {
 63 |           name: 'get_build_status',
 64 |           description: 'Get the status of a Jenkins build',
 65 |           inputSchema: {
 66 |             type: 'object',
 67 |             properties: {
 68 |               jobPath: {
 69 |                 type: 'string',
 70 |                 description: 'Path to the Jenkins job (e.g., "view/xxx_debug")',
 71 |               },
 72 |               buildNumber: {
 73 |                 type: 'string',
 74 |                 description: 'Build number (use "lastBuild" for most recent)',
 75 |               },
 76 |             },
 77 |             required: ['jobPath'],
 78 |           },
 79 |         },
 80 |         {
 81 |           name: 'trigger_build',
 82 |           description: 'Trigger a new Jenkins build',
 83 |           inputSchema: {
 84 |             type: 'object',
 85 |             properties: {
 86 |               jobPath: {
 87 |                 type: 'string',
 88 |                 description: 'Path to the Jenkins job',
 89 |               },
 90 |               parameters: {
 91 |                 type: 'object',
 92 |                 description: 'Build parameters (optional)',
 93 |                 additionalProperties: true,
 94 |               },
 95 |             },
 96 |             required: ['jobPath', 'parameters'],
 97 |           },
 98 |         },
 99 |         {
100 |           name: 'get_build_log',
101 |           description: 'Get the console output of a Jenkins build',
102 |           inputSchema: {
103 |             type: 'object',
104 |             properties: {
105 |               jobPath: {
106 |                 type: 'string',
107 |                 description: 'Path to the Jenkins job',
108 |               },
109 |               buildNumber: {
110 |                 type: 'string',
111 |                 description: 'Build number (use "lastBuild" for most recent)',
112 |               },
113 |             },
114 |             required: ['jobPath', 'buildNumber'],
115 |           },
116 |         },
117 |       ],
118 |     }));
119 | 
120 |     this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
121 |       try {
122 |         switch (request.params.name) {
123 |           case 'get_build_status':
124 |             return await this.getBuildStatus(request.params.arguments);
125 |           case 'trigger_build':
126 |             return await this.triggerBuild(request.params.arguments);
127 |           case 'get_build_log':
128 |             return await this.getBuildLog(request.params.arguments);
129 |           default:
130 |             throw new McpError(
131 |               ErrorCode.MethodNotFound,
132 |               `Unknown tool: ${request.params.name}`
133 |             );
134 |         }
135 |       } catch (error) {
136 |         if (error instanceof McpError) {
137 |           throw error;
138 |         }
139 |         if (axios.isAxiosError(error)) {
140 |           throw new McpError(
141 |             ErrorCode.InternalError,
142 |             `Jenkins API error: ${error.response?.data?.message || error.message}`
143 |           );
144 |         }
145 |         throw new McpError(ErrorCode.InternalError, 'Unknown error occurred');
146 |       }
147 |     });
148 |   }
149 | 
150 |   private async getBuildStatus(args: any) {
151 |     const buildNumber = args.buildNumber || 'lastBuild';
152 |     const response = await this.axiosInstance.get<BuildStatus>(
153 |       `/${args.jobPath}/${buildNumber}/api/json`
154 |     );
155 | 
156 |     return {
157 |       content: [
158 |         {
159 |           type: 'text',
160 |           text: JSON.stringify({
161 |             building: response.data.building,
162 |             result: response.data.result,
163 |             timestamp: response.data.timestamp,
164 |             duration: response.data.duration,
165 |             url: response.data.url,
166 |           }, null, 2),
167 |         },
168 |       ],
169 |     };
170 |   }
171 | 
172 |   private async triggerBuild(args: any) {
173 |     const params = new URLSearchParams();
174 |     if (args.parameters) {
175 |       Object.entries(args.parameters).forEach(([key, value]) => {
176 |         params.append(key, String(value));
177 |       });
178 |     }
179 | 
180 |     await this.axiosInstance.post(
181 |       `/${args.jobPath}/buildWithParameters`,
182 |       params
183 |     );
184 | 
185 |     return {
186 |       content: [
187 |         {
188 |           type: 'text',
189 |           text: 'Build triggered successfully',
190 |         },
191 |       ],
192 |     };
193 |   }
194 | 
195 |   private async getBuildLog(args: any) {
196 |     const response = await this.axiosInstance.get(
197 |       `/${args.jobPath}/${args.buildNumber}/consoleText`
198 |     );
199 | 
200 |     return {
201 |       content: [
202 |         {
203 |           type: 'text',
204 |           text: response.data,
205 |         },
206 |       ],
207 |     };
208 |   }
209 | 
210 |   async run() {
211 |     const transport = new StdioServerTransport();
212 |     await this.server.connect(transport);
213 |     console.error('Jenkins MCP server running on stdio');
214 |   }
215 | }
216 | 
217 | const server = new JenkinsServer();
218 | server.run().catch(console.error);
219 | 
```