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

```
├── .gitignore
├── CLINE_INSTALL_SCRIPT.md
├── CLINE_INSTALLATION.md
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── index.ts
│   ├── mcp-sdk
│   │   ├── index.js
│   │   ├── stdio.js
│   │   └── types.js
│   ├── server.ts
│   ├── tools
│   │   ├── files.ts
│   │   ├── issues.ts
│   │   ├── pull-requests.ts
│   │   ├── repository.ts
│   │   └── search.ts
│   ├── types
│   │   └── mcp-sdk.d.ts
│   └── utils
│       ├── error-handling.ts
│       ├── github-api.ts
│       └── validation.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
  1 | # Dependency directories
  2 | node_modules/
  3 | jspm_packages/
  4 | 
  5 | # Build outputs
  6 | dist/
  7 | build/
  8 | out/
  9 | *.tsbuildinfo
 10 | 
 11 | # Logs
 12 | logs
 13 | *.log
 14 | npm-debug.log*
 15 | yarn-debug.log*
 16 | yarn-error.log*
 17 | lerna-debug.log*
 18 | 
 19 | # Environment variables
 20 | .env
 21 | .env.local
 22 | .env.development.local
 23 | .env.test.local
 24 | .env.production.local
 25 | 
 26 | # IDE and editor files
 27 | .idea/
 28 | .vscode/
 29 | *.swp
 30 | *.swo
 31 | .DS_Store
 32 | .project
 33 | .classpath
 34 | .settings/
 35 | *.sublime-workspace
 36 | *.sublime-project
 37 | 
 38 | # Coverage directory used by tools like istanbul
 39 | coverage/
 40 | *.lcov
 41 | 
 42 | # nyc test coverage
 43 | .nyc_output
 44 | 
 45 | # TypeScript cache
 46 | *.tsbuildinfo
 47 | 
 48 | # Optional npm cache directory
 49 | .npm
 50 | 
 51 | # Optional eslint cache
 52 | .eslintcache
 53 | 
 54 | # Optional REPL history
 55 | .node_repl_history
 56 | 
 57 | # Output of 'npm pack'
 58 | *.tgz
 59 | 
 60 | # Yarn Integrity file
 61 | .yarn-integrity
 62 | 
 63 | # dotenv environment variable files
 64 | .env
 65 | .env.test
 66 | .env.local
 67 | 
 68 | # parcel-bundler cache
 69 | .cache
 70 | .parcel-cache
 71 | 
 72 | # Next.js build output
 73 | .next
 74 | out
 75 | 
 76 | # Nuxt.js build / generate output
 77 | .nuxt
 78 | dist
 79 | 
 80 | # Gatsby files
 81 | .cache/
 82 | public
 83 | 
 84 | # vuepress build output
 85 | .vuepress/dist
 86 | 
 87 | # Serverless directories
 88 | .serverless/
 89 | 
 90 | # FuseBox cache
 91 | .fusebox/
 92 | 
 93 | # DynamoDB Local files
 94 | .dynamodb/
 95 | 
 96 | # TernJS port file
 97 | .tern-port
 98 | 
 99 | # Stores VSCode versions used for testing VSCode extensions
100 | .vscode-test
101 | 
102 | # yarn v2
103 | .yarn/cache
104 | .yarn/unplugged
105 | .yarn/build-state.yml
106 | .yarn/install-state.gz
107 | .pnp.*
108 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # GitHub Enterprise MCP Server
  2 | 
  3 | MCP Server for the GitHub API, enabling file operations, repository management, search functionality, and more.
  4 | 
  5 | ## Features
  6 | 
  7 | - **Automatic Branch Creation**: When creating/updating files or pushing changes, branches are automatically created if they don't exist
  8 | - **Comprehensive Error Handling**: Clear error messages for common issues
  9 | - **Git History Preservation**: Operations maintain proper Git history without force pushing
 10 | - **Batch Operations**: Support for both single-file and multi-file operations
 11 | - **Advanced Search**: Support for searching code, issues/PRs, and users
 12 | 
 13 | ## Cline Installation Guide
 14 | 
 15 | This guide will help you install and configure the GitHub Enterprise MCP server in Cline, enabling you to use GitHub API functionality directly through Cline.
 16 | 
 17 | ### Prerequisites
 18 | 
 19 | 1. Node.js installed on your system
 20 | 2. A GitHub Personal Access Token with appropriate permissions
 21 | 3. Cline installed on your system
 22 | 
 23 | ### Installation Steps
 24 | 
 25 | #### 1. Clone the Repository
 26 | 
 27 | ```bash
 28 | git clone https://github.com/yourusername/github-enterprise-mcp.git
 29 | cd github-enterprise-mcp
 30 | ```
 31 | 
 32 | #### 2. Install Dependencies and Build
 33 | 
 34 | ```bash
 35 | npm install
 36 | npm run build
 37 | ```
 38 | 
 39 | This will create a `dist` directory with the compiled JavaScript files.
 40 | 
 41 | #### 3. Create a GitHub Personal Access Token
 42 | 
 43 | 1. Go to [GitHub Personal Access Tokens](https://github.com/settings/tokens) (in GitHub Settings > Developer settings)
 44 | 2. Click "Generate new token"
 45 | 3. Select which repositories you'd like this token to have access to (Public, All, or Select)
 46 | 4. Create a token with the `repo` scope ("Full control of private repositories")
 47 |    - Alternatively, if working only with public repositories, select only the `public_repo` scope
 48 | 5. Copy the generated token
 49 | 
 50 | #### 4. Configure Cline MCP Settings
 51 | 
 52 | ##### For Cline VS Code Extension
 53 | 
 54 | 1. Open VS Code
 55 | 2. Locate the Cline MCP settings file at:
 56 |    - Windows: `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`
 57 |    - macOS: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
 58 |    - Linux: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
 59 | 
 60 | 3. Add the GitHub Enterprise MCP server configuration to the `mcpServers` object:
 61 | 
 62 | ```json
 63 | {
 64 |   "mcpServers": {
 65 |     "github-enterprise": {
 66 |       "command": "node",
 67 |       "args": [
 68 |         "/absolute/path/to/github-enterprise-mcp/dist/index.js"
 69 |       ],
 70 |       "env": {
 71 |         "GITHUB_PERSONAL_ACCESS_TOKEN": "your-personal-access-token",
 72 |         "GITHUB_API_URL": "https://api.github.com" // For GitHub.com
 73 |         // For GitHub Enterprise, use your instance URL, e.g., "https://github.yourdomain.com/api/v3"
 74 |       },
 75 |       "disabled": false,
 76 |       "autoApprove": []
 77 |     }
 78 |   }
 79 | }
 80 | ```
 81 | 
 82 | Replace `/absolute/path/to/github-enterprise-mcp/dist/index.js` with the absolute path to the built index.js file.
 83 | 
 84 | ##### For Claude Desktop App
 85 | 
 86 | 1. Locate the Claude Desktop configuration file at:
 87 |    - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
 88 |    - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
 89 |    - Linux: `~/.config/Claude/claude_desktop_config.json`
 90 | 
 91 | 2. Add the GitHub Enterprise MCP server configuration to the `mcpServers` object:
 92 | 
 93 | ```json
 94 | {
 95 |   "mcpServers": {
 96 |     "github-enterprise": {
 97 |       "command": "node",
 98 |       "args": [
 99 |         "/absolute/path/to/github-enterprise-mcp/dist/index.js"
100 |       ],
101 |       "env": {
102 |         "GITHUB_PERSONAL_ACCESS_TOKEN": "your-personal-access-token",
103 |         "GITHUB_API_URL": "https://api.github.com" // For GitHub.com
104 |         // For GitHub Enterprise, use your instance URL, e.g., "https://github.yourdomain.com/api/v3"
105 |       },
106 |       "disabled": false,
107 |       "autoApprove": []
108 |     }
109 |   }
110 | }
111 | ```
112 | 
113 | #### 5. Restart Cline
114 | 
115 | After configuring the MCP settings, restart Cline (VS Code or Claude Desktop) to apply the changes.
116 | 
117 | ### Verification
118 | 
119 | To verify that the GitHub Enterprise MCP server is properly installed and configured, you can ask Cline to use one of the GitHub tools:
120 | 
121 | ```
122 | Can you search for repositories with the keyword "react" using the GitHub Enterprise MCP server?
123 | ```
124 | 
125 | Cline should be able to use the `search_repositories` tool to search for repositories with the keyword "react".
126 | 
127 | ### Interactive Installation with Cline
128 | 
129 | For an interactive installation experience, copy and paste the following text into Cline, and it will guide you through the installation process:
130 | 
131 | ```
132 | I want you to help me install the GitHub Enterprise MCP server. Here's what I need you to do:
133 | 
134 | 1. First, explain what the GitHub Enterprise MCP server is and what capabilities it will give you.
135 | 
136 | 2. Guide me through the installation process:
137 |    - Help me clone the repository (if I haven't already)
138 |    - Help me install dependencies and build the project
139 |    - Guide me through creating a GitHub Personal Access Token if I don't have one
140 |    - Help me configure the MCP settings in either VS Code or Claude Desktop
141 |    - Verify the installation is working
142 | 
143 | 3. Show me some examples of how I can use the GitHub Enterprise MCP server once it's installed.
144 | 
145 | Please start by explaining what the GitHub Enterprise MCP server is and what it will allow you to do for me.
146 | ```
147 | 
148 | ### Example Usage
149 | 
150 | After installation, you can ask Cline to perform GitHub operations directly, such as:
151 | 
152 | - "Create a new repository called 'my-project'"
153 | - "Search for repositories related to machine learning"
154 | - "Create a pull request from my feature branch to main"
155 | - "Get the contents of the README.md file in repository X"
156 | - "List open issues in my repository"
157 | 
158 | ### Troubleshooting
159 | 
160 | If you encounter issues with the GitHub Enterprise MCP server:
161 | 
162 | 1. Check that the path to the index.js file is correct in your MCP settings
163 | 2. Verify that your GitHub Personal Access Token has the necessary permissions
164 | 3. Ensure that the GitHub API URL is correct for your GitHub instance
165 | 4. Check the logs for any error messages
166 | 
167 | ### Additional Configuration Options
168 | 
169 | #### GitHub API Version
170 | 
171 | You can specify a GitHub API version by adding the `GITHUB_API_VERSION` environment variable:
172 | 
173 | ```json
174 | "env": {
175 |   "GITHUB_PERSONAL_ACCESS_TOKEN": "your-personal-access-token",
176 |   "GITHUB_API_URL": "https://api.github.com",
177 |   "GITHUB_API_VERSION": "2022-11-28"
178 | }
179 | ```
180 | 
181 | #### Enterprise Authentication
182 | 
183 | For GitHub Enterprise instances that use different authentication methods, you may need to provide additional configuration. Refer to the [Octokit documentation](https://github.com/octokit/rest.js) for more information.
184 | 
185 | ## Tools
186 | 
187 | 1. `create_or_update_file`
188 |    - Create or update a single file in a repository
189 |    - Inputs:
190 |      - `owner` (string): Repository owner (username or organization)
191 |      - `repo` (string): Repository name
192 |      - `path` (string): Path where to create/update the file
193 |      - `content` (string): Content of the file
194 |      - `message` (string): Commit message
195 |      - `branch` (string): Branch to create/update the file in
196 |      - `sha` (optional string): SHA of file being replaced (for updates)
197 |    - Returns: File content and commit details
198 | 
199 | 2. `push_files`
200 |    - Push multiple files in a single commit
201 |    - Inputs:
202 |      - `owner` (string): Repository owner
203 |      - `repo` (string): Repository name
204 |      - `branch` (string): Branch to push to
205 |      - `files` (array): Files to push, each with `path` and `content`
206 |      - `message` (string): Commit message
207 |    - Returns: Updated branch reference
208 | 
209 | 3. `search_repositories`
210 |    - Search for GitHub repositories
211 |    - Inputs:
212 |      - `query` (string): Search query
213 |      - `page` (optional number): Page number for pagination
214 |      - `perPage` (optional number): Results per page (max 100)
215 |    - Returns: Repository search results
216 | 
217 | 4. `create_repository`
218 |    - Create a new GitHub repository
219 |    - Inputs:
220 |      - `name` (string): Repository name
221 |      - `description` (optional string): Repository description
222 |      - `private` (optional boolean): Whether repo should be private
223 |      - `autoInit` (optional boolean): Initialize with README
224 |    - Returns: Created repository details
225 | 
226 | 5. `get_file_contents`
227 |    - Get contents of a file or directory
228 |    - Inputs:
229 |      - `owner` (string): Repository owner
230 |      - `repo` (string): Repository name
231 |      - `path` (string): Path to file/directory
232 |      - `branch` (optional string): Branch to get contents from
233 |    - Returns: File/directory contents
234 | 
235 | 6. `create_issue`
236 |    - Create a new issue
237 |    - Inputs:
238 |      - `owner` (string): Repository owner
239 |      - `repo` (string): Repository name
240 |      - `title` (string): Issue title
241 |      - `body` (optional string): Issue description
242 |      - `assignees` (optional string[]): Usernames to assign
243 |      - `labels` (optional string[]): Labels to add
244 |      - `milestone` (optional number): Milestone number
245 |    - Returns: Created issue details
246 | 
247 | 7. `create_pull_request`
248 |    - Create a new pull request
249 |    - Inputs:
250 |      - `owner` (string): Repository owner
251 |      - `repo` (string): Repository name
252 |      - `title` (string): PR title
253 |      - `body` (optional string): PR description
254 |      - `head` (string): Branch containing changes
255 |      - `base` (string): Branch to merge into
256 |      - `draft` (optional boolean): Create as draft PR
257 |      - `maintainer_can_modify` (optional boolean): Allow maintainer edits
258 |    - Returns: Created pull request details
259 | 
260 | 8. `fork_repository`
261 |    - Fork a repository
262 |    - Inputs:
263 |      - `owner` (string): Repository owner
264 |      - `repo` (string): Repository name
265 |      - `organization` (optional string): Organization to fork to
266 |    - Returns: Forked repository details
267 | 
268 | 9. `create_branch`
269 |    - Create a new branch
270 |    - Inputs:
271 |      - `owner` (string): Repository owner
272 |      - `repo` (string): Repository name
273 |      - `branch` (string): Name for new branch
274 |      - `from_branch` (optional string): Source branch (defaults to repo default)
275 |    - Returns: Created branch reference
276 | 
277 | 10. `list_issues`
278 |     - List and filter repository issues
279 |     - Inputs:
280 |       - `owner` (string): Repository owner
281 |       - `repo` (string): Repository name
282 |       - `state` (optional string): Filter by state ('open', 'closed', 'all')
283 |       - `labels` (optional string[]): Filter by labels
284 |       - `sort` (optional string): Sort by ('created', 'updated', 'comments')
285 |       - `direction` (optional string): Sort direction ('asc', 'desc')
286 |       - `since` (optional string): Filter by date (ISO 8601 timestamp)
287 |       - `page` (optional number): Page number
288 |       - `per_page` (optional number): Results per page
289 |     - Returns: Array of issue details
290 | 
291 | 11. `update_issue`
292 |     - Update an existing issue
293 |     - Inputs:
294 |       - `owner` (string): Repository owner
295 |       - `repo` (string): Repository name
296 |       - `issue_number` (number): Issue number to update
297 |       - `title` (optional string): New title
298 |       - `body` (optional string): New description
299 |       - `state` (optional string): New state ('open' or 'closed')
300 |       - `labels` (optional string[]): New labels
301 |       - `assignees` (optional string[]): New assignees
302 |       - `milestone` (optional number): New milestone number
303 |     - Returns: Updated issue details
304 | 
305 | 12. `add_issue_comment`
306 |     - Add a comment to an issue
307 |     - Inputs:
308 |       - `owner` (string): Repository owner
309 |       - `repo` (string): Repository name
310 |       - `issue_number` (number): Issue number to comment on
311 |       - `body` (string): Comment text
312 |     - Returns: Created comment details
313 | 
314 | 13. `search_code`
315 |     - Search for code across GitHub repositories
316 |     - Inputs:
317 |       - `q` (string): Search query using GitHub code search syntax
318 |       - `sort` (optional string): Sort field ('indexed' only)
319 |       - `order` (optional string): Sort order ('asc' or 'desc')
320 |       - `per_page` (optional number): Results per page (max 100)
321 |       - `page` (optional number): Page number
322 |     - Returns: Code search results with repository context
323 | 
324 | 14. `search_issues`
325 |     - Search for issues and pull requests
326 |     - Inputs:
327 |       - `q` (string): Search query using GitHub issues search syntax
328 |       - `sort` (optional string): Sort field (comments, reactions, created, etc.)
329 |       - `order` (optional string): Sort order ('asc' or 'desc')
330 |       - `per_page` (optional number): Results per page (max 100)
331 |       - `page` (optional number): Page number
332 |     - Returns: Issue and pull request search results
333 | 
334 | 15. `search_users`
335 |     - Search for GitHub users
336 |     - Inputs:
337 |       - `q` (string): Search query using GitHub users search syntax
338 |       - `sort` (optional string): Sort field (followers, repositories, joined)
339 |       - `order` (optional string): Sort order ('asc' or 'desc')
340 |       - `per_page` (optional number): Results per page (max 100)
341 |       - `page` (optional number): Page number
342 |     - Returns: User search results
343 | 
344 | 16. `list_commits`
345 |    - Gets commits of a branch in a repository
346 |    - Inputs:
347 |      - `owner` (string): Repository owner
348 |      - `repo` (string): Repository name
349 |      - `page` (optional string): page number
350 |      - `per_page` (optional string): number of record per page
351 |      - `sha` (optional string): branch name
352 |    - Returns: List of commits
353 | 
354 | 17. `get_issue`
355 |    - Gets the contents of an issue within a repository
356 |    - Inputs:
357 |      - `owner` (string): Repository owner
358 |      - `repo` (string): Repository name
359 |      - `issue_number` (number): Issue number to retrieve
360 |    - Returns: Github Issue object & details
361 | 
362 | 18. `get_pull_request`
363 |    - Get details of a specific pull request
364 |    - Inputs:
365 |      - `owner` (string): Repository owner
366 |      - `repo` (string): Repository name
367 |      - `pull_number` (number): Pull request number
368 |    - Returns: Pull request details including diff and review status
369 | 
370 | 19. `list_pull_requests`
371 |    - List and filter repository pull requests
372 |    - Inputs:
373 |      - `owner` (string): Repository owner
374 |      - `repo` (string): Repository name
375 |      - `state` (optional string): Filter by state ('open', 'closed', 'all')
376 |      - `head` (optional string): Filter by head user/org and branch
377 |      - `base` (optional string): Filter by base branch
378 |      - `sort` (optional string): Sort by ('created', 'updated', 'popularity', 'long-running')
379 |      - `direction` (optional string): Sort direction ('asc', 'desc')
380 |      - `per_page` (optional number): Results per page (max 100)
381 |      - `page` (optional number): Page number
382 |    - Returns: Array of pull request details
383 | 
384 | 20. `create_pull_request_review`
385 |    - Create a review on a pull request
386 |    - Inputs:
387 |      - `owner` (string): Repository owner
388 |      - `repo` (string): Repository name
389 |      - `pull_number` (number): Pull request number
390 |      - `body` (string): Review comment text
391 |      - `event` (string): Review action ('APPROVE', 'REQUEST_CHANGES', 'COMMENT')
392 |      - `commit_id` (optional string): SHA of commit to review
393 |      - `comments` (optional array): Line-specific comments, each with:
394 |        - `path` (string): File path
395 |        - `position` (number): Line position in diff
396 |        - `body` (string): Comment text
397 |    - Returns: Created review details
398 | 
399 | 21. `merge_pull_request`
400 |    - Merge a pull request
401 |    - Inputs:
402 |      - `owner` (string): Repository owner
403 |      - `repo` (string): Repository name
404 |      - `pull_number` (number): Pull request number
405 |      - `commit_title` (optional string): Title for merge commit
406 |      - `commit_message` (optional string): Extra detail for merge commit
407 |      - `merge_method` (optional string): Merge method ('merge', 'squash', 'rebase')
408 |    - Returns: Merge result details
409 | 
410 | 22. `get_pull_request_files`
411 |    - Get the list of files changed in a pull request
412 |    - Inputs:
413 |      - `owner` (string): Repository owner
414 |      - `repo` (string): Repository name
415 |      - `pull_number` (number): Pull request number
416 |    - Returns: Array of changed files with patch and status details
417 | 
418 | 23. `get_pull_request_status`
419 |    - Get the combined status of all status checks for a pull request
420 |    - Inputs:
421 |      - `owner` (string): Repository owner
422 |      - `repo` (string): Repository name
423 |      - `pull_number` (number): Pull request number
424 |    - Returns: Combined status check results and individual check details
425 | 
426 | 24. `update_pull_request_branch`
427 |    - Update a pull request branch with the latest changes from the base branch (equivalent to GitHub's "Update branch" button)
428 |    - Inputs:
429 |      - `owner` (string): Repository owner
430 |      - `repo` (string): Repository name
431 |      - `pull_number` (number): Pull request number
432 |      - `expected_head_sha` (optional string): The expected SHA of the pull request's HEAD ref
433 |    - Returns: Success message when branch is updated
434 | 
435 | 25. `get_pull_request_comments`
436 |    - Get the review comments on a pull request
437 |    - Inputs:
438 |      - `owner` (string): Repository owner
439 |      - `repo` (string): Repository name
440 |      - `pull_number` (number): Pull request number
441 |    - Returns: Array of pull request review comments with details like the comment text, author, and location in the diff
442 | 
443 | 26. `get_pull_request_reviews`
444 |    - Get the reviews on a pull request
445 |    - Inputs:
446 |      - `owner` (string): Repository owner
447 |      - `repo` (string): Repository name
448 |      - `pull_number` (number): Pull request number
449 |    - Returns: Array of pull request reviews with details like the review state (APPROVED, CHANGES_REQUESTED, etc.), reviewer, and review body
450 | 
451 | ## Search Query Syntax
452 | 
453 | ### Code Search
454 | - `language:javascript`: Search by programming language
455 | - `repo:owner/name`: Search in specific repository
456 | - `path:app/src`: Search in specific path
457 | - `extension:js`: Search by file extension
458 | - Example: `q: "import express" language:typescript path:src/`
459 | 
460 | ### Issues Search
461 | - `is:issue` or `is:pr`: Filter by type
462 | - `is:open` or `is:closed`: Filter by state
463 | - `label:bug`: Search by label
464 | - `author:username`: Search by author
465 | - Example: `q: "memory leak" is:issue is:open label:bug`
466 | 
467 | ### Users Search
468 | - `type:user` or `type:org`: Filter by account type
469 | - `followers:>1000`: Filter by followers
470 | - `location:London`: Search by location
471 | - Example: `q: "fullstack developer" location:London followers:>100`
472 | 
473 | For detailed search syntax, see [GitHub's searching documentation](https://docs.github.com/en/search-github/searching-on-github).
474 | 
475 | ## Setup
476 | 
477 | ### Personal Access Token
478 | [Create a GitHub Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with appropriate permissions:
479 |    - Go to [Personal access tokens](https://github.com/settings/tokens) (in GitHub Settings > Developer settings)
480 |    - Select which repositories you'd like this token to have access to (Public, All, or Select)
481 |    - Create a token with the `repo` scope ("Full control of private repositories")
482 |      - Alternatively, if working only with public repositories, select only the `public_repo` scope
483 |    - Copy the generated token
484 | 
485 | ### Usage with Claude Desktop
486 | To use this with Claude Desktop, add the following to your `claude_desktop_config.json`:
487 | 
488 | ```json
489 | {
490 |   "mcpServers": {
491 |     "github": {
492 |       "command": "node",
493 |       "args": [
494 |         "/path/to/github-enterprise-mcp/dist/index.js"
495 |       ],
496 |       "env": {
497 |         "GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"
498 |       }
499 |     }
500 |   }
501 | }
502 | ```
503 | 
504 | ## Build
505 | 
506 | ```bash
507 | # Install dependencies
508 | npm install
509 | 
510 | # Build the project
511 | npm run build
512 | 
513 | # Start the server
514 | npm start
515 | ```
516 | 
517 | ## License
518 | 
519 | This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.
520 | 
```

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

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "esModuleInterop": true,
 7 |     "strict": true,
 8 |     "outDir": "dist",
 9 |     "sourceMap": true,
10 |     "declaration": true,
11 |     "resolveJsonModule": true,
12 |     "skipLibCheck": true,
13 |     "forceConsistentCasingInFileNames": true
14 |   },
15 |   "include": ["src/**/*"],
16 |   "exclude": ["node_modules", "dist", "**/*.test.ts"]
17 | }
18 | 
```

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

```typescript
 1 | #!/usr/bin/env node
 2 | 
 3 | import { GitHubEnterpriseServer } from './server.js';
 4 | 
 5 | /**
 6 |  * Main entry point for the GitHub Enterprise MCP server
 7 |  */
 8 | async function main(): Promise<void> {
 9 |   try {
10 |     const server = new GitHubEnterpriseServer();
11 |     await server.run();
12 |   } catch (error) {
13 |     console.error('Failed to start GitHub Enterprise MCP server:', error);
14 |     process.exit(1);
15 |   }
16 | }
17 | 
18 | // Start the server
19 | main().catch((error) => {
20 |   console.error('Unhandled error:', error);
21 |   process.exit(1);
22 | });
23 | 
```

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

```json
 1 | {
 2 |   "name": "github-enterprise-mcp",
 3 |   "version": "1.0.0",
 4 |   "description": "MCP Server for GitHub Enterprise API",
 5 |   "main": "dist/index.js",
 6 |   "type": "module",
 7 |   "scripts": {
 8 |     "build": "tsc",
 9 |     "start": "node dist/index.js",
10 |     "dev": "ts-node-esm src/index.ts",
11 |     "test": "jest"
12 |   },
13 |   "keywords": [
14 |     "mcp",
15 |     "github",
16 |     "github-enterprise",
17 |     "api"
18 |   ],
19 |   "author": "",
20 |   "license": "MIT",
21 |   "dependencies": {
22 |     "@modelcontextprotocol/sdk": "^1.8.0",
23 |     "@octokit/rest": "^20.0.2",
24 |     "axios": "^1.6.2",
25 |     "zod": "^3.22.4"
26 |   },
27 |   "devDependencies": {
28 |     "@types/jest": "^29.5.10",
29 |     "@types/node": "^20.10.0",
30 |     "jest": "^29.7.0",
31 |     "ts-jest": "^29.1.1",
32 |     "ts-node": "^10.9.1",
33 |     "typescript": "^5.3.2"
34 |   }
35 | }
36 | 
```

--------------------------------------------------------------------------------
/src/mcp-sdk/types.js:
--------------------------------------------------------------------------------

```javascript
 1 | /**
 2 |  * This is a minimal implementation of the MCP SDK types for the GitHub Enterprise MCP server.
 3 |  * In a real-world scenario, you would use the actual @modelcontextprotocol/sdk package.
 4 |  */
 5 | 
 6 | export const ErrorCode = {
 7 |   ParseError: -32700,
 8 |   InvalidRequest: -32600,
 9 |   MethodNotFound: -32601,
10 |   InvalidParams: -32602,
11 |   InternalError: -32603,
12 |   ConfigurationError: -32000,
13 |   Unauthorized: -32001,
14 |   Forbidden: -32003,
15 |   NotFound: -32004,
16 | };
17 | 
18 | export class McpError extends Error {
19 |   constructor(code, message) {
20 |     super(message);
21 |     this.code = code;
22 |     this.name = 'McpError';
23 |   }
24 | }
25 | 
26 | // Schema constants
27 | export const CallToolRequestSchema = { id: 'call_tool' };
28 | export const ListToolsRequestSchema = { id: 'list_tools' };
29 | export const ListResourcesRequestSchema = { id: 'list_resources' };
30 | export const ListResourceTemplatesRequestSchema = { id: 'list_resource_templates' };
31 | export const ReadResourceRequestSchema = { id: 'read_resource' };
32 | 
```

--------------------------------------------------------------------------------
/src/types/mcp-sdk.d.ts:
--------------------------------------------------------------------------------

```typescript
 1 | declare module '@modelcontextprotocol/sdk/server/index.js' {
 2 |   export class Server {
 3 |     constructor(
 4 |       info: { name: string; version: string },
 5 |       options: { capabilities: { tools: Record<string, unknown> } }
 6 |     );
 7 |     
 8 |     onerror: (error: any) => void;
 9 |     
10 |     setRequestHandler<T>(schema: any, handler: (request: any) => Promise<T>): void;
11 |     
12 |     connect(transport: any): Promise<void>;
13 |     
14 |     close(): Promise<void>;
15 |   }
16 | }
17 | 
18 | declare module '@modelcontextprotocol/sdk/server/stdio.js' {
19 |   export class StdioServerTransport {
20 |     constructor();
21 |   }
22 | }
23 | 
24 | declare module '@modelcontextprotocol/sdk/types.js' {
25 |   export enum ErrorCode {
26 |     ParseError = -32700,
27 |     InvalidRequest = -32600,
28 |     MethodNotFound = -32601,
29 |     InvalidParams = -32602,
30 |     InternalError = -32603,
31 |     ConfigurationError = -32000,
32 |     Unauthorized = -32001,
33 |     Forbidden = -32003,
34 |     NotFound = -32004,
35 |   }
36 |   
37 |   export class McpError extends Error {
38 |     constructor(code: ErrorCode, message: string);
39 |     code: ErrorCode;
40 |   }
41 |   
42 |   export const CallToolRequestSchema: any;
43 |   export const ListToolsRequestSchema: any;
44 |   export const ListResourcesRequestSchema: any;
45 |   export const ListResourceTemplatesRequestSchema: any;
46 |   export const ReadResourceRequestSchema: any;
47 | }
48 | 
```

--------------------------------------------------------------------------------
/src/mcp-sdk/stdio.js:
--------------------------------------------------------------------------------

```javascript
 1 | /**
 2 |  * This is a minimal implementation of the MCP SDK stdio transport for the GitHub Enterprise MCP server.
 3 |  * In a real-world scenario, you would use the actual @modelcontextprotocol/sdk package.
 4 |  */
 5 | 
 6 | export class StdioServerTransport {
 7 |   constructor() {
 8 |     this.onRequest = null;
 9 |     
10 |     // Set up stdin/stdout handling
11 |     process.stdin.on('data', (data) => {
12 |       try {
13 |         const request = JSON.parse(data.toString());
14 |         if (this.onRequest) {
15 |           this.onRequest(request)
16 |             .then((result) => {
17 |               const response = {
18 |                 jsonrpc: '2.0',
19 |                 id: request.id,
20 |                 result,
21 |               };
22 |               process.stdout.write(JSON.stringify(response) + '\n');
23 |             })
24 |             .catch((error) => {
25 |               const response = {
26 |                 jsonrpc: '2.0',
27 |                 id: request.id,
28 |                 error: {
29 |                   code: error.code || -32603, // Internal error
30 |                   message: error.message || 'Unknown error',
31 |                 },
32 |               };
33 |               process.stdout.write(JSON.stringify(response) + '\n');
34 |             });
35 |         }
36 |       } catch (error) {
37 |         console.error('Error processing request:', error);
38 |       }
39 |     });
40 |   }
41 | }
42 | 
```

--------------------------------------------------------------------------------
/CLINE_INSTALL_SCRIPT.md:
--------------------------------------------------------------------------------

```markdown
 1 | # GitHub Enterprise MCP Installation Script for Cline
 2 | 
 3 | Copy and paste the following text into Cline to have it guide you through installing the GitHub Enterprise MCP server:
 4 | 
 5 | ```
 6 | I want you to help me install the GitHub Enterprise MCP server. Here's what I need you to do:
 7 | 
 8 | 1. First, explain what the GitHub Enterprise MCP server is and what capabilities it will give you.
 9 | 
10 | 2. Guide me through the installation process:
11 |    - Help me clone the repository (if I haven't already)
12 |    - Help me install dependencies and build the project
13 |    - Guide me through creating a GitHub Personal Access Token if I don't have one
14 |    - Help me configure the MCP settings in either VS Code or Claude Desktop
15 |    - Verify the installation is working
16 | 
17 | 3. Show me some examples of how I can use the GitHub Enterprise MCP server once it's installed.
18 | 
19 | Please start by explaining what the GitHub Enterprise MCP server is and what it will allow you to do for me.
20 | ```
21 | 
22 | After Cline helps you install the GitHub Enterprise MCP server, you can ask it to perform GitHub operations directly, such as:
23 | 
24 | - "Create a new repository called 'my-project'"
25 | - "Search for repositories related to machine learning"
26 | - "Create a pull request from my feature branch to main"
27 | - "Get the contents of the README.md file in repository X"
28 | - "List open issues in my repository"
29 | 
30 | The GitHub Enterprise MCP server enables Cline to interact with GitHub on your behalf, making it easier to manage your GitHub repositories, issues, pull requests, and more without leaving your Cline environment.
31 | 
```

--------------------------------------------------------------------------------
/src/utils/error-handling.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  2 | 
  3 | /**
  4 |  * Safely parse JSON with error handling
  5 |  * @param input JSON string to parse
  6 |  * @param errorMessage Custom error message
  7 |  * @returns Parsed JSON object
  8 |  */
  9 | export function safeJsonParse(input: string, errorMessage = 'Invalid JSON'): any {
 10 |   try {
 11 |     return JSON.parse(input);
 12 |   } catch (error) {
 13 |     throw new McpError(ErrorCode.InvalidParams, `${errorMessage}: ${(error as Error).message}`);
 14 |   }
 15 | }
 16 | 
 17 | /**
 18 |  * Safely stringify JSON with error handling
 19 |  * @param input Object to stringify
 20 |  * @param errorMessage Custom error message
 21 |  * @returns JSON string
 22 |  */
 23 | export function safeJsonStringify(input: any, errorMessage = 'Failed to stringify object'): string {
 24 |   try {
 25 |     return JSON.stringify(input);
 26 |   } catch (error) {
 27 |     throw new McpError(ErrorCode.InternalError, `${errorMessage}: ${(error as Error).message}`);
 28 |   }
 29 | }
 30 | 
 31 | /**
 32 |  * Wrap async function execution with error handling
 33 |  * @param fn Async function to execute
 34 |  * @param errorMessage Custom error message
 35 |  * @returns Result of the function
 36 |  */
 37 | export async function tryCatchAsync<T>(
 38 |   fn: () => Promise<T>,
 39 |   errorMessage = 'Operation failed'
 40 | ): Promise<T> {
 41 |   try {
 42 |     return await fn();
 43 |   } catch (error) {
 44 |     if (error instanceof McpError) {
 45 |       throw error;
 46 |     }
 47 |     throw new McpError(
 48 |       ErrorCode.InternalError,
 49 |       `${errorMessage}: ${(error as Error).message}`
 50 |     );
 51 |   }
 52 | }
 53 | 
 54 | /**
 55 |  * Wrap synchronous function execution with error handling
 56 |  * @param fn Function to execute
 57 |  * @param errorMessage Custom error message
 58 |  * @returns Result of the function
 59 |  */
 60 | export function tryCatch<T>(fn: () => T, errorMessage = 'Operation failed'): T {
 61 |   try {
 62 |     return fn();
 63 |   } catch (error) {
 64 |     if (error instanceof McpError) {
 65 |       throw error;
 66 |     }
 67 |     throw new McpError(
 68 |       ErrorCode.InternalError,
 69 |       `${errorMessage}: ${(error as Error).message}`
 70 |     );
 71 |   }
 72 | }
 73 | 
 74 | /**
 75 |  * Convert a base64 string to UTF-8
 76 |  * @param base64 Base64 string
 77 |  * @returns UTF-8 string
 78 |  */
 79 | export function base64ToUtf8(base64: string): string {
 80 |   try {
 81 |     return Buffer.from(base64, 'base64').toString('utf-8');
 82 |   } catch (error) {
 83 |     throw new McpError(
 84 |       ErrorCode.InternalError,
 85 |       `Failed to decode base64 string: ${(error as Error).message}`
 86 |     );
 87 |   }
 88 | }
 89 | 
 90 | /**
 91 |  * Convert a UTF-8 string to base64
 92 |  * @param utf8 UTF-8 string
 93 |  * @returns Base64 string
 94 |  */
 95 | export function utf8ToBase64(utf8: string): string {
 96 |   try {
 97 |     return Buffer.from(utf8, 'utf-8').toString('base64');
 98 |   } catch (error) {
 99 |     throw new McpError(
100 |       ErrorCode.InternalError,
101 |       `Failed to encode string to base64: ${(error as Error).message}`
102 |     );
103 |   }
104 | }
105 | 
106 | /**
107 |  * Validate required environment variables
108 |  * @param variables Array of required environment variable names
109 |  * @throws McpError if any required variable is missing
110 |  */
111 | export function validateEnvVariables(variables: string[]): void {
112 |   const missing = variables.filter(variable => !process.env[variable]);
113 |   if (missing.length > 0) {
114 |     throw new McpError(
115 |       ErrorCode.ConfigurationError,
116 |       `Missing required environment variables: ${missing.join(', ')}`
117 |     );
118 |   }
119 | }
120 | 
```

--------------------------------------------------------------------------------
/src/mcp-sdk/index.js:
--------------------------------------------------------------------------------

```javascript
  1 | /**
  2 |  * This is a minimal implementation of the MCP SDK for the GitHub Enterprise MCP server.
  3 |  * In a real-world scenario, you would use the actual @modelcontextprotocol/sdk package.
  4 |  */
  5 | 
  6 | export class Server {
  7 |   constructor(info, options) {
  8 |     this.info = info;
  9 |     this.options = options;
 10 |     this.handlers = new Map();
 11 |   }
 12 | 
 13 |   onerror = (error) => {
 14 |     console.error('Server error:', error);
 15 |   };
 16 | 
 17 |   setRequestHandler(schema, handler) {
 18 |     const schemaId = schema.id || schema.name || 'unknown';
 19 |     this.handlers.set(schemaId, handler);
 20 |   }
 21 | 
 22 |   async connect(transport) {
 23 |     this.transport = transport;
 24 |     console.error(`MCP Server ${this.info.name} v${this.info.version} connected`);
 25 |     
 26 |     // Set up transport
 27 |     if (this.transport) {
 28 |       this.transport.onRequest = async (request) => {
 29 |         try {
 30 |           // Find handler for method
 31 |           const handler = this.handlers.get(request.method) || this.handlers.get('unknown');
 32 |           if (!handler) {
 33 |             throw new McpError(ErrorCode.MethodNotFound, `Method not found: ${request.method}`);
 34 |           }
 35 |           
 36 |           // Call handler
 37 |           const result = await handler({ params: request.params });
 38 |           return result;
 39 |         } catch (error) {
 40 |           this.onerror(error);
 41 |           throw error;
 42 |         }
 43 |       };
 44 |     }
 45 |   }
 46 | 
 47 |   async close() {
 48 |     console.error(`MCP Server ${this.info.name} v${this.info.version} closed`);
 49 |   }
 50 | }
 51 | 
 52 | export class StdioServerTransport {
 53 |   constructor() {
 54 |     this.onRequest = null;
 55 |     
 56 |     // Set up stdin/stdout handling
 57 |     process.stdin.on('data', (data) => {
 58 |       try {
 59 |         const request = JSON.parse(data.toString());
 60 |         if (this.onRequest) {
 61 |           this.onRequest(request)
 62 |             .then((result) => {
 63 |               const response = {
 64 |                 jsonrpc: '2.0',
 65 |                 id: request.id,
 66 |                 result,
 67 |               };
 68 |               process.stdout.write(JSON.stringify(response) + '\n');
 69 |             })
 70 |             .catch((error) => {
 71 |               const response = {
 72 |                 jsonrpc: '2.0',
 73 |                 id: request.id,
 74 |                 error: {
 75 |                   code: error.code || ErrorCode.InternalError,
 76 |                   message: error.message || 'Unknown error',
 77 |                 },
 78 |               };
 79 |               process.stdout.write(JSON.stringify(response) + '\n');
 80 |             });
 81 |         }
 82 |       } catch (error) {
 83 |         console.error('Error processing request:', error);
 84 |       }
 85 |     });
 86 |   }
 87 | }
 88 | 
 89 | export const ErrorCode = {
 90 |   ParseError: -32700,
 91 |   InvalidRequest: -32600,
 92 |   MethodNotFound: -32601,
 93 |   InvalidParams: -32602,
 94 |   InternalError: -32603,
 95 |   ConfigurationError: -32000,
 96 |   Unauthorized: -32001,
 97 |   Forbidden: -32003,
 98 |   NotFound: -32004,
 99 | };
100 | 
101 | export class McpError extends Error {
102 |   constructor(code, message) {
103 |     super(message);
104 |     this.code = code;
105 |     this.name = 'McpError';
106 |   }
107 | }
108 | 
109 | // Schema constants
110 | export const CallToolRequestSchema = { id: 'call_tool' };
111 | export const ListToolsRequestSchema = { id: 'list_tools' };
112 | export const ListResourcesRequestSchema = { id: 'list_resources' };
113 | export const ListResourceTemplatesRequestSchema = { id: 'list_resource_templates' };
114 | export const ReadResourceRequestSchema = { id: 'read_resource' };
115 | 
```

--------------------------------------------------------------------------------
/src/utils/github-api.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Octokit } from '@octokit/rest';
  2 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  3 | 
  4 | /**
  5 |  * GitHub API client wrapper
  6 |  */
  7 | export class GitHubApi {
  8 |   private octokit: Octokit;
  9 |   private baseUrl?: string;
 10 | 
 11 |   /**
 12 |    * Create a new GitHub API client
 13 |    * @param token GitHub Personal Access Token
 14 |    * @param baseUrl Optional base URL for GitHub Enterprise
 15 |    */
 16 |   constructor(token: string, baseUrl?: string) {
 17 |     if (!token) {
 18 |       throw new McpError(
 19 |         ErrorCode.ConfigurationError,
 20 |         'GitHub Personal Access Token is required'
 21 |       );
 22 |     }
 23 | 
 24 |     this.baseUrl = baseUrl;
 25 |     this.octokit = new Octokit({
 26 |       auth: token,
 27 |       ...(baseUrl ? { baseUrl } : {}),
 28 |     });
 29 |   }
 30 | 
 31 |   /**
 32 |    * Get the Octokit instance
 33 |    */
 34 |   getOctokit(): Octokit {
 35 |     return this.octokit;
 36 |   }
 37 | 
 38 |   /**
 39 |    * Check if a branch exists in a repository
 40 |    */
 41 |   async branchExists(owner: string, repo: string, branch: string): Promise<boolean> {
 42 |     try {
 43 |       await this.octokit.git.getRef({
 44 |         owner,
 45 |         repo,
 46 |         ref: `heads/${branch}`,
 47 |       });
 48 |       return true;
 49 |     } catch (error: any) {
 50 |       if (error.status === 404) {
 51 |         return false;
 52 |       }
 53 |       throw this.handleApiError(error);
 54 |     }
 55 |   }
 56 | 
 57 |   /**
 58 |    * Create a new branch in a repository
 59 |    */
 60 |   async createBranch(
 61 |     owner: string,
 62 |     repo: string,
 63 |     branch: string,
 64 |     fromBranch?: string
 65 |   ): Promise<any> {
 66 |     try {
 67 |       // Get the SHA of the latest commit on the default branch
 68 |       const defaultBranch = fromBranch || (await this.getDefaultBranch(owner, repo));
 69 |       const { data: refData } = await this.octokit.git.getRef({
 70 |         owner,
 71 |         repo,
 72 |         ref: `heads/${defaultBranch}`,
 73 |       });
 74 | 
 75 |       // Create a new branch from the default branch
 76 |       const { data } = await this.octokit.git.createRef({
 77 |         owner,
 78 |         repo,
 79 |         ref: `refs/heads/${branch}`,
 80 |         sha: refData.object.sha,
 81 |       });
 82 | 
 83 |       return data;
 84 |     } catch (error: any) {
 85 |       throw this.handleApiError(error);
 86 |     }
 87 |   }
 88 | 
 89 |   /**
 90 |    * Get the default branch of a repository
 91 |    */
 92 |   async getDefaultBranch(owner: string, repo: string): Promise<string> {
 93 |     try {
 94 |       const { data } = await this.octokit.repos.get({
 95 |         owner,
 96 |         repo,
 97 |       });
 98 |       return data.default_branch;
 99 |     } catch (error: any) {
100 |       throw this.handleApiError(error);
101 |     }
102 |   }
103 | 
104 |   /**
105 |    * Handle GitHub API errors
106 |    */
107 |   handleApiError(error: any): Error {
108 |     if (error instanceof McpError) {
109 |       return error;
110 |     }
111 | 
112 |     const status = error.status || 500;
113 |     const message = error.message || 'Unknown GitHub API error';
114 |     const response = error.response?.data?.message || '';
115 | 
116 |     // Map HTTP status codes to MCP error codes
117 |     let errorCode: ErrorCode;
118 |     switch (status) {
119 |       case 400:
120 |         errorCode = ErrorCode.InvalidParams;
121 |         break;
122 |       case 401:
123 |         errorCode = ErrorCode.Unauthorized;
124 |         break;
125 |       case 403:
126 |         errorCode = ErrorCode.Forbidden;
127 |         break;
128 |       case 404:
129 |         errorCode = ErrorCode.NotFound;
130 |         break;
131 |       case 422:
132 |         errorCode = ErrorCode.InvalidParams;
133 |         break;
134 |       default:
135 |         errorCode = ErrorCode.InternalError;
136 |     }
137 | 
138 |     return new McpError(
139 |       errorCode,
140 |       `GitHub API Error: ${message}${response ? ` - ${response}` : ''}`
141 |     );
142 |   }
143 | }
144 | 
145 | // Create a singleton instance
146 | let githubApiInstance: GitHubApi | null = null;
147 | 
148 | /**
149 |  * Get the GitHub API instance
150 |  */
151 | export function getGitHubApi(): GitHubApi {
152 |   if (!githubApiInstance) {
153 |     const token = process.env.GITHUB_PERSONAL_ACCESS_TOKEN;
154 |     const baseUrl = process.env.GITHUB_API_URL;
155 |     
156 |     if (!token) {
157 |       throw new McpError(
158 |         ErrorCode.ConfigurationError,
159 |         'GITHUB_PERSONAL_ACCESS_TOKEN environment variable is required'
160 |       );
161 |     }
162 |     
163 |     githubApiInstance = new GitHubApi(token, baseUrl);
164 |   }
165 |   
166 |   return githubApiInstance;
167 | }
168 | 
```

--------------------------------------------------------------------------------
/src/tools/search.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { getGitHubApi } from '../utils/github-api.js';
  2 | import { tryCatchAsync } from '../utils/error-handling.js';
  3 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  4 | import {
  5 |   SearchCodeSchema,
  6 |   SearchIssuesSchema,
  7 |   SearchUsersSchema,
  8 | } from '../utils/validation.js';
  9 | 
 10 | /**
 11 |  * Search for code across GitHub repositories
 12 |  */
 13 | export async function searchCode(args: unknown): Promise<any> {
 14 |   const { q, order, page, per_page } = SearchCodeSchema.parse(args);
 15 |   const github = getGitHubApi();
 16 | 
 17 |   return tryCatchAsync(async () => {
 18 |     const { data } = await github.getOctokit().search.code({
 19 |       q,
 20 |       order,
 21 |       page,
 22 |       per_page,
 23 |     });
 24 | 
 25 |     return {
 26 |       total_count: data.total_count,
 27 |       incomplete_results: data.incomplete_results,
 28 |       items: data.items.map((item) => ({
 29 |         name: item.name,
 30 |         path: item.path,
 31 |         sha: item.sha,
 32 |         url: item.html_url,
 33 |         repository: item.repository ? {
 34 |           name: item.repository.name,
 35 |           full_name: item.repository.full_name,
 36 |           owner: {
 37 |             login: item.repository.owner.login,
 38 |           },
 39 |         } : null,
 40 |         score: item.score,
 41 |       })),
 42 |     };
 43 |   }, 'Failed to search code');
 44 | }
 45 | 
 46 | /**
 47 |  * Search for issues and pull requests across GitHub repositories
 48 |  */
 49 | export async function searchIssues(args: unknown): Promise<any> {
 50 |   const { q, sort, order, page, per_page } = SearchIssuesSchema.parse(args);
 51 |   const github = getGitHubApi();
 52 | 
 53 |   return tryCatchAsync(async () => {
 54 |     const { data } = await github.getOctokit().search.issuesAndPullRequests({
 55 |       q,
 56 |       sort: sort as any,
 57 |       order,
 58 |       page,
 59 |       per_page,
 60 |     });
 61 | 
 62 |     return {
 63 |       total_count: data.total_count,
 64 |       incomplete_results: data.incomplete_results,
 65 |       items: data.items.map((item) => ({
 66 |         id: item.id,
 67 |         number: item.number,
 68 |         title: item.title,
 69 |         state: item.state,
 70 |         locked: item.locked,
 71 |         repository: item.repository ? {
 72 |           name: item.repository.name,
 73 |           full_name: item.repository.full_name,
 74 |           owner: {
 75 |             login: item.repository.owner.login,
 76 |           },
 77 |         } : null,
 78 |         user: item.user ? {
 79 |           login: item.user.login,
 80 |           id: item.user.id,
 81 |         } : null,
 82 |         labels: item.labels?.map((label) => 
 83 |           typeof label === 'string' ? label : {
 84 |             name: label.name,
 85 |             color: label.color,
 86 |           }
 87 |         ),
 88 |         comments: item.comments,
 89 |         created_at: item.created_at,
 90 |         updated_at: item.updated_at,
 91 |         closed_at: item.closed_at,
 92 |         body: item.body,
 93 |         url: item.html_url,
 94 |         pull_request: item.pull_request ? {
 95 |           url: item.pull_request.html_url,
 96 |         } : null,
 97 |         score: item.score,
 98 |       })),
 99 |     };
100 |   }, 'Failed to search issues');
101 | }
102 | 
103 | /**
104 |  * Search for users on GitHub
105 |  */
106 | export async function searchUsers(args: unknown): Promise<any> {
107 |   const { q, sort, order, page, per_page } = SearchUsersSchema.parse(args);
108 |   const github = getGitHubApi();
109 | 
110 |   return tryCatchAsync(async () => {
111 |     const { data } = await github.getOctokit().search.users({
112 |       q,
113 |       sort: sort as any,
114 |       order,
115 |       page,
116 |       per_page,
117 |     });
118 | 
119 |     return {
120 |       total_count: data.total_count,
121 |       incomplete_results: data.incomplete_results,
122 |       items: data.items.map((user) => ({
123 |         login: user.login,
124 |         id: user.id,
125 |         avatar_url: user.avatar_url,
126 |         html_url: user.html_url,
127 |         type: user.type,
128 |         site_admin: user.site_admin,
129 |         score: user.score,
130 |       })),
131 |     };
132 |   }, 'Failed to search users');
133 | }
134 | 
135 | /**
136 |  * Get license information
137 |  */
138 | export async function getLicenseInfo(): Promise<any> {
139 |   const github = getGitHubApi();
140 | 
141 |   return tryCatchAsync(async () => {
142 |     const { data } = await github.getOctokit().licenses.getAllCommonlyUsed();
143 | 
144 |     return data.map((license) => ({
145 |       key: license.key,
146 |       name: license.name,
147 |       spdx_id: license.spdx_id,
148 |       url: license.url,
149 |       node_id: license.node_id,
150 |     }));
151 |   }, 'Failed to get license information');
152 | }
153 | 
154 | /**
155 |  * Get GitHub Enterprise stats
156 |  */
157 | export async function getEnterpriseStats(): Promise<any> {
158 |   const github = getGitHubApi();
159 |   const baseUrl = process.env.GITHUB_API_URL;
160 | 
161 |   // This function is only available for GitHub Enterprise
162 |   if (!baseUrl) {
163 |     return {
164 |       error: 'This function is only available for GitHub Enterprise',
165 |     };
166 |   }
167 | 
168 |   return tryCatchAsync(async () => {
169 |     try {
170 |       // Try to get enterprise stats
171 |       const { data } = await github.getOctokit().request('GET /enterprise/stats/all');
172 |       return data;
173 |     } catch (error) {
174 |       // If not available, return basic information
175 |       return {
176 |         message: 'Enterprise stats not available or insufficient permissions',
177 |         api_url: baseUrl,
178 |       };
179 |     }
180 |   }, 'Failed to get enterprise stats');
181 | }
182 | 
```

--------------------------------------------------------------------------------
/CLINE_INSTALLATION.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Installing GitHub Enterprise MCP Server in Cline
  2 | 
  3 | This guide will help you install and configure the GitHub Enterprise MCP server in Cline, enabling you to use GitHub API functionality directly through Cline.
  4 | 
  5 | ## Prerequisites
  6 | 
  7 | 1. Node.js installed on your system
  8 | 2. A GitHub Personal Access Token with appropriate permissions
  9 | 3. Cline installed on your system
 10 | 
 11 | ## Installation Steps
 12 | 
 13 | ### 1. Clone the Repository
 14 | 
 15 | ```bash
 16 | git clone https://github.com/piyushgIITian/github-enterprice-mcp.git
 17 | cd github-enterprise-mcp
 18 | ```
 19 | 
 20 | ### 2. Install Dependencies and Build
 21 | 
 22 | ```bash
 23 | npm install
 24 | npm run build
 25 | ```
 26 | 
 27 | This will create a `dist` directory with the compiled JavaScript files.
 28 | 
 29 | ### 3. Create a GitHub Personal Access Token
 30 | 
 31 | 1. Go to [GitHub Personal Access Tokens](https://github.com/settings/tokens) (in GitHub Settings > Developer settings)
 32 | 2. Click "Generate new token"
 33 | 3. Select which repositories you'd like this token to have access to (Public, All, or Select)
 34 | 4. Create a token with the `repo` scope ("Full control of private repositories")
 35 |    - Alternatively, if working only with public repositories, select only the `public_repo` scope
 36 | 5. Copy the generated token
 37 | 
 38 | ### 4. Configure Cline MCP Settings
 39 | 
 40 | #### For Cline VS Code Extension
 41 | 
 42 | 1. Open VS Code
 43 | 2. Locate the Cline MCP settings file at:
 44 |    - Windows: `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`
 45 |    - macOS: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
 46 |    - Linux: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
 47 | 
 48 | 3. Add the GitHub Enterprise MCP server configuration to the `mcpServers` object:
 49 | 
 50 | ```json
 51 | {
 52 |   "mcpServers": {
 53 |     "github-enterprise": {
 54 |       "command": "node",
 55 |       "args": [
 56 |         "/absolute/path/to/github-enterprise-mcp/dist/index.js"
 57 |       ],
 58 |       "env": {
 59 |         "GITHUB_PERSONAL_ACCESS_TOKEN": "your-personal-access-token",
 60 |         "GITHUB_API_URL": "https://api.github.com" // For GitHub.com
 61 |         // For GitHub Enterprise, use your instance URL, e.g., "https://github.yourdomain.com/api/v3"
 62 |       },
 63 |       "disabled": false,
 64 |       "autoApprove": []
 65 |     }
 66 |   }
 67 | }
 68 | ```
 69 | 
 70 | Replace `/absolute/path/to/github-enterprise-mcp/dist/index.js` with the absolute path to the built index.js file.
 71 | 
 72 | #### For Claude Desktop App
 73 | 
 74 | 1. Locate the Claude Desktop configuration file at:
 75 |    - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
 76 |    - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
 77 |    - Linux: `~/.config/Claude/claude_desktop_config.json`
 78 | 
 79 | 2. Add the GitHub Enterprise MCP server configuration to the `mcpServers` object:
 80 | 
 81 | ```json
 82 | {
 83 |   "mcpServers": {
 84 |     "github-enterprise": {
 85 |       "command": "node",
 86 |       "args": [
 87 |         "/absolute/path/to/github-enterprise-mcp/dist/index.js"
 88 |       ],
 89 |       "env": {
 90 |         "GITHUB_PERSONAL_ACCESS_TOKEN": "your-personal-access-token",
 91 |         "GITHUB_API_URL": "https://api.github.com" // For GitHub.com
 92 |         // For GitHub Enterprise, use your instance URL, e.g., "https://github.yourdomain.com/api/v3"
 93 |       },
 94 |       "disabled": false,
 95 |       "autoApprove": []
 96 |     }
 97 |   }
 98 | }
 99 | ```
100 | 
101 | ### 5. Restart Cline
102 | 
103 | After configuring the MCP settings, restart Cline (VS Code or Claude Desktop) to apply the changes.
104 | 
105 | ## Verification
106 | 
107 | To verify that the GitHub Enterprise MCP server is properly installed and configured, you can ask Cline to use one of the GitHub tools:
108 | 
109 | ```
110 | Can you search for repositories with the keyword "react" using the GitHub Enterprise MCP server?
111 | ```
112 | 
113 | Cline should be able to use the `search_repositories` tool to search for repositories with the keyword "react".
114 | 
115 | ## Available Tools
116 | 
117 | The GitHub Enterprise MCP server provides a wide range of tools for interacting with GitHub, including:
118 | 
119 | - Repository management (create, update, delete)
120 | - File operations (create, update, get contents)
121 | - Issue and pull request management
122 | - Code, issue, and user search
123 | - Branch management
124 | - And more
125 | 
126 | For a complete list of available tools and their usage, refer to the [README.md](./README.md) file.
127 | 
128 | ## Troubleshooting
129 | 
130 | If you encounter issues with the GitHub Enterprise MCP server:
131 | 
132 | 1. Check that the path to the index.js file is correct in your MCP settings
133 | 2. Verify that your GitHub Personal Access Token has the necessary permissions
134 | 3. Ensure that the GitHub API URL is correct for your GitHub instance
135 | 4. Check the logs for any error messages
136 | 
137 | ## Additional Configuration Options
138 | 
139 | ### GitHub API Version
140 | 
141 | You can specify a GitHub API version by adding the `GITHUB_API_VERSION` environment variable:
142 | 
143 | ```json
144 | "env": {
145 |   "GITHUB_PERSONAL_ACCESS_TOKEN": "your-personal-access-token",
146 |   "GITHUB_API_URL": "https://api.github.com",
147 |   "GITHUB_API_VERSION": "2022-11-28"
148 | }
149 | ```
150 | 
151 | ### Enterprise Authentication
152 | 
153 | For GitHub Enterprise instances that use different authentication methods, you may need to provide additional configuration. Refer to the [Octokit documentation](https://github.com/octokit/rest.js) for more information.
154 | 
```

--------------------------------------------------------------------------------
/src/tools/files.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { getGitHubApi } from '../utils/github-api.js';
  2 | import { tryCatchAsync, base64ToUtf8, utf8ToBase64 } from '../utils/error-handling.js';
  3 | import {
  4 |   CreateOrUpdateFileSchema,
  5 |   PushFilesSchema,
  6 |   GetFileContentsSchema,
  7 | } from '../utils/validation.js';
  8 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  9 | 
 10 | /**
 11 |  * Create or update a file in a GitHub repository
 12 |  */
 13 | export async function createOrUpdateFile(args: unknown): Promise<any> {
 14 |   const { owner, repo, path, content, message, branch, sha } = CreateOrUpdateFileSchema.parse(args);
 15 |   const github = getGitHubApi();
 16 | 
 17 |   return tryCatchAsync(async () => {
 18 |     // Check if branch exists, create it if it doesn't
 19 |     const branchExists = await github.branchExists(owner, repo, branch);
 20 |     if (!branchExists) {
 21 |       await github.createBranch(owner, repo, branch);
 22 |     }
 23 | 
 24 |     // Create or update the file
 25 |     const { data } = await github.getOctokit().repos.createOrUpdateFileContents({
 26 |       owner,
 27 |       repo,
 28 |       path,
 29 |       message,
 30 |       content: utf8ToBase64(content),
 31 |       branch,
 32 |       sha,
 33 |     });
 34 | 
 35 |     return {
 36 |       content: {
 37 |         name: data.content?.name,
 38 |         path: data.content?.path,
 39 |         sha: data.content?.sha,
 40 |         size: data.content?.size,
 41 |         url: data.content?.html_url,
 42 |       },
 43 |       commit: {
 44 |         sha: data.commit.sha,
 45 |         url: data.commit.html_url,
 46 |         message: data.commit.message,
 47 |         author: data.commit.author,
 48 |         committer: data.commit.committer,
 49 |       },
 50 |     };
 51 |   }, 'Failed to create or update file');
 52 | }
 53 | 
 54 | /**
 55 |  * Push multiple files to a GitHub repository in a single commit
 56 |  */
 57 | export async function pushFiles(args: unknown): Promise<any> {
 58 |   const { owner, repo, branch, files, message } = PushFilesSchema.parse(args);
 59 |   const github = getGitHubApi();
 60 | 
 61 |   return tryCatchAsync(async () => {
 62 |     // Check if branch exists, create it if it doesn't
 63 |     const branchExists = await github.branchExists(owner, repo, branch);
 64 |     if (!branchExists) {
 65 |       await github.createBranch(owner, repo, branch);
 66 |     }
 67 | 
 68 |     // Get the latest commit SHA on the branch
 69 |     const { data: refData } = await github.getOctokit().git.getRef({
 70 |       owner,
 71 |       repo,
 72 |       ref: `heads/${branch}`,
 73 |     });
 74 |     const latestCommitSha = refData.object.sha;
 75 | 
 76 |     // Get the commit to get the tree SHA
 77 |     const { data: commitData } = await github.getOctokit().git.getCommit({
 78 |       owner,
 79 |       repo,
 80 |       commit_sha: latestCommitSha,
 81 |     });
 82 |     const baseTreeSha = commitData.tree.sha;
 83 | 
 84 |     // Create a new tree with the files
 85 |     const tree = await Promise.all(
 86 |       files.map(async (file) => {
 87 |         // Check if file exists to get its SHA
 88 |         let fileSha;
 89 |         try {
 90 |           const { data: existingFile } = await github.getOctokit().repos.getContent({
 91 |             owner,
 92 |             repo,
 93 |             path: file.path,
 94 |             ref: branch,
 95 |           });
 96 |           
 97 |           if (!Array.isArray(existingFile)) {
 98 |             fileSha = existingFile.sha;
 99 |           }
100 |         } catch (error: any) {
101 |           // File doesn't exist, which is fine for new files
102 |           if (error.status !== 404) {
103 |             throw error;
104 |           }
105 |         }
106 | 
107 |         return {
108 |           path: file.path,
109 |           mode: '100644' as '100644', // Regular file
110 |           type: 'blob' as 'blob',
111 |           content: file.content,
112 |         };
113 |       })
114 |     );
115 | 
116 |     // Create a new tree
117 |     const { data: newTree } = await github.getOctokit().git.createTree({
118 |       owner,
119 |       repo,
120 |       base_tree: baseTreeSha,
121 |       tree,
122 |     });
123 | 
124 |     // Create a new commit
125 |     const { data: newCommit } = await github.getOctokit().git.createCommit({
126 |       owner,
127 |       repo,
128 |       message,
129 |       tree: newTree.sha,
130 |       parents: [latestCommitSha],
131 |     });
132 | 
133 |     // Update the reference
134 |     const { data: updatedRef } = await github.getOctokit().git.updateRef({
135 |       owner,
136 |       repo,
137 |       ref: `heads/${branch}`,
138 |       sha: newCommit.sha,
139 |     });
140 | 
141 |     return {
142 |       success: true,
143 |       branch,
144 |       commit: {
145 |         sha: newCommit.sha,
146 |         message,
147 |         url: `https://github.com/${owner}/${repo}/commit/${newCommit.sha}`,
148 |       },
149 |       files: files.map((file) => file.path),
150 |     };
151 |   }, 'Failed to push files');
152 | }
153 | 
154 | /**
155 |  * Get the contents of a file or directory from a GitHub repository
156 |  */
157 | export async function getFileContents(args: unknown): Promise<any> {
158 |   const { owner, repo, path, branch } = GetFileContentsSchema.parse(args);
159 |   const github = getGitHubApi();
160 | 
161 |   return tryCatchAsync(async () => {
162 |     const { data } = await github.getOctokit().repos.getContent({
163 |       owner,
164 |       repo,
165 |       path,
166 |       ref: branch,
167 |     });
168 | 
169 |     // Handle directory listing
170 |     if (Array.isArray(data)) {
171 |       return data.map((item) => ({
172 |         name: item.name,
173 |         path: item.path,
174 |         sha: item.sha,
175 |         size: item.size,
176 |         type: item.type,
177 |         url: item.html_url,
178 |         download_url: item.download_url,
179 |       }));
180 |     }
181 | 
182 |     // Handle file content
183 |     if (data.type === 'file') {
184 |       return {
185 |         name: data.name,
186 |         path: data.path,
187 |         sha: data.sha,
188 |         size: data.size,
189 |         type: data.type,
190 |         url: data.html_url,
191 |         content: data.content ? base64ToUtf8(data.content) : null,
192 |         encoding: data.encoding,
193 |       };
194 |     }
195 | 
196 |     // Handle submodule or symlink
197 |     return {
198 |       name: data.name,
199 |       path: data.path,
200 |       sha: data.sha,
201 |       size: data.size,
202 |       type: data.type,
203 |       url: data.html_url,
204 |     };
205 |   }, 'Failed to get file contents');
206 | }
207 | 
208 | /**
209 |  * Fork a GitHub repository
210 |  */
211 | export async function forkRepository(args: unknown): Promise<any> {
212 |   const { owner, repo, organization } = args as { owner: string; repo: string; organization?: string };
213 |   const github = getGitHubApi();
214 | 
215 |   return tryCatchAsync(async () => {
216 |     const { data } = await github.getOctokit().repos.createFork({
217 |       owner,
218 |       repo,
219 |       organization,
220 |     });
221 | 
222 |     return {
223 |       id: data.id,
224 |       name: data.name,
225 |       full_name: data.full_name,
226 |       owner: {
227 |         login: data.owner.login,
228 |         id: data.owner.id,
229 |         type: data.owner.type,
230 |       },
231 |       private: data.private,
232 |       html_url: data.html_url,
233 |       description: data.description,
234 |       fork: data.fork,
235 |       created_at: data.created_at,
236 |       updated_at: data.updated_at,
237 |       pushed_at: data.pushed_at,
238 |       default_branch: data.default_branch,
239 |       parent: data.parent ? {
240 |         name: data.parent.name,
241 |         full_name: data.parent.full_name,
242 |         owner: {
243 |           login: data.parent.owner.login,
244 |         },
245 |       } : null,
246 |       source: data.source ? {
247 |         name: data.source.name,
248 |         full_name: data.source.full_name,
249 |         owner: {
250 |           login: data.source.owner.login,
251 |         },
252 |       } : null,
253 |     };
254 |   }, 'Failed to fork repository');
255 | }
256 | 
257 | /**
258 |  * Get pull request files
259 |  */
260 | export async function getPullRequestFiles(args: unknown): Promise<any> {
261 |   const { owner, repo, pull_number } = args as { owner: string; repo: string; pull_number: number };
262 |   const github = getGitHubApi();
263 | 
264 |   return tryCatchAsync(async () => {
265 |     const { data } = await github.getOctokit().pulls.listFiles({
266 |       owner,
267 |       repo,
268 |       pull_number,
269 |     });
270 | 
271 |     return data.map((file) => ({
272 |       sha: file.sha,
273 |       filename: file.filename,
274 |       status: file.status,
275 |       additions: file.additions,
276 |       deletions: file.deletions,
277 |       changes: file.changes,
278 |       blob_url: file.blob_url,
279 |       raw_url: file.raw_url,
280 |       contents_url: file.contents_url,
281 |       patch: file.patch,
282 |     }));
283 |   }, 'Failed to get pull request files');
284 | }
285 | 
```

--------------------------------------------------------------------------------
/src/utils/validation.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { z } from 'zod';
  2 | 
  3 | // Common schemas
  4 | export const OwnerRepoSchema = z.object({
  5 |   owner: z.string().min(1, 'Repository owner is required'),
  6 |   repo: z.string().min(1, 'Repository name is required'),
  7 | });
  8 | 
  9 | export const PaginationSchema = z.object({
 10 |   page: z.number().optional(),
 11 |   perPage: z.number().min(1).max(100).optional(),
 12 | });
 13 | 
 14 | // File operations schemas
 15 | export const CreateOrUpdateFileSchema = OwnerRepoSchema.extend({
 16 |   path: z.string().min(1, 'File path is required'),
 17 |   content: z.string().min(1, 'File content is required'),
 18 |   message: z.string().min(1, 'Commit message is required'),
 19 |   branch: z.string().min(1, 'Branch name is required'),
 20 |   sha: z.string().optional(),
 21 | });
 22 | 
 23 | export const PushFilesSchema = OwnerRepoSchema.extend({
 24 |   branch: z.string().min(1, 'Branch name is required'),
 25 |   files: z.array(
 26 |     z.object({
 27 |       path: z.string().min(1, 'File path is required'),
 28 |       content: z.string().min(1, 'File content is required'),
 29 |     })
 30 |   ).min(1, 'At least one file is required'),
 31 |   message: z.string().min(1, 'Commit message is required'),
 32 | });
 33 | 
 34 | export const GetFileContentsSchema = OwnerRepoSchema.extend({
 35 |   path: z.string().min(1, 'File path is required'),
 36 |   branch: z.string().optional(),
 37 | });
 38 | 
 39 | // Repository schemas
 40 | export const SearchRepositoriesSchema = z.object({
 41 |   query: z.string().min(1, 'Search query is required'),
 42 |   page: z.number().optional(),
 43 |   perPage: z.number().min(1).max(100).optional(),
 44 | });
 45 | 
 46 | export const CreateRepositorySchema = z.object({
 47 |   name: z.string().min(1, 'Repository name is required'),
 48 |   description: z.string().optional(),
 49 |   private: z.boolean().optional(),
 50 |   autoInit: z.boolean().optional(),
 51 |   org: z.string().optional(),
 52 | });
 53 | 
 54 | export const UpdateRepositorySchema = OwnerRepoSchema.extend({
 55 |   description: z.string().optional(),
 56 |   private: z.boolean().optional(),
 57 |   default_branch: z.string().optional(),
 58 |   has_issues: z.boolean().optional(),
 59 |   has_projects: z.boolean().optional(),
 60 |   has_wiki: z.boolean().optional(),
 61 |   archived: z.boolean().optional(),
 62 | });
 63 | 
 64 | export const DeleteRepositorySchema = OwnerRepoSchema.extend({
 65 |   confirm: z.boolean().refine(val => val === true, {
 66 |     message: 'You must confirm deletion by setting confirm to true',
 67 |   }),
 68 | });
 69 | 
 70 | // Branch schemas
 71 | export const CreateBranchSchema = OwnerRepoSchema.extend({
 72 |   branch: z.string().min(1, 'Branch name is required'),
 73 |   from_branch: z.string().optional(),
 74 | });
 75 | 
 76 | // Issue schemas
 77 | export const ListIssuesSchema = OwnerRepoSchema.extend({
 78 |   state: z.enum(['open', 'closed', 'all']).optional(),
 79 |   labels: z.array(z.string()).optional(),
 80 |   sort: z.enum(['created', 'updated', 'comments']).optional(),
 81 |   direction: z.enum(['asc', 'desc']).optional(),
 82 |   since: z.string().optional(),
 83 |   page: z.number().optional(),
 84 |   per_page: z.number().optional(),
 85 | });
 86 | 
 87 | export const GetIssueSchema = OwnerRepoSchema.extend({
 88 |   issue_number: z.number().int().positive(),
 89 | });
 90 | 
 91 | export const CreateIssueSchema = OwnerRepoSchema.extend({
 92 |   title: z.string().min(1, 'Issue title is required'),
 93 |   body: z.string().optional(),
 94 |   assignees: z.array(z.string()).optional(),
 95 |   milestone: z.number().optional(),
 96 |   labels: z.array(z.string()).optional(),
 97 | });
 98 | 
 99 | export const UpdateIssueSchema = OwnerRepoSchema.extend({
100 |   issue_number: z.number().int().positive(),
101 |   title: z.string().optional(),
102 |   body: z.string().optional(),
103 |   assignees: z.array(z.string()).optional(),
104 |   milestone: z.number().optional(),
105 |   labels: z.array(z.string()).optional(),
106 |   state: z.enum(['open', 'closed']).optional(),
107 | });
108 | 
109 | export const AddIssueCommentSchema = OwnerRepoSchema.extend({
110 |   issue_number: z.number().int().positive(),
111 |   body: z.string().min(1, 'Comment body is required'),
112 | });
113 | 
114 | // Pull request schemas
115 | export const ListPullRequestsSchema = OwnerRepoSchema.extend({
116 |   state: z.enum(['open', 'closed', 'all']).optional(),
117 |   head: z.string().optional(),
118 |   base: z.string().optional(),
119 |   sort: z.enum(['created', 'updated', 'popularity', 'long-running']).optional(),
120 |   direction: z.enum(['asc', 'desc']).optional(),
121 |   per_page: z.number().optional(),
122 |   page: z.number().optional(),
123 | });
124 | 
125 | export const GetPullRequestSchema = OwnerRepoSchema.extend({
126 |   pull_number: z.number().int().positive(),
127 | });
128 | 
129 | export const CreatePullRequestSchema = OwnerRepoSchema.extend({
130 |   title: z.string().min(1, 'Pull request title is required'),
131 |   head: z.string().min(1, 'Head branch is required'),
132 |   base: z.string().min(1, 'Base branch is required'),
133 |   body: z.string().optional(),
134 |   draft: z.boolean().optional(),
135 |   maintainer_can_modify: z.boolean().optional(),
136 | });
137 | 
138 | export const CreatePullRequestReviewSchema = OwnerRepoSchema.extend({
139 |   pull_number: z.number().int().positive(),
140 |   body: z.string().min(1, 'Review body is required'),
141 |   event: z.enum(['APPROVE', 'REQUEST_CHANGES', 'COMMENT']),
142 |   commit_id: z.string().optional(),
143 |   comments: z
144 |     .array(
145 |       z.object({
146 |         path: z.string().min(1, 'File path is required'),
147 |         position: z.number().int().positive(),
148 |         body: z.string().min(1, 'Comment body is required'),
149 |       })
150 |     )
151 |     .optional(),
152 | });
153 | 
154 | export const MergePullRequestSchema = OwnerRepoSchema.extend({
155 |   pull_number: z.number().int().positive(),
156 |   commit_title: z.string().optional(),
157 |   commit_message: z.string().optional(),
158 |   merge_method: z.enum(['merge', 'squash', 'rebase']).optional(),
159 | });
160 | 
161 | export const GetPullRequestFilesSchema = OwnerRepoSchema.extend({
162 |   pull_number: z.number().int().positive(),
163 | });
164 | 
165 | export const GetPullRequestStatusSchema = OwnerRepoSchema.extend({
166 |   pull_number: z.number().int().positive(),
167 | });
168 | 
169 | export const UpdatePullRequestBranchSchema = OwnerRepoSchema.extend({
170 |   pull_number: z.number().int().positive(),
171 |   expected_head_sha: z.string().optional(),
172 | });
173 | 
174 | export const GetPullRequestCommentsSchema = OwnerRepoSchema.extend({
175 |   pull_number: z.number().int().positive(),
176 | });
177 | 
178 | export const GetPullRequestReviewsSchema = OwnerRepoSchema.extend({
179 |   pull_number: z.number().int().positive(),
180 | });
181 | 
182 | // Search schemas
183 | export const SearchCodeSchema = z.object({
184 |   q: z.string().min(1, 'Search query is required'),
185 |   order: z.enum(['asc', 'desc']).optional(),
186 |   page: z.number().min(1).optional(),
187 |   per_page: z.number().min(1).max(100).optional(),
188 | });
189 | 
190 | export const SearchIssuesSchema = z.object({
191 |   q: z.string().min(1, 'Search query is required'),
192 |   sort: z
193 |     .enum([
194 |       'comments',
195 |       'reactions',
196 |       'reactions-+1',
197 |       'reactions--1',
198 |       'reactions-smile',
199 |       'reactions-thinking_face',
200 |       'reactions-heart',
201 |       'reactions-tada',
202 |       'interactions',
203 |       'created',
204 |       'updated',
205 |     ])
206 |     .optional(),
207 |   order: z.enum(['asc', 'desc']).optional(),
208 |   page: z.number().min(1).optional(),
209 |   per_page: z.number().min(1).max(100).optional(),
210 | });
211 | 
212 | export const SearchUsersSchema = z.object({
213 |   q: z.string().min(1, 'Search query is required'),
214 |   sort: z.enum(['followers', 'repositories', 'joined']).optional(),
215 |   order: z.enum(['asc', 'desc']).optional(),
216 |   page: z.number().min(1).optional(),
217 |   per_page: z.number().min(1).max(100).optional(),
218 | });
219 | 
220 | // Commits schema
221 | export const ListCommitsSchema = OwnerRepoSchema.extend({
222 |   sha: z.string().optional(),
223 |   page: z.number().optional(),
224 |   perPage: z.number().optional(),
225 | });
226 | 
227 | // Workflow schemas
228 | export const ListWorkflowsSchema = OwnerRepoSchema.extend({
229 |   page: z.number().int().optional(),
230 |   perPage: z.number().int().optional(),
231 | });
232 | 
233 | export const ListWorkflowRunsSchema = OwnerRepoSchema.extend({
234 |   workflow_id: z.union([z.string(), z.number()]).optional(),
235 |   branch: z.string().optional(),
236 |   status: z
237 |     .enum([
238 |       'completed',
239 |       'action_required',
240 |       'cancelled',
241 |       'failure',
242 |       'neutral',
243 |       'skipped',
244 |       'stale',
245 |       'success',
246 |       'timed_out',
247 |       'in_progress',
248 |       'queued',
249 |       'requested',
250 |       'waiting',
251 |     ])
252 |     .optional(),
253 |   page: z.number().int().optional(),
254 |   perPage: z.number().int().optional(),
255 | });
256 | 
257 | export const TriggerWorkflowSchema = OwnerRepoSchema.extend({
258 |   workflow_id: z.union([z.string(), z.number()]),
259 |   ref: z.string().min(1, 'Git reference is required'),
260 |   inputs: z.record(z.string()).optional(),
261 | });
262 | 
```

--------------------------------------------------------------------------------
/src/tools/issues.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { getGitHubApi } from '../utils/github-api.js';
  2 | import { tryCatchAsync } from '../utils/error-handling.js';
  3 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  4 | import {
  5 |   ListIssuesSchema,
  6 |   GetIssueSchema,
  7 |   CreateIssueSchema,
  8 |   UpdateIssueSchema,
  9 |   AddIssueCommentSchema,
 10 | } from '../utils/validation.js';
 11 | 
 12 | /**
 13 |  * List issues in a GitHub repository
 14 |  */
 15 | export async function listIssues(args: unknown): Promise<any> {
 16 |   const { owner, repo, state, labels, sort, direction, since, page, per_page } = ListIssuesSchema.parse(args);
 17 |   const github = getGitHubApi();
 18 | 
 19 |   return tryCatchAsync(async () => {
 20 |     const { data } = await github.getOctokit().issues.listForRepo({
 21 |       owner,
 22 |       repo,
 23 |       state,
 24 |       labels: labels?.join(','),
 25 |       sort,
 26 |       direction,
 27 |       since,
 28 |       page,
 29 |       per_page,
 30 |     });
 31 | 
 32 |     return data.map((issue) => ({
 33 |       id: issue.id,
 34 |       number: issue.number,
 35 |       title: issue.title,
 36 |       state: issue.state,
 37 |       locked: issue.locked,
 38 |       assignees: issue.assignees?.map((assignee) => ({
 39 |         login: assignee.login,
 40 |         id: assignee.id,
 41 |         type: assignee.type,
 42 |       })),
 43 |       user: issue.user ? {
 44 |         login: issue.user.login,
 45 |         id: issue.user.id,
 46 |         type: issue.user.type,
 47 |       } : null,
 48 |       labels: issue.labels?.map((label) => 
 49 |         typeof label === 'string' ? label : {
 50 |           name: label.name,
 51 |           color: label.color,
 52 |           description: label.description,
 53 |         }
 54 |       ),
 55 |       milestone: issue.milestone ? {
 56 |         id: issue.milestone.id,
 57 |         number: issue.milestone.number,
 58 |         title: issue.milestone.title,
 59 |         description: issue.milestone.description,
 60 |         state: issue.milestone.state,
 61 |       } : null,
 62 |       comments: issue.comments,
 63 |       created_at: issue.created_at,
 64 |       updated_at: issue.updated_at,
 65 |       closed_at: issue.closed_at,
 66 |       body: issue.body,
 67 |       url: issue.html_url,
 68 |       pull_request: issue.pull_request ? {
 69 |         url: issue.pull_request.html_url,
 70 |       } : null,
 71 |     }));
 72 |   }, 'Failed to list issues');
 73 | }
 74 | 
 75 | /**
 76 |  * Get a specific issue in a GitHub repository
 77 |  */
 78 | export async function getIssue(args: unknown): Promise<any> {
 79 |   const { owner, repo, issue_number } = GetIssueSchema.parse(args);
 80 |   const github = getGitHubApi();
 81 | 
 82 |   return tryCatchAsync(async () => {
 83 |     const { data } = await github.getOctokit().issues.get({
 84 |       owner,
 85 |       repo,
 86 |       issue_number,
 87 |     });
 88 | 
 89 |     return {
 90 |       id: data.id,
 91 |       number: data.number,
 92 |       title: data.title,
 93 |       state: data.state,
 94 |       locked: data.locked,
 95 |       assignees: data.assignees?.map((assignee) => ({
 96 |         login: assignee.login,
 97 |         id: assignee.id,
 98 |         type: assignee.type,
 99 |       })),
100 |       user: data.user ? {
101 |         login: data.user.login,
102 |         id: data.user.id,
103 |         type: data.user.type,
104 |       } : null,
105 |       labels: data.labels?.map((label) => 
106 |         typeof label === 'string' ? label : {
107 |           name: label.name,
108 |           color: label.color,
109 |           description: label.description,
110 |         }
111 |       ),
112 |       milestone: data.milestone ? {
113 |         id: data.milestone.id,
114 |         number: data.milestone.number,
115 |         title: data.milestone.title,
116 |         description: data.milestone.description,
117 |         state: data.milestone.state,
118 |       } : null,
119 |       comments: data.comments,
120 |       created_at: data.created_at,
121 |       updated_at: data.updated_at,
122 |       closed_at: data.closed_at,
123 |       body: data.body,
124 |       url: data.html_url,
125 |       pull_request: data.pull_request ? {
126 |         url: data.pull_request.html_url,
127 |       } : null,
128 |     };
129 |   }, 'Failed to get issue');
130 | }
131 | 
132 | /**
133 |  * Create a new issue in a GitHub repository
134 |  */
135 | export async function createIssue(args: unknown): Promise<any> {
136 |   const { owner, repo, title, body, assignees, milestone, labels } = CreateIssueSchema.parse(args);
137 |   const github = getGitHubApi();
138 | 
139 |   return tryCatchAsync(async () => {
140 |     const { data } = await github.getOctokit().issues.create({
141 |       owner,
142 |       repo,
143 |       title,
144 |       body,
145 |       assignees,
146 |       milestone,
147 |       labels,
148 |     });
149 | 
150 |     return {
151 |       id: data.id,
152 |       number: data.number,
153 |       title: data.title,
154 |       state: data.state,
155 |       assignees: data.assignees?.map((assignee) => ({
156 |         login: assignee.login,
157 |         id: assignee.id,
158 |       })),
159 |       user: data.user ? {
160 |         login: data.user.login,
161 |         id: data.user.id,
162 |       } : null,
163 |       labels: data.labels?.map((label) => 
164 |         typeof label === 'string' ? label : {
165 |           name: label.name,
166 |           color: label.color,
167 |         }
168 |       ),
169 |       milestone: data.milestone ? {
170 |         number: data.milestone.number,
171 |         title: data.milestone.title,
172 |       } : null,
173 |       created_at: data.created_at,
174 |       updated_at: data.updated_at,
175 |       body: data.body,
176 |       url: data.html_url,
177 |     };
178 |   }, 'Failed to create issue');
179 | }
180 | 
181 | /**
182 |  * Update an existing issue in a GitHub repository
183 |  */
184 | export async function updateIssue(args: unknown): Promise<any> {
185 |   const { owner, repo, issue_number, title, body, assignees, milestone, labels, state } = UpdateIssueSchema.parse(args);
186 |   const github = getGitHubApi();
187 | 
188 |   return tryCatchAsync(async () => {
189 |     const { data } = await github.getOctokit().issues.update({
190 |       owner,
191 |       repo,
192 |       issue_number,
193 |       title,
194 |       body,
195 |       assignees,
196 |       milestone,
197 |       labels,
198 |       state,
199 |     });
200 | 
201 |     return {
202 |       id: data.id,
203 |       number: data.number,
204 |       title: data.title,
205 |       state: data.state,
206 |       assignees: data.assignees?.map((assignee) => ({
207 |         login: assignee.login,
208 |         id: assignee.id,
209 |       })),
210 |       labels: data.labels?.map((label) => 
211 |         typeof label === 'string' ? label : {
212 |           name: label.name,
213 |           color: label.color,
214 |         }
215 |       ),
216 |       milestone: data.milestone ? {
217 |         number: data.milestone.number,
218 |         title: data.milestone.title,
219 |       } : null,
220 |       updated_at: data.updated_at,
221 |       body: data.body,
222 |       url: data.html_url,
223 |     };
224 |   }, 'Failed to update issue');
225 | }
226 | 
227 | /**
228 |  * Add a comment to an issue in a GitHub repository
229 |  */
230 | export async function addIssueComment(args: unknown): Promise<any> {
231 |   const { owner, repo, issue_number, body } = AddIssueCommentSchema.parse(args);
232 |   const github = getGitHubApi();
233 | 
234 |   return tryCatchAsync(async () => {
235 |     const { data } = await github.getOctokit().issues.createComment({
236 |       owner,
237 |       repo,
238 |       issue_number,
239 |       body,
240 |     });
241 | 
242 |     return {
243 |       id: data.id,
244 |       user: data.user ? {
245 |         login: data.user.login,
246 |         id: data.user.id,
247 |       } : null,
248 |       created_at: data.created_at,
249 |       updated_at: data.updated_at,
250 |       body: data.body,
251 |       url: data.html_url,
252 |     };
253 |   }, 'Failed to add issue comment');
254 | }
255 | 
256 | /**
257 |  * Search for issues and pull requests across GitHub repositories
258 |  */
259 | export async function searchIssues(args: unknown): Promise<any> {
260 |   const { q, sort, order, page, per_page } = args as { 
261 |     q: string; 
262 |     sort?: string; 
263 |     order?: 'asc' | 'desc'; 
264 |     page?: number; 
265 |     per_page?: number;
266 |   };
267 |   const github = getGitHubApi();
268 | 
269 |   return tryCatchAsync(async () => {
270 |     const { data } = await github.getOctokit().search.issuesAndPullRequests({
271 |       q,
272 |       sort: sort as any,
273 |       order,
274 |       page,
275 |       per_page,
276 |     });
277 | 
278 |     return {
279 |       total_count: data.total_count,
280 |       incomplete_results: data.incomplete_results,
281 |       items: data.items.map((item) => ({
282 |         id: item.id,
283 |         number: item.number,
284 |         title: item.title,
285 |         state: item.state,
286 |         locked: item.locked,
287 |         repository: item.repository ? {
288 |           name: item.repository.name,
289 |           full_name: item.repository.full_name,
290 |           owner: {
291 |             login: item.repository.owner.login,
292 |           },
293 |         } : null,
294 |         user: item.user ? {
295 |           login: item.user.login,
296 |           id: item.user.id,
297 |         } : null,
298 |         labels: item.labels?.map((label) => 
299 |           typeof label === 'string' ? label : {
300 |             name: label.name,
301 |             color: label.color,
302 |           }
303 |         ),
304 |         comments: item.comments,
305 |         created_at: item.created_at,
306 |         updated_at: item.updated_at,
307 |         closed_at: item.closed_at,
308 |         body: item.body,
309 |         url: item.html_url,
310 |         pull_request: item.pull_request ? {
311 |           url: item.pull_request.html_url,
312 |         } : null,
313 |       })),
314 |     };
315 |   }, 'Failed to search issues');
316 | }
317 | 
```

--------------------------------------------------------------------------------
/src/tools/repository.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { getGitHubApi } from '../utils/github-api.js';
  2 | import { tryCatchAsync } from '../utils/error-handling.js';
  3 | import {
  4 |   SearchRepositoriesSchema,
  5 |   CreateRepositorySchema,
  6 |   UpdateRepositorySchema,
  7 |   DeleteRepositorySchema,
  8 |   CreateBranchSchema,
  9 |   ListCommitsSchema,
 10 |   ListWorkflowsSchema,
 11 |   ListWorkflowRunsSchema,
 12 |   TriggerWorkflowSchema,
 13 | } from '../utils/validation.js';
 14 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
 15 | 
 16 | /**
 17 |  * Search for GitHub repositories
 18 |  */
 19 | export async function searchRepositories(args: unknown): Promise<any> {
 20 |   const { query, page, perPage } = SearchRepositoriesSchema.parse(args);
 21 |   const github = getGitHubApi();
 22 | 
 23 |   return tryCatchAsync(async () => {
 24 |     const { data } = await github.getOctokit().search.repos({
 25 |       q: query,
 26 |       page,
 27 |       per_page: perPage,
 28 |     });
 29 | 
 30 |     return {
 31 |       total_count: data.total_count,
 32 |       incomplete_results: data.incomplete_results,
 33 |       items: data.items.map((repo) => ({
 34 |         id: repo.id,
 35 |         name: repo.name,
 36 |         full_name: repo.full_name,
 37 |         owner: repo.owner ? {
 38 |           login: repo.owner.login,
 39 |           id: repo.owner.id,
 40 |           type: repo.owner.type,
 41 |         } : null,
 42 |         private: repo.private,
 43 |         description: repo.description,
 44 |         fork: repo.fork,
 45 |         created_at: repo.created_at,
 46 |         updated_at: repo.updated_at,
 47 |         pushed_at: repo.pushed_at,
 48 |         homepage: repo.homepage,
 49 |         size: repo.size,
 50 |         stargazers_count: repo.stargazers_count,
 51 |         watchers_count: repo.watchers_count,
 52 |         language: repo.language,
 53 |         forks_count: repo.forks_count,
 54 |         open_issues_count: repo.open_issues_count,
 55 |         default_branch: repo.default_branch,
 56 |         url: repo.html_url,
 57 |       })),
 58 |     };
 59 |   }, 'Failed to search repositories');
 60 | }
 61 | 
 62 | /**
 63 |  * Create a new GitHub repository
 64 |  */
 65 | export async function createRepository(args: unknown): Promise<any> {
 66 |   const { name, description, private: isPrivate, autoInit, org } = CreateRepositorySchema.parse(args);
 67 |   const github = getGitHubApi();
 68 | 
 69 |   return tryCatchAsync(async () => {
 70 |     let data;
 71 | 
 72 |     if (org) {
 73 |       // Create repository in an organization
 74 |       const response = await github.getOctokit().repos.createInOrg({
 75 |         org,
 76 |         name,
 77 |         description,
 78 |         private: isPrivate,
 79 |         auto_init: autoInit,
 80 |       });
 81 |       data = response.data;
 82 |     } else {
 83 |       // Create repository for the authenticated user
 84 |       const response = await github.getOctokit().repos.createForAuthenticatedUser({
 85 |         name,
 86 |         description,
 87 |         private: isPrivate,
 88 |         auto_init: autoInit,
 89 |       });
 90 |       data = response.data;
 91 |     }
 92 | 
 93 |     return {
 94 |       id: data.id,
 95 |       name: data.name,
 96 |       full_name: data.full_name,
 97 |       private: data.private,
 98 |       description: data.description,
 99 |       html_url: data.html_url,
100 |       clone_url: data.clone_url,
101 |       ssh_url: data.ssh_url,
102 |       created_at: data.created_at,
103 |       updated_at: data.updated_at,
104 |       default_branch: data.default_branch,
105 |     };
106 |   }, 'Failed to create repository');
107 | }
108 | 
109 | /**
110 |  * Update an existing GitHub repository
111 |  */
112 | export async function updateRepository(args: unknown): Promise<any> {
113 |   const {
114 |     owner,
115 |     repo,
116 |     description,
117 |     private: isPrivate,
118 |     default_branch,
119 |     has_issues,
120 |     has_projects,
121 |     has_wiki,
122 |     archived,
123 |   } = UpdateRepositorySchema.parse(args);
124 |   const github = getGitHubApi();
125 | 
126 |   return tryCatchAsync(async () => {
127 |     const { data } = await github.getOctokit().repos.update({
128 |       owner,
129 |       repo,
130 |       description,
131 |       private: isPrivate,
132 |       default_branch,
133 |       has_issues,
134 |       has_projects,
135 |       has_wiki,
136 |       archived,
137 |     });
138 | 
139 |     return {
140 |       id: data.id,
141 |       name: data.name,
142 |       full_name: data.full_name,
143 |       private: data.private,
144 |       description: data.description,
145 |       html_url: data.html_url,
146 |       default_branch: data.default_branch,
147 |       has_issues: data.has_issues,
148 |       has_projects: data.has_projects,
149 |       has_wiki: data.has_wiki,
150 |       archived: data.archived,
151 |       updated_at: data.updated_at,
152 |     };
153 |   }, 'Failed to update repository');
154 | }
155 | 
156 | /**
157 |  * Delete a GitHub repository
158 |  */
159 | export async function deleteRepository(args: unknown): Promise<any> {
160 |   const { owner, repo, confirm } = DeleteRepositorySchema.parse(args);
161 |   
162 |   if (!confirm) {
163 |     throw new McpError(
164 |       ErrorCode.InvalidParams,
165 |       'You must confirm deletion by setting confirm to true'
166 |     );
167 |   }
168 |   
169 |   const github = getGitHubApi();
170 | 
171 |   return tryCatchAsync(async () => {
172 |     await github.getOctokit().repos.delete({
173 |       owner,
174 |       repo,
175 |     });
176 | 
177 |     return {
178 |       success: true,
179 |       message: `Repository ${owner}/${repo} has been deleted`,
180 |     };
181 |   }, 'Failed to delete repository');
182 | }
183 | 
184 | /**
185 |  * Create a new branch in a GitHub repository
186 |  */
187 | export async function createBranch(args: unknown): Promise<any> {
188 |   const { owner, repo, branch, from_branch } = CreateBranchSchema.parse(args);
189 |   const github = getGitHubApi();
190 | 
191 |   return tryCatchAsync(async () => {
192 |     // Check if branch already exists
193 |     try {
194 |       await github.getOctokit().git.getRef({
195 |         owner,
196 |         repo,
197 |         ref: `heads/${branch}`,
198 |       });
199 |       
200 |       return {
201 |         success: false,
202 |         message: `Branch '${branch}' already exists in ${owner}/${repo}`,
203 |       };
204 |     } catch (error: any) {
205 |       // If error is not 404 (not found), rethrow it
206 |       if (error.status !== 404) {
207 |         throw error;
208 |       }
209 |     }
210 | 
211 |     // Get the SHA of the latest commit on the source branch
212 |     const sourceBranch = from_branch || await github.getDefaultBranch(owner, repo);
213 |     const { data: refData } = await github.getOctokit().git.getRef({
214 |       owner,
215 |       repo,
216 |       ref: `heads/${sourceBranch}`,
217 |     });
218 | 
219 |     // Create a new branch from the source branch
220 |     const { data } = await github.getOctokit().git.createRef({
221 |       owner,
222 |       repo,
223 |       ref: `refs/heads/${branch}`,
224 |       sha: refData.object.sha,
225 |     });
226 | 
227 |     return {
228 |       success: true,
229 |       ref: data.ref,
230 |       url: data.url,
231 |       object: {
232 |         sha: data.object.sha,
233 |         type: data.object.type,
234 |         url: data.object.url,
235 |       },
236 |       message: `Branch '${branch}' created from '${sourceBranch}' in ${owner}/${repo}`,
237 |     };
238 |   }, 'Failed to create branch');
239 | }
240 | 
241 | /**
242 |  * List commits in a GitHub repository
243 |  */
244 | export async function listCommits(args: unknown): Promise<any> {
245 |   const { owner, repo, sha, page, perPage } = ListCommitsSchema.parse(args);
246 |   const github = getGitHubApi();
247 | 
248 |   return tryCatchAsync(async () => {
249 |     const { data } = await github.getOctokit().repos.listCommits({
250 |       owner,
251 |       repo,
252 |       sha,
253 |       page,
254 |       per_page: perPage,
255 |     });
256 | 
257 |     return data.map((commit) => ({
258 |       sha: commit.sha,
259 |       commit: {
260 |         author: commit.commit.author,
261 |         committer: commit.commit.committer,
262 |         message: commit.commit.message,
263 |       },
264 |       author: commit.author ? {
265 |         login: commit.author.login,
266 |         id: commit.author.id,
267 |         type: commit.author.type,
268 |       } : null,
269 |       committer: commit.committer ? {
270 |         login: commit.committer.login,
271 |         id: commit.committer.id,
272 |         type: commit.committer.type,
273 |       } : null,
274 |       html_url: commit.html_url,
275 |     }));
276 |   }, 'Failed to list commits');
277 | }
278 | 
279 | /**
280 |  * List workflows in a GitHub repository
281 |  */
282 | export async function listWorkflows(args: unknown): Promise<any> {
283 |   const { owner, repo, page, perPage } = ListWorkflowsSchema.parse(args);
284 |   const github = getGitHubApi();
285 | 
286 |   return tryCatchAsync(async () => {
287 |     const { data } = await github.getOctokit().actions.listRepoWorkflows({
288 |       owner,
289 |       repo,
290 |       page,
291 |       per_page: perPage,
292 |     });
293 | 
294 |     return {
295 |       total_count: data.total_count,
296 |       workflows: data.workflows.map((workflow) => ({
297 |         id: workflow.id,
298 |         name: workflow.name,
299 |         path: workflow.path,
300 |         state: workflow.state,
301 |         created_at: workflow.created_at,
302 |         updated_at: workflow.updated_at,
303 |         url: workflow.html_url,
304 |       })),
305 |     };
306 |   }, 'Failed to list workflows');
307 | }
308 | 
309 | /**
310 |  * List workflow runs in a GitHub repository
311 |  */
312 | export async function listWorkflowRuns(args: unknown): Promise<any> {
313 |   const { owner, repo, workflow_id, branch, status, page, perPage } = ListWorkflowRunsSchema.parse(args);
314 |   const github = getGitHubApi();
315 | 
316 |   return tryCatchAsync(async () => {
317 |     let data;
318 | 
319 |     if (workflow_id) {
320 |       // List runs for a specific workflow
321 |       const response = await github.getOctokit().actions.listWorkflowRuns({
322 |         owner,
323 |         repo,
324 |         workflow_id,
325 |         branch,
326 |         status: status as any,
327 |         page,
328 |         per_page: perPage,
329 |       });
330 |       data = response.data;
331 |     } else {
332 |       // List all workflow runs
333 |       const response = await github.getOctokit().actions.listWorkflowRunsForRepo({
334 |         owner,
335 |         repo,
336 |         branch,
337 |         status: status as any,
338 |         page,
339 |         per_page: perPage,
340 |       });
341 |       data = response.data;
342 |     }
343 | 
344 |     return {
345 |       total_count: data.total_count,
346 |       workflow_runs: data.workflow_runs.map((run) => ({
347 |         id: run.id,
348 |         name: run.name,
349 |         workflow_id: run.workflow_id,
350 |         head_branch: run.head_branch,
351 |         head_sha: run.head_sha,
352 |         run_number: run.run_number,
353 |         event: run.event,
354 |         status: run.status,
355 |         conclusion: run.conclusion,
356 |         created_at: run.created_at,
357 |         updated_at: run.updated_at,
358 |         url: run.html_url,
359 |       })),
360 |     };
361 |   }, 'Failed to list workflow runs');
362 | }
363 | 
364 | /**
365 |  * Trigger a workflow run in a GitHub repository
366 |  */
367 | export async function triggerWorkflow(args: unknown): Promise<any> {
368 |   const { owner, repo, workflow_id, ref, inputs } = TriggerWorkflowSchema.parse(args);
369 |   const github = getGitHubApi();
370 | 
371 |   return tryCatchAsync(async () => {
372 |     const { data } = await github.getOctokit().actions.createWorkflowDispatch({
373 |       owner,
374 |       repo,
375 |       workflow_id,
376 |       ref,
377 |       inputs,
378 |     });
379 | 
380 |     return {
381 |       success: true,
382 |       message: `Workflow dispatch event created for workflow ${workflow_id} on ref ${ref}`,
383 |       data,
384 |     };
385 |   }, 'Failed to trigger workflow');
386 | }
387 | 
```

--------------------------------------------------------------------------------
/src/tools/pull-requests.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { getGitHubApi } from '../utils/github-api.js';
  2 | import { tryCatchAsync } from '../utils/error-handling.js';
  3 | import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
  4 | import {
  5 |   ListPullRequestsSchema,
  6 |   GetPullRequestSchema,
  7 |   CreatePullRequestSchema,
  8 |   CreatePullRequestReviewSchema,
  9 |   MergePullRequestSchema,
 10 |   GetPullRequestFilesSchema,
 11 |   GetPullRequestStatusSchema,
 12 |   UpdatePullRequestBranchSchema,
 13 |   GetPullRequestCommentsSchema,
 14 |   GetPullRequestReviewsSchema,
 15 | } from '../utils/validation.js';
 16 | 
 17 | /**
 18 |  * List pull requests in a GitHub repository
 19 |  */
 20 | export async function listPullRequests(args: unknown): Promise<any> {
 21 |   const { owner, repo, state, head, base, sort, direction, per_page, page } = ListPullRequestsSchema.parse(args);
 22 |   const github = getGitHubApi();
 23 | 
 24 |   return tryCatchAsync(async () => {
 25 |     const { data } = await github.getOctokit().pulls.list({
 26 |       owner,
 27 |       repo,
 28 |       state,
 29 |       head,
 30 |       base,
 31 |       sort,
 32 |       direction,
 33 |       per_page,
 34 |       page,
 35 |     });
 36 | 
 37 |     return data.map((pr) => ({
 38 |       id: pr.id,
 39 |       number: pr.number,
 40 |       title: pr.title,
 41 |       state: pr.state,
 42 |       locked: pr.locked,
 43 |       user: pr.user ? {
 44 |         login: pr.user.login,
 45 |         id: pr.user.id,
 46 |         type: pr.user.type,
 47 |       } : null,
 48 |       created_at: pr.created_at,
 49 |       updated_at: pr.updated_at,
 50 |       closed_at: pr.closed_at,
 51 |       merged_at: pr.merged_at,
 52 |       merge_commit_sha: pr.merge_commit_sha,
 53 |       draft: pr.draft,
 54 |       head: {
 55 |         ref: pr.head.ref,
 56 |         sha: pr.head.sha,
 57 |         repo: pr.head.repo ? {
 58 |           name: pr.head.repo.name,
 59 |           full_name: pr.head.repo.full_name,
 60 |           owner: {
 61 |             login: pr.head.repo.owner.login,
 62 |           },
 63 |         } : null,
 64 |       },
 65 |       base: {
 66 |         ref: pr.base.ref,
 67 |         sha: pr.base.sha,
 68 |         repo: pr.base.repo ? {
 69 |           name: pr.base.repo.name,
 70 |           full_name: pr.base.repo.full_name,
 71 |           owner: {
 72 |             login: pr.base.repo.owner.login,
 73 |           },
 74 |         } : null,
 75 |       },
 76 |       body: pr.body,
 77 |       url: pr.html_url,
 78 |     }));
 79 |   }, 'Failed to list pull requests');
 80 | }
 81 | 
 82 | /**
 83 |  * Get a specific pull request in a GitHub repository
 84 |  */
 85 | export async function getPullRequest(args: unknown): Promise<any> {
 86 |   const { owner, repo, pull_number } = GetPullRequestSchema.parse(args);
 87 |   const github = getGitHubApi();
 88 | 
 89 |   return tryCatchAsync(async () => {
 90 |     const { data } = await github.getOctokit().pulls.get({
 91 |       owner,
 92 |       repo,
 93 |       pull_number,
 94 |     });
 95 | 
 96 |     return {
 97 |       id: data.id,
 98 |       number: data.number,
 99 |       title: data.title,
100 |       state: data.state,
101 |       locked: data.locked,
102 |       user: data.user ? {
103 |         login: data.user.login,
104 |         id: data.user.id,
105 |         type: data.user.type,
106 |       } : null,
107 |       created_at: data.created_at,
108 |       updated_at: data.updated_at,
109 |       closed_at: data.closed_at,
110 |       merged_at: data.merged_at,
111 |       merge_commit_sha: data.merge_commit_sha,
112 |       draft: data.draft,
113 |       head: {
114 |         ref: data.head.ref,
115 |         sha: data.head.sha,
116 |         repo: data.head.repo ? {
117 |           name: data.head.repo.name,
118 |           full_name: data.head.repo.full_name,
119 |           owner: {
120 |             login: data.head.repo.owner.login,
121 |           },
122 |         } : null,
123 |       },
124 |       base: {
125 |         ref: data.base.ref,
126 |         sha: data.base.sha,
127 |         repo: data.base.repo ? {
128 |           name: data.base.repo.name,
129 |           full_name: data.base.repo.full_name,
130 |           owner: {
131 |             login: data.base.repo.owner.login,
132 |           },
133 |         } : null,
134 |       },
135 |       body: data.body,
136 |       url: data.html_url,
137 |       mergeable: data.mergeable,
138 |       mergeable_state: data.mergeable_state,
139 |       merged: data.merged,
140 |       merged_by: data.merged_by ? {
141 |         login: data.merged_by.login,
142 |         id: data.merged_by.id,
143 |       } : null,
144 |       comments: data.comments,
145 |       review_comments: data.review_comments,
146 |       commits: data.commits,
147 |       additions: data.additions,
148 |       deletions: data.deletions,
149 |       changed_files: data.changed_files,
150 |     };
151 |   }, 'Failed to get pull request');
152 | }
153 | 
154 | /**
155 |  * Create a new pull request in a GitHub repository
156 |  */
157 | export async function createPullRequest(args: unknown): Promise<any> {
158 |   const { owner, repo, title, head, base, body, draft, maintainer_can_modify } = CreatePullRequestSchema.parse(args);
159 |   const github = getGitHubApi();
160 | 
161 |   return tryCatchAsync(async () => {
162 |     const { data } = await github.getOctokit().pulls.create({
163 |       owner,
164 |       repo,
165 |       title,
166 |       head,
167 |       base,
168 |       body,
169 |       draft,
170 |       maintainer_can_modify,
171 |     });
172 | 
173 |     return {
174 |       id: data.id,
175 |       number: data.number,
176 |       title: data.title,
177 |       state: data.state,
178 |       user: data.user ? {
179 |         login: data.user.login,
180 |         id: data.user.id,
181 |       } : null,
182 |       created_at: data.created_at,
183 |       updated_at: data.updated_at,
184 |       head: {
185 |         ref: data.head.ref,
186 |         sha: data.head.sha,
187 |         repo: data.head.repo ? {
188 |           name: data.head.repo.name,
189 |           full_name: data.head.repo.full_name,
190 |         } : null,
191 |       },
192 |       base: {
193 |         ref: data.base.ref,
194 |         sha: data.base.sha,
195 |         repo: data.base.repo ? {
196 |           name: data.base.repo.name,
197 |           full_name: data.base.repo.full_name,
198 |         } : null,
199 |       },
200 |       body: data.body,
201 |       draft: data.draft,
202 |       url: data.html_url,
203 |     };
204 |   }, 'Failed to create pull request');
205 | }
206 | 
207 | /**
208 |  * Create a review on a pull request
209 |  */
210 | export async function createPullRequestReview(args: unknown): Promise<any> {
211 |   const { owner, repo, pull_number, body, event, commit_id, comments } = CreatePullRequestReviewSchema.parse(args);
212 |   const github = getGitHubApi();
213 | 
214 |   return tryCatchAsync(async () => {
215 |     const { data } = await github.getOctokit().pulls.createReview({
216 |       owner,
217 |       repo,
218 |       pull_number,
219 |       body,
220 |       event,
221 |       commit_id,
222 |       comments,
223 |     });
224 | 
225 |     return {
226 |       id: data.id,
227 |       user: data.user ? {
228 |         login: data.user.login,
229 |         id: data.user.id,
230 |       } : null,
231 |       body: data.body,
232 |       state: data.state,
233 |       commit_id: data.commit_id,
234 |       submitted_at: data.submitted_at,
235 |       url: data.html_url,
236 |     };
237 |   }, 'Failed to create pull request review');
238 | }
239 | 
240 | /**
241 |  * Merge a pull request
242 |  */
243 | export async function mergePullRequest(args: unknown): Promise<any> {
244 |   const { owner, repo, pull_number, commit_title, commit_message, merge_method } = MergePullRequestSchema.parse(args);
245 |   const github = getGitHubApi();
246 | 
247 |   return tryCatchAsync(async () => {
248 |     const { data } = await github.getOctokit().pulls.merge({
249 |       owner,
250 |       repo,
251 |       pull_number,
252 |       commit_title,
253 |       commit_message,
254 |       merge_method,
255 |     });
256 | 
257 |     return {
258 |       merged: data.merged,
259 |       message: data.message,
260 |       sha: data.sha,
261 |     };
262 |   }, 'Failed to merge pull request');
263 | }
264 | 
265 | /**
266 |  * Get the list of files changed in a pull request
267 |  */
268 | export async function getPullRequestFiles(args: unknown): Promise<any> {
269 |   const { owner, repo, pull_number } = GetPullRequestFilesSchema.parse(args);
270 |   const github = getGitHubApi();
271 | 
272 |   return tryCatchAsync(async () => {
273 |     const { data } = await github.getOctokit().pulls.listFiles({
274 |       owner,
275 |       repo,
276 |       pull_number,
277 |     });
278 | 
279 |     return data.map((file) => ({
280 |       sha: file.sha,
281 |       filename: file.filename,
282 |       status: file.status,
283 |       additions: file.additions,
284 |       deletions: file.deletions,
285 |       changes: file.changes,
286 |       blob_url: file.blob_url,
287 |       raw_url: file.raw_url,
288 |       contents_url: file.contents_url,
289 |       patch: file.patch,
290 |     }));
291 |   }, 'Failed to get pull request files');
292 | }
293 | 
294 | /**
295 |  * Get the combined status of all status checks for a pull request
296 |  */
297 | export async function getPullRequestStatus(args: unknown): Promise<any> {
298 |   const { owner, repo, pull_number } = GetPullRequestStatusSchema.parse(args);
299 |   const github = getGitHubApi();
300 | 
301 |   return tryCatchAsync(async () => {
302 |     // First get the pull request to get the head SHA
303 |     const { data: pr } = await github.getOctokit().pulls.get({
304 |       owner,
305 |       repo,
306 |       pull_number,
307 |     });
308 | 
309 |     // Then get the combined status for the head SHA
310 |     const { data } = await github.getOctokit().repos.getCombinedStatusForRef({
311 |       owner,
312 |       repo,
313 |       ref: pr.head.sha,
314 |     });
315 | 
316 |     return {
317 |       state: data.state,
318 |       statuses: data.statuses.map((status) => ({
319 |         context: status.context,
320 |         state: status.state,
321 |         description: status.description,
322 |         target_url: status.target_url,
323 |         created_at: status.created_at,
324 |         updated_at: status.updated_at,
325 |       })),
326 |       sha: data.sha,
327 |       total_count: data.total_count,
328 |       repository: {
329 |         name: data.repository.name,
330 |         full_name: data.repository.full_name,
331 |         owner: {
332 |           login: data.repository.owner.login,
333 |         },
334 |       },
335 |     };
336 |   }, 'Failed to get pull request status');
337 | }
338 | 
339 | /**
340 |  * Update a pull request branch with the latest changes from the base branch
341 |  */
342 | export async function updatePullRequestBranch(args: unknown): Promise<any> {
343 |   const { owner, repo, pull_number, expected_head_sha } = UpdatePullRequestBranchSchema.parse(args);
344 |   const github = getGitHubApi();
345 | 
346 |   return tryCatchAsync(async () => {
347 |     const { data } = await github.getOctokit().pulls.updateBranch({
348 |       owner,
349 |       repo,
350 |       pull_number,
351 |       expected_head_sha,
352 |     });
353 | 
354 |     return {
355 |       message: data.message,
356 |       url: data.url,
357 |     };
358 |   }, 'Failed to update pull request branch');
359 | }
360 | 
361 | /**
362 |  * Get the review comments on a pull request
363 |  */
364 | export async function getPullRequestComments(args: unknown): Promise<any> {
365 |   const { owner, repo, pull_number } = GetPullRequestCommentsSchema.parse(args);
366 |   const github = getGitHubApi();
367 | 
368 |   return tryCatchAsync(async () => {
369 |     const { data } = await github.getOctokit().pulls.listReviewComments({
370 |       owner,
371 |       repo,
372 |       pull_number,
373 |     });
374 | 
375 |     return data.map((comment) => ({
376 |       id: comment.id,
377 |       user: comment.user ? {
378 |         login: comment.user.login,
379 |         id: comment.user.id,
380 |       } : null,
381 |       body: comment.body,
382 |       created_at: comment.created_at,
383 |       updated_at: comment.updated_at,
384 |       path: comment.path,
385 |       position: comment.position,
386 |       commit_id: comment.commit_id,
387 |       url: comment.html_url,
388 |     }));
389 |   }, 'Failed to get pull request comments');
390 | }
391 | 
392 | /**
393 |  * Get the reviews on a pull request
394 |  */
395 | export async function getPullRequestReviews(args: unknown): Promise<any> {
396 |   const { owner, repo, pull_number } = GetPullRequestReviewsSchema.parse(args);
397 |   const github = getGitHubApi();
398 | 
399 |   return tryCatchAsync(async () => {
400 |     const { data } = await github.getOctokit().pulls.listReviews({
401 |       owner,
402 |       repo,
403 |       pull_number,
404 |     });
405 | 
406 |     return data.map((review) => ({
407 |       id: review.id,
408 |       user: review.user ? {
409 |         login: review.user.login,
410 |         id: review.user.id,
411 |       } : null,
412 |       body: review.body,
413 |       state: review.state,
414 |       commit_id: review.commit_id,
415 |       submitted_at: review.submitted_at,
416 |       url: review.html_url,
417 |     }));
418 |   }, 'Failed to get pull request reviews');
419 | }
420 | 
```

--------------------------------------------------------------------------------
/src/server.ts:
--------------------------------------------------------------------------------

```typescript
   1 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
   2 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
   3 | import {
   4 |   CallToolRequestSchema,
   5 |   ListToolsRequestSchema,
   6 |   McpError,
   7 |   ErrorCode,
   8 | } from '@modelcontextprotocol/sdk/types.js';
   9 | import { validateEnvVariables } from './utils/error-handling.js';
  10 | import { safeJsonParse } from './utils/error-handling.js';
  11 | 
  12 | // Import all tools
  13 | import {
  14 |   searchRepositories,
  15 |   createRepository,
  16 |   updateRepository,
  17 |   deleteRepository,
  18 |   createBranch,
  19 |   listCommits,
  20 |   listWorkflows,
  21 |   listWorkflowRuns,
  22 |   triggerWorkflow,
  23 | } from './tools/repository.js';
  24 | 
  25 | import {
  26 |   createOrUpdateFile,
  27 |   pushFiles,
  28 |   getFileContents,
  29 |   forkRepository,
  30 |   getPullRequestFiles,
  31 | } from './tools/files.js';
  32 | 
  33 | import {
  34 |   listIssues,
  35 |   getIssue,
  36 |   createIssue,
  37 |   updateIssue,
  38 |   addIssueComment,
  39 |   searchIssues as searchIssuesAndPRs,
  40 | } from './tools/issues.js';
  41 | 
  42 | import {
  43 |   listPullRequests,
  44 |   getPullRequest,
  45 |   createPullRequest,
  46 |   createPullRequestReview,
  47 |   mergePullRequest,
  48 |   getPullRequestStatus,
  49 |   updatePullRequestBranch,
  50 |   getPullRequestComments,
  51 |   getPullRequestReviews,
  52 | } from './tools/pull-requests.js';
  53 | 
  54 | import {
  55 |   searchCode,
  56 |   searchIssues,
  57 |   searchUsers,
  58 |   getLicenseInfo,
  59 |   getEnterpriseStats,
  60 | } from './tools/search.js';
  61 | 
  62 | /**
  63 |  * GitHub Enterprise MCP Server
  64 |  */
  65 | export class GitHubEnterpriseServer {
  66 |   private server: Server;
  67 | 
  68 |   constructor() {
  69 |     // Validate required environment variables
  70 |     validateEnvVariables(['GITHUB_PERSONAL_ACCESS_TOKEN']);
  71 | 
  72 |     // Create MCP server
  73 |     this.server = new Server(
  74 |       {
  75 |         name: 'github-enterprise',
  76 |         version: '1.0.0',
  77 |       },
  78 |       {
  79 |         capabilities: {
  80 |           tools: {},
  81 |         },
  82 |       }
  83 |     );
  84 | 
  85 |     // Set up request handlers
  86 |     this.setupRequestHandlers();
  87 | 
  88 |     // Set up error handler
  89 |     this.server.onerror = (error) => {
  90 |       console.error('[GitHub Enterprise MCP Server Error]', error);
  91 |     };
  92 |   }
  93 | 
  94 |   /**
  95 |    * Set up request handlers for the MCP server
  96 |    */
  97 |   private setupRequestHandlers(): void {
  98 |     // List available tools
  99 |     this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
 100 |       tools: [
 101 |         // Repository tools
 102 |         {
 103 |           name: 'search-repositories',
 104 |           description: 'Search for GitHub repositories',
 105 |           inputSchema: {
 106 |             type: 'object',
 107 |             properties: {
 108 |               query: {
 109 |                 type: 'string',
 110 |                 description: 'Search query (see GitHub search syntax)',
 111 |               },
 112 |               page: {
 113 |                 type: 'number',
 114 |                 description: 'Page number for pagination (default: 1)',
 115 |               },
 116 |               perPage: {
 117 |                 type: 'number',
 118 |                 description: 'Number of results per page (default: 30, max: 100)',
 119 |               },
 120 |             },
 121 |             required: ['query'],
 122 |             additionalProperties: false,
 123 |           },
 124 |         },
 125 |         {
 126 |           name: 'create-repository',
 127 |           description: 'Create a new GitHub repository in your account',
 128 |           inputSchema: {
 129 |             type: 'object',
 130 |             properties: {
 131 |               name: {
 132 |                 type: 'string',
 133 |                 description: 'Repository name',
 134 |               },
 135 |               description: {
 136 |                 type: 'string',
 137 |                 description: 'Repository description',
 138 |               },
 139 |               private: {
 140 |                 type: 'boolean',
 141 |                 description: 'Whether the repository should be private',
 142 |               },
 143 |               autoInit: {
 144 |                 type: 'boolean',
 145 |                 description: 'Initialize with README.md',
 146 |               },
 147 |             },
 148 |             required: ['name'],
 149 |             additionalProperties: false,
 150 |           },
 151 |         },
 152 |         {
 153 |           name: 'update-repository',
 154 |           description: 'Update an existing GitHub repository',
 155 |           inputSchema: {
 156 |             type: 'object',
 157 |             properties: {
 158 |               owner: {
 159 |                 type: 'string',
 160 |                 description: 'Repository owner',
 161 |               },
 162 |               repo: {
 163 |                 type: 'string',
 164 |                 description: 'Repository name',
 165 |               },
 166 |               description: {
 167 |                 type: 'string',
 168 |                 description: 'New description',
 169 |               },
 170 |               private: {
 171 |                 type: 'boolean',
 172 |                 description: 'Change privacy setting',
 173 |               },
 174 |               default_branch: {
 175 |                 type: 'string',
 176 |                 description: 'Change default branch',
 177 |               },
 178 |               has_issues: {
 179 |                 type: 'boolean',
 180 |                 description: 'Enable/disable issues',
 181 |               },
 182 |               has_projects: {
 183 |                 type: 'boolean',
 184 |                 description: 'Enable/disable projects',
 185 |               },
 186 |               has_wiki: {
 187 |                 type: 'boolean',
 188 |                 description: 'Enable/disable wiki',
 189 |               },
 190 |               archived: {
 191 |                 type: 'boolean',
 192 |                 description: 'Archive/unarchive repository',
 193 |               },
 194 |             },
 195 |             required: ['owner', 'repo'],
 196 |             additionalProperties: false,
 197 |           },
 198 |         },
 199 |         {
 200 |           name: 'delete-repository',
 201 |           description: 'Delete a GitHub repository',
 202 |           inputSchema: {
 203 |             type: 'object',
 204 |             properties: {
 205 |               owner: {
 206 |                 type: 'string',
 207 |                 description: 'Repository owner',
 208 |               },
 209 |               repo: {
 210 |                 type: 'string',
 211 |                 description: 'Repository name',
 212 |               },
 213 |               confirm: {
 214 |                 type: 'boolean',
 215 |                 description: 'Confirmation for deletion (must be true)',
 216 |               },
 217 |             },
 218 |             required: ['owner', 'repo', 'confirm'],
 219 |             additionalProperties: false,
 220 |           },
 221 |         },
 222 |         {
 223 |           name: 'create-branch',
 224 |           description: 'Create a new branch in a GitHub repository',
 225 |           inputSchema: {
 226 |             type: 'object',
 227 |             properties: {
 228 |               owner: {
 229 |                 type: 'string',
 230 |                 description: 'Repository owner',
 231 |               },
 232 |               repo: {
 233 |                 type: 'string',
 234 |                 description: 'Repository name',
 235 |               },
 236 |               branch: {
 237 |                 type: 'string',
 238 |                 description: 'Name for the new branch',
 239 |               },
 240 |               from_branch: {
 241 |                 type: 'string',
 242 |                 description: 'Source branch to create from (defaults to the repository\'s default branch)',
 243 |               },
 244 |             },
 245 |             required: ['owner', 'repo', 'branch'],
 246 |             additionalProperties: false,
 247 |           },
 248 |         },
 249 |         {
 250 |           name: 'list-commits',
 251 |           description: 'Get list of commits of a branch in a GitHub repository',
 252 |           inputSchema: {
 253 |             type: 'object',
 254 |             properties: {
 255 |               owner: {
 256 |                 type: 'string',
 257 |               },
 258 |               repo: {
 259 |                 type: 'string',
 260 |               },
 261 |               sha: {
 262 |                 type: 'string',
 263 |               },
 264 |               page: {
 265 |                 type: 'number',
 266 |               },
 267 |               perPage: {
 268 |                 type: 'number',
 269 |               },
 270 |             },
 271 |             required: ['owner', 'repo'],
 272 |             additionalProperties: false,
 273 |           },
 274 |         },
 275 |         {
 276 |           name: 'list-workflows',
 277 |           description: 'List workflows in a GitHub repository',
 278 |           inputSchema: {
 279 |             type: 'object',
 280 |             properties: {
 281 |               owner: {
 282 |                 type: 'string',
 283 |                 description: 'Repository owner',
 284 |               },
 285 |               repo: {
 286 |                 type: 'string',
 287 |                 description: 'Repository name',
 288 |               },
 289 |               page: {
 290 |                 type: 'integer',
 291 |                 description: 'Page number',
 292 |               },
 293 |               perPage: {
 294 |                 type: 'integer',
 295 |                 description: 'Items per page',
 296 |               },
 297 |             },
 298 |             required: ['owner', 'repo'],
 299 |             additionalProperties: false,
 300 |           },
 301 |         },
 302 |         {
 303 |           name: 'list-workflow-runs',
 304 |           description: 'List workflow runs in a GitHub repository',
 305 |           inputSchema: {
 306 |             type: 'object',
 307 |             properties: {
 308 |               owner: {
 309 |                 type: 'string',
 310 |                 description: 'Repository owner',
 311 |               },
 312 |               repo: {
 313 |                 type: 'string',
 314 |                 description: 'Repository name',
 315 |               },
 316 |               workflow_id: {
 317 |                 type: ['string', 'number'],
 318 |                 description: 'Workflow ID or file name',
 319 |               },
 320 |               branch: {
 321 |                 type: 'string',
 322 |                 description: 'Filter by branch name',
 323 |               },
 324 |               status: {
 325 |                 type: 'string',
 326 |                 enum: [
 327 |                   'completed',
 328 |                   'action_required',
 329 |                   'cancelled',
 330 |                   'failure',
 331 |                   'neutral',
 332 |                   'skipped',
 333 |                   'stale',
 334 |                   'success',
 335 |                   'timed_out',
 336 |                   'in_progress',
 337 |                   'queued',
 338 |                   'requested',
 339 |                   'waiting',
 340 |                 ],
 341 |                 description: 'Filter by run status',
 342 |               },
 343 |               page: {
 344 |                 type: 'integer',
 345 |                 description: 'Page number',
 346 |               },
 347 |               perPage: {
 348 |                 type: 'integer',
 349 |                 description: 'Items per page',
 350 |               },
 351 |             },
 352 |             required: ['owner', 'repo'],
 353 |             additionalProperties: false,
 354 |           },
 355 |         },
 356 |         {
 357 |           name: 'trigger-workflow',
 358 |           description: 'Trigger a workflow run in a GitHub repository',
 359 |           inputSchema: {
 360 |             type: 'object',
 361 |             properties: {
 362 |               owner: {
 363 |                 type: 'string',
 364 |                 description: 'Repository owner',
 365 |               },
 366 |               repo: {
 367 |                 type: 'string',
 368 |                 description: 'Repository name',
 369 |               },
 370 |               workflow_id: {
 371 |                 type: ['string', 'number'],
 372 |                 description: 'Workflow ID or file name',
 373 |               },
 374 |               ref: {
 375 |                 type: 'string',
 376 |                 description: 'Git reference (branch, tag, SHA)',
 377 |               },
 378 |               inputs: {
 379 |                 type: 'object',
 380 |                 description: 'Workflow inputs',
 381 |               },
 382 |             },
 383 |             required: ['owner', 'repo', 'workflow_id', 'ref'],
 384 |             additionalProperties: false,
 385 |           },
 386 |         },
 387 |         // File operations tools
 388 |         {
 389 |           name: 'create-or-update-file',
 390 |           description: 'Create or update a single file in a GitHub repository',
 391 |           inputSchema: {
 392 |             type: 'object',
 393 |             properties: {
 394 |               owner: {
 395 |                 type: 'string',
 396 |                 description: 'Repository owner (username or organization)',
 397 |               },
 398 |               repo: {
 399 |                 type: 'string',
 400 |                 description: 'Repository name',
 401 |               },
 402 |               path: {
 403 |                 type: 'string',
 404 |                 description: 'Path where to create/update the file',
 405 |               },
 406 |               content: {
 407 |                 type: 'string',
 408 |                 description: 'Content of the file',
 409 |               },
 410 |               message: {
 411 |                 type: 'string',
 412 |                 description: 'Commit message',
 413 |               },
 414 |               branch: {
 415 |                 type: 'string',
 416 |                 description: 'Branch to create/update the file in',
 417 |               },
 418 |               sha: {
 419 |                 type: 'string',
 420 |                 description: 'SHA of the file being replaced (required when updating existing files)',
 421 |               },
 422 |             },
 423 |             required: ['owner', 'repo', 'path', 'content', 'message', 'branch'],
 424 |             additionalProperties: false,
 425 |           },
 426 |         },
 427 |         {
 428 |           name: 'push-files',
 429 |           description: 'Push multiple files to a GitHub repository in a single commit',
 430 |           inputSchema: {
 431 |             type: 'object',
 432 |             properties: {
 433 |               owner: {
 434 |                 type: 'string',
 435 |                 description: 'Repository owner (username or organization)',
 436 |               },
 437 |               repo: {
 438 |                 type: 'string',
 439 |                 description: 'Repository name',
 440 |               },
 441 |               branch: {
 442 |                 type: 'string',
 443 |                 description: 'Branch to push to (e.g., \'main\' or \'master\')',
 444 |               },
 445 |               files: {
 446 |                 type: 'array',
 447 |                 items: {
 448 |                   type: 'object',
 449 |                   properties: {
 450 |                     path: {
 451 |                       type: 'string',
 452 |                     },
 453 |                     content: {
 454 |                       type: 'string',
 455 |                     },
 456 |                   },
 457 |                   required: ['path', 'content'],
 458 |                   additionalProperties: false,
 459 |                 },
 460 |                 description: 'Array of files to push',
 461 |               },
 462 |               message: {
 463 |                 type: 'string',
 464 |                 description: 'Commit message',
 465 |               },
 466 |             },
 467 |             required: ['owner', 'repo', 'branch', 'files', 'message'],
 468 |             additionalProperties: false,
 469 |           },
 470 |         },
 471 |         {
 472 |           name: 'get-file-contents',
 473 |           description: 'Get the contents of a file or directory from a GitHub repository',
 474 |           inputSchema: {
 475 |             type: 'object',
 476 |             properties: {
 477 |               owner: {
 478 |                 type: 'string',
 479 |                 description: 'Repository owner (username or organization)',
 480 |               },
 481 |               repo: {
 482 |                 type: 'string',
 483 |                 description: 'Repository name',
 484 |               },
 485 |               path: {
 486 |                 type: 'string',
 487 |                 description: 'Path to the file or directory',
 488 |               },
 489 |               branch: {
 490 |                 type: 'string',
 491 |                 description: 'Branch to get contents from',
 492 |               },
 493 |             },
 494 |             required: ['owner', 'repo', 'path'],
 495 |             additionalProperties: false,
 496 |           },
 497 |         },
 498 |         {
 499 |           name: 'fork-repository',
 500 |           description: 'Fork a GitHub repository to your account or specified organization',
 501 |           inputSchema: {
 502 |             type: 'object',
 503 |             properties: {
 504 |               owner: {
 505 |                 type: 'string',
 506 |                 description: 'Repository owner (username or organization)',
 507 |               },
 508 |               repo: {
 509 |                 type: 'string',
 510 |                 description: 'Repository name',
 511 |               },
 512 |               organization: {
 513 |                 type: 'string',
 514 |                 description: 'Optional: organization to fork to (defaults to your personal account)',
 515 |               },
 516 |             },
 517 |             required: ['owner', 'repo'],
 518 |             additionalProperties: false,
 519 |           },
 520 |         },
 521 |         // Issue tools
 522 |         {
 523 |           name: 'list-issues',
 524 |           description: 'List and filter repository issues',
 525 |           inputSchema: {
 526 |             type: 'object',
 527 |             properties: {
 528 |               owner: {
 529 |                 type: 'string',
 530 |               },
 531 |               repo: {
 532 |                 type: 'string',
 533 |               },
 534 |               state: {
 535 |                 type: 'string',
 536 |                 enum: ['open', 'closed', 'all'],
 537 |               },
 538 |               labels: {
 539 |                 type: 'array',
 540 |                 items: {
 541 |                   type: 'string',
 542 |                 },
 543 |               },
 544 |               sort: {
 545 |                 type: 'string',
 546 |                 enum: ['created', 'updated', 'comments'],
 547 |               },
 548 |               direction: {
 549 |                 type: 'string',
 550 |                 enum: ['asc', 'desc'],
 551 |               },
 552 |               since: {
 553 |                 type: 'string',
 554 |               },
 555 |               page: {
 556 |                 type: 'number',
 557 |               },
 558 |               per_page: {
 559 |                 type: 'number',
 560 |               },
 561 |             },
 562 |             required: ['owner', 'repo'],
 563 |             additionalProperties: false,
 564 |           },
 565 |         },
 566 |         {
 567 |           name: 'get-issue',
 568 |           description: 'Get details of a specific issue in a GitHub repository.',
 569 |           inputSchema: {
 570 |             type: 'object',
 571 |             properties: {
 572 |               owner: {
 573 |                 type: 'string',
 574 |               },
 575 |               repo: {
 576 |                 type: 'string',
 577 |               },
 578 |               issue_number: {
 579 |                 type: 'number',
 580 |               },
 581 |             },
 582 |             required: ['owner', 'repo', 'issue_number'],
 583 |             additionalProperties: false,
 584 |           },
 585 |         },
 586 |         {
 587 |           name: 'create-issue',
 588 |           description: 'Create a new issue in a GitHub repository',
 589 |           inputSchema: {
 590 |             type: 'object',
 591 |             properties: {
 592 |               owner: {
 593 |                 type: 'string',
 594 |               },
 595 |               repo: {
 596 |                 type: 'string',
 597 |               },
 598 |               title: {
 599 |                 type: 'string',
 600 |               },
 601 |               body: {
 602 |                 type: 'string',
 603 |               },
 604 |               assignees: {
 605 |                 type: 'array',
 606 |                 items: {
 607 |                   type: 'string',
 608 |                 },
 609 |               },
 610 |               milestone: {
 611 |                 type: 'number',
 612 |               },
 613 |               labels: {
 614 |                 type: 'array',
 615 |                 items: {
 616 |                   type: 'string',
 617 |                 },
 618 |               },
 619 |             },
 620 |             required: ['owner', 'repo', 'title'],
 621 |             additionalProperties: false,
 622 |           },
 623 |         },
 624 |         {
 625 |           name: 'update-issue',
 626 |           description: 'Update an existing issue in a GitHub repository',
 627 |           inputSchema: {
 628 |             type: 'object',
 629 |             properties: {
 630 |               owner: {
 631 |                 type: 'string',
 632 |               },
 633 |               repo: {
 634 |                 type: 'string',
 635 |               },
 636 |               issue_number: {
 637 |                 type: 'number',
 638 |               },
 639 |               title: {
 640 |                 type: 'string',
 641 |               },
 642 |               body: {
 643 |                 type: 'string',
 644 |               },
 645 |               assignees: {
 646 |                 type: 'array',
 647 |                 items: {
 648 |                   type: 'string',
 649 |                 },
 650 |               },
 651 |               milestone: {
 652 |                 type: 'number',
 653 |               },
 654 |               labels: {
 655 |                 type: 'array',
 656 |                 items: {
 657 |                   type: 'string',
 658 |                 },
 659 |               },
 660 |               state: {
 661 |                 type: 'string',
 662 |                 enum: ['open', 'closed'],
 663 |               },
 664 |             },
 665 |             required: ['owner', 'repo', 'issue_number'],
 666 |             additionalProperties: false,
 667 |           },
 668 |         },
 669 |         {
 670 |           name: 'add-issue-comment',
 671 |           description: 'Add a comment to an existing issue',
 672 |           inputSchema: {
 673 |             type: 'object',
 674 |             properties: {
 675 |               owner: {
 676 |                 type: 'string',
 677 |               },
 678 |               repo: {
 679 |                 type: 'string',
 680 |               },
 681 |               issue_number: {
 682 |                 type: 'number',
 683 |               },
 684 |               body: {
 685 |                 type: 'string',
 686 |               },
 687 |             },
 688 |             required: ['owner', 'repo', 'issue_number', 'body'],
 689 |             additionalProperties: false,
 690 |           },
 691 |         },
 692 |         // Pull request tools
 693 |         {
 694 |           name: 'list-pull-requests',
 695 |           description: 'List and filter repository pull requests',
 696 |           inputSchema: {
 697 |             type: 'object',
 698 |             properties: {
 699 |               owner: {
 700 |                 type: 'string',
 701 |                 description: 'Repository owner (username or organization)',
 702 |               },
 703 |               repo: {
 704 |                 type: 'string',
 705 |                 description: 'Repository name',
 706 |               },
 707 |               state: {
 708 |                 type: 'string',
 709 |                 enum: ['open', 'closed', 'all'],
 710 |                 description: 'State of the pull requests to return',
 711 |               },
 712 |               head: {
 713 |                 type: 'string',
 714 |                 description: 'Filter by head user or head organization and branch name',
 715 |               },
 716 |               base: {
 717 |                 type: 'string',
 718 |                 description: 'Filter by base branch name',
 719 |               },
 720 |               sort: {
 721 |                 type: 'string',
 722 |                 enum: ['created', 'updated', 'popularity', 'long-running'],
 723 |                 description: 'What to sort results by',
 724 |               },
 725 |               direction: {
 726 |                 type: 'string',
 727 |                 enum: ['asc', 'desc'],
 728 |                 description: 'The direction of the sort',
 729 |               },
 730 |               per_page: {
 731 |                 type: 'number',
 732 |                 description: 'Results per page (max 100)',
 733 |               },
 734 |               page: {
 735 |                 type: 'number',
 736 |                 description: 'Page number of the results',
 737 |               },
 738 |             },
 739 |             required: ['owner', 'repo'],
 740 |             additionalProperties: false,
 741 |           },
 742 |         },
 743 |         {
 744 |           name: 'get-pull-request',
 745 |           description: 'Get details of a specific pull request',
 746 |           inputSchema: {
 747 |             type: 'object',
 748 |             properties: {
 749 |               owner: {
 750 |                 type: 'string',
 751 |                 description: 'Repository owner (username or organization)',
 752 |               },
 753 |               repo: {
 754 |                 type: 'string',
 755 |                 description: 'Repository name',
 756 |               },
 757 |               pull_number: {
 758 |                 type: 'number',
 759 |                 description: 'Pull request number',
 760 |               },
 761 |             },
 762 |             required: ['owner', 'repo', 'pull_number'],
 763 |             additionalProperties: false,
 764 |           },
 765 |         },
 766 |         {
 767 |           name: 'create-pull-request',
 768 |           description: 'Create a new pull request in a GitHub repository',
 769 |           inputSchema: {
 770 |             type: 'object',
 771 |             properties: {
 772 |               owner: {
 773 |                 type: 'string',
 774 |                 description: 'Repository owner (username or organization)',
 775 |               },
 776 |               repo: {
 777 |                 type: 'string',
 778 |                 description: 'Repository name',
 779 |               },
 780 |               title: {
 781 |                 type: 'string',
 782 |                 description: 'Pull request title',
 783 |               },
 784 |               body: {
 785 |                 type: 'string',
 786 |                 description: 'Pull request body/description',
 787 |               },
 788 |               head: {
 789 |                 type: 'string',
 790 |                 description: 'The name of the branch where your changes are implemented',
 791 |               },
 792 |               base: {
 793 |                 type: 'string',
 794 |                 description: 'The name of the branch you want the changes pulled into',
 795 |               },
 796 |               draft: {
 797 |                 type: 'boolean',
 798 |                 description: 'Whether to create the pull request as a draft',
 799 |               },
 800 |               maintainer_can_modify: {
 801 |                 type: 'boolean',
 802 |                 description: 'Whether maintainers can modify the pull request',
 803 |               },
 804 |             },
 805 |             required: ['owner', 'repo', 'title', 'head', 'base'],
 806 |             additionalProperties: false,
 807 |           },
 808 |         },
 809 |         {
 810 |           name: 'create-pull-request-review',
 811 |           description: 'Create a review on a pull request',
 812 |           inputSchema: {
 813 |             type: 'object',
 814 |             properties: {
 815 |               owner: {
 816 |                 type: 'string',
 817 |                 description: 'Repository owner (username or organization)',
 818 |               },
 819 |               repo: {
 820 |                 type: 'string',
 821 |                 description: 'Repository name',
 822 |               },
 823 |               pull_number: {
 824 |                 type: 'number',
 825 |                 description: 'Pull request number',
 826 |               },
 827 |               commit_id: {
 828 |                 type: 'string',
 829 |                 description: 'The SHA of the commit that needs a review',
 830 |               },
 831 |               body: {
 832 |                 type: 'string',
 833 |                 description: 'The body text of the review',
 834 |               },
 835 |               event: {
 836 |                 type: 'string',
 837 |                 enum: ['APPROVE', 'REQUEST_CHANGES', 'COMMENT'],
 838 |                 description: 'The review action to perform',
 839 |               },
 840 |               comments: {
 841 |                 type: 'array',
 842 |                 items: {
 843 |                   type: 'object',
 844 |                   properties: {
 845 |                     path: {
 846 |                       type: 'string',
 847 |                       description: 'The relative path to the file being commented on',
 848 |                     },
 849 |                     position: {
 850 |                       type: 'number',
 851 |                       description: 'The position in the diff where you want to add a review comment',
 852 |                     },
 853 |                     body: {
 854 |                       type: 'string',
 855 |                       description: 'Text of the review comment',
 856 |                     },
 857 |                   },
 858 |                   required: ['path', 'position', 'body'],
 859 |                   additionalProperties: false,
 860 |                 },
 861 |                 description: 'Comments to post as part of the review',
 862 |               },
 863 |             },
 864 |             required: ['owner', 'repo', 'pull_number', 'body', 'event'],
 865 |             additionalProperties: false,
 866 |           },
 867 |         },
 868 |         {
 869 |           name: 'merge-pull-request',
 870 |           description: 'Merge a pull request',
 871 |           inputSchema: {
 872 |             type: 'object',
 873 |             properties: {
 874 |               owner: {
 875 |                 type: 'string',
 876 |                 description: 'Repository owner (username or organization)',
 877 |               },
 878 |               repo: {
 879 |                 type: 'string',
 880 |                 description: 'Repository name',
 881 |               },
 882 |               pull_number: {
 883 |                 type: 'number',
 884 |                 description: 'Pull request number',
 885 |               },
 886 |               commit_title: {
 887 |                 type: 'string',
 888 |                 description: 'Title for the automatic commit message',
 889 |               },
 890 |               commit_message: {
 891 |                 type: 'string',
 892 |                 description: 'Extra detail to append to automatic commit message',
 893 |               },
 894 |               merge_method: {
 895 |                 type: 'string',
 896 |                 enum: ['merge', 'squash', 'rebase'],
 897 |                 description: 'Merge method to use',
 898 |               },
 899 |             },
 900 |             required: ['owner', 'repo', 'pull_number'],
 901 |             additionalProperties: false,
 902 |           },
 903 |         },
 904 |         {
 905 |           name: 'get-pull-request-files',
 906 |           description: 'Get the list of files changed in a pull request',
 907 |           inputSchema: {
 908 |             type: 'object',
 909 |             properties: {
 910 |               owner: {
 911 |                 type: 'string',
 912 |                 description: 'Repository owner (username or organization)',
 913 |               },
 914 |               repo: {
 915 |                 type: 'string',
 916 |                 description: 'Repository name',
 917 |               },
 918 |               pull_number: {
 919 |                 type: 'number',
 920 |                 description: 'Pull request number',
 921 |               },
 922 |             },
 923 |             required: ['owner', 'repo', 'pull_number'],
 924 |             additionalProperties: false,
 925 |           },
 926 |         },
 927 |         {
 928 |           name: 'get-pull-request-status',
 929 |           description: 'Get the combined status of all status checks for a pull request',
 930 |           inputSchema: {
 931 |             type: 'object',
 932 |             properties: {
 933 |               owner: {
 934 |                 type: 'string',
 935 |                 description: 'Repository owner (username or organization)',
 936 |               },
 937 |               repo: {
 938 |                 type: 'string',
 939 |                 description: 'Repository name',
 940 |               },
 941 |               pull_number: {
 942 |                 type: 'number',
 943 |                 description: 'Pull request number',
 944 |               },
 945 |             },
 946 |             required: ['owner', 'repo', 'pull_number'],
 947 |             additionalProperties: false,
 948 |           },
 949 |         },
 950 |         {
 951 |           name: 'update-pull-request-branch',
 952 |           description: 'Update a pull request branch with the latest changes from the base branch',
 953 |           inputSchema: {
 954 |             type: 'object',
 955 |             properties: {
 956 |               owner: {
 957 |                 type: 'string',
 958 |                 description: 'Repository owner (username or organization)',
 959 |               },
 960 |               repo: {
 961 |                 type: 'string',
 962 |                 description: 'Repository name',
 963 |               },
 964 |               pull_number: {
 965 |                 type: 'number',
 966 |                 description: 'Pull request number',
 967 |               },
 968 |               expected_head_sha: {
 969 |                 type: 'string',
 970 |                 description: 'The expected SHA of the pull request\'s HEAD ref',
 971 |               },
 972 |             },
 973 |             required: ['owner', 'repo', 'pull_number'],
 974 |             additionalProperties: false,
 975 |           },
 976 |         },
 977 |         {
 978 |           name: 'get-pull-request-comments',
 979 |           description: 'Get the review comments on a pull request',
 980 |           inputSchema: {
 981 |             type: 'object',
 982 |             properties: {
 983 |               owner: {
 984 |                 type: 'string',
 985 |                 description: 'Repository owner (username or organization)',
 986 |               },
 987 |               repo: {
 988 |                 type: 'string',
 989 |                 description: 'Repository name',
 990 |               },
 991 |               pull_number: {
 992 |                 type: 'number',
 993 |                 description: 'Pull request number',
 994 |               },
 995 |             },
 996 |             required: ['owner', 'repo', 'pull_number'],
 997 |             additionalProperties: false,
 998 |           },
 999 |         },
1000 |         {
1001 |           name: 'get-pull-request-reviews',
1002 |           description: 'Get the reviews on a pull request',
1003 |           inputSchema: {
1004 |             type: 'object',
1005 |             properties: {
1006 |               owner: {
1007 |                 type: 'string',
1008 |                 description: 'Repository owner (username or organization)',
1009 |               },
1010 |               repo: {
1011 |                 type: 'string',
1012 |                 description: 'Repository name',
1013 |               },
1014 |               pull_number: {
1015 |                 type: 'number',
1016 |                 description: 'Pull request number',
1017 |               },
1018 |             },
1019 |             required: ['owner', 'repo', 'pull_number'],
1020 |             additionalProperties: false,
1021 |           },
1022 |         },
1023 |         // Search tools
1024 |         {
1025 |           name: 'search-code',
1026 |           description: 'Search for code across GitHub repositories',
1027 |           inputSchema: {
1028 |             type: 'object',
1029 |             properties: {
1030 |               q: {
1031 |                 type: 'string',
1032 |               },
1033 |               order: {
1034 |                 type: 'string',
1035 |                 enum: ['asc', 'desc'],
1036 |               },
1037 |               page: {
1038 |                 type: 'number',
1039 |                 minimum: 1,
1040 |               },
1041 |               per_page: {
1042 |                 type: 'number',
1043 |                 minimum: 1,
1044 |                 maximum: 100,
1045 |               },
1046 |             },
1047 |             required: ['q'],
1048 |             additionalProperties: false,
1049 |           },
1050 |         },
1051 |         {
1052 |           name: 'search-issues',
1053 |           description: 'Search for issues and pull requests across GitHub repositories',
1054 |           inputSchema: {
1055 |             type: 'object',
1056 |             properties: {
1057 |               q: {
1058 |                 type: 'string',
1059 |               },
1060 |               order: {
1061 |                 type: 'string',
1062 |                 enum: ['asc', 'desc'],
1063 |               },
1064 |               page: {
1065 |                 type: 'number',
1066 |                 minimum: 1,
1067 |               },
1068 |               per_page: {
1069 |                 type: 'number',
1070 |                 minimum: 1,
1071 |                 maximum: 100,
1072 |               },
1073 |               sort: {
1074 |                 type: 'string',
1075 |                 enum: [
1076 |                   'comments',
1077 |                   'reactions',
1078 |                   'reactions-+1',
1079 |                   'reactions--1',
1080 |                   'reactions-smile',
1081 |                   'reactions-thinking_face',
1082 |                   'reactions-heart',
1083 |                   'reactions-tada',
1084 |                   'interactions',
1085 |                   'created',
1086 |                   'updated',
1087 |                 ],
1088 |               },
1089 |             },
1090 |             required: ['q'],
1091 |             additionalProperties: false,
1092 |           },
1093 |         },
1094 |         {
1095 |           name: 'search-users',
1096 |           description: 'Search for users on GitHub',
1097 |           inputSchema: {
1098 |             type: 'object',
1099 |             properties: {
1100 |               q: {
1101 |                 type: 'string',
1102 |               },
1103 |               order: {
1104 |                 type: 'string',
1105 |                 enum: ['asc', 'desc'],
1106 |               },
1107 |               page: {
1108 |                 type: 'number',
1109 |                 minimum: 1,
1110 |               },
1111 |               per_page: {
1112 |                 type: 'number',
1113 |                 minimum: 1,
1114 |                 maximum: 100,
1115 |               },
1116 |               sort: {
1117 |                 type: 'string',
1118 |                 enum: ['followers', 'repositories', 'joined'],
1119 |               },
1120 |             },
1121 |             required: ['q'],
1122 |             additionalProperties: false,
1123 |           },
1124 |         },
1125 |         {
1126 |           name: 'get-license-info',
1127 |           description: 'Get information about commonly used licenses on GitHub',
1128 |           inputSchema: {
1129 |             type: 'object',
1130 |             properties: {},
1131 |             additionalProperties: false,
1132 |           },
1133 |         },
1134 |         {
1135 |           name: 'get-enterprise-stats',
1136 |           description: 'Get GitHub Enterprise statistics (only available for GitHub Enterprise)',
1137 |           inputSchema: {
1138 |             type: 'object',
1139 |             properties: {},
1140 |             additionalProperties: false,
1141 |           },
1142 |         },
1143 |       ],
1144 |     }));
1145 | 
1146 |     // Handle tool calls
1147 |     this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
1148 |       const { name, arguments: args } = request.params;
1149 |       let parsedArgs;
1150 | 
1151 |       try {
1152 |         // Parse arguments if they are provided as a string
1153 |         if (typeof args === 'string') {
1154 |           parsedArgs = safeJsonParse(args);
1155 |         } else {
1156 |           parsedArgs = args;
1157 |         }
1158 | 
1159 |         // Call the appropriate tool
1160 |         let result;
1161 |         switch (name) {
1162 |           // Repository tools
1163 |           case 'search-repositories':
1164 |             result = await searchRepositories(parsedArgs);
1165 |             break;
1166 |           case 'create-repository':
1167 |             result = await createRepository(parsedArgs);
1168 |             break;
1169 |           case 'update-repository':
1170 |             result = await updateRepository(parsedArgs);
1171 |             break;
1172 |           case 'delete-repository':
1173 |             result = await deleteRepository(parsedArgs);
1174 |             break;
1175 |           case 'create-branch':
1176 |             result = await createBranch(parsedArgs);
1177 |             break;
1178 |           case 'list-commits':
1179 |             result = await listCommits(parsedArgs);
1180 |             break;
1181 |           case 'list-workflows':
1182 |             result = await listWorkflows(parsedArgs);
1183 |             break;
1184 |           case 'list-workflow-runs':
1185 |             result = await listWorkflowRuns(parsedArgs);
1186 |             break;
1187 |           case 'trigger-workflow':
1188 |             result = await triggerWorkflow(parsedArgs);
1189 |             break;
1190 | 
1191 |           // File operations tools
1192 |           case 'create-or-update-file':
1193 |             result = await createOrUpdateFile(parsedArgs);
1194 |             break;
1195 |           case 'push-files':
1196 |             result = await pushFiles(parsedArgs);
1197 |             break;
1198 |           case 'get-file-contents':
1199 |             result = await getFileContents(parsedArgs);
1200 |             break;
1201 |           case 'fork-repository':
1202 |             result = await forkRepository(parsedArgs);
1203 |             break;
1204 |           case 'get-pull-request-files':
1205 |             result = await getPullRequestFiles(parsedArgs);
1206 |             break;
1207 | 
1208 |           // Issue tools
1209 |           case 'list-issues':
1210 |             result = await listIssues(parsedArgs);
1211 |             break;
1212 |           case 'get-issue':
1213 |             result = await getIssue(parsedArgs);
1214 |             break;
1215 |           case 'create-issue':
1216 |             result = await createIssue(parsedArgs);
1217 |             break;
1218 |           case 'update-issue':
1219 |             result = await updateIssue(parsedArgs);
1220 |             break;
1221 |           case 'add-issue-comment':
1222 |             result = await addIssueComment(parsedArgs);
1223 |             break;
1224 | 
1225 |           // Pull request tools
1226 |           case 'list-pull-requests':
1227 |             result = await listPullRequests(parsedArgs);
1228 |             break;
1229 |           case 'get-pull-request':
1230 |             result = await getPullRequest(parsedArgs);
1231 |             break;
1232 |           case 'create-pull-request':
1233 |             result = await createPullRequest(parsedArgs);
1234 |             break;
1235 |           case 'create-pull-request-review':
1236 |             result = await createPullRequestReview(parsedArgs);
1237 |             break;
1238 |           case 'merge-pull-request':
1239 |             result = await mergePullRequest(parsedArgs);
1240 |             break;
1241 |           case 'get-pull-request-status':
1242 |             result = await getPullRequestStatus(parsedArgs);
1243 |             break;
1244 |           case 'update-pull-request-branch':
1245 |             result = await updatePullRequestBranch(parsedArgs);
1246 |             break;
1247 |           case 'get-pull-request-comments':
1248 |             result = await getPullRequestComments(parsedArgs);
1249 |             break;
1250 |           case 'get-pull-request-reviews':
1251 |             result = await getPullRequestReviews(parsedArgs);
1252 |             break;
1253 | 
1254 |           // Search tools
1255 |           case 'search-code':
1256 |             result = await searchCode(parsedArgs);
1257 |             break;
1258 |           case 'search-issues':
1259 |             result = await searchIssues(parsedArgs);
1260 |             break;
1261 |           case 'search-users':
1262 |             result = await searchUsers(parsedArgs);
1263 |             break;
1264 |           case 'get-license-info':
1265 |             result = await getLicenseInfo();
1266 |             break;
1267 |           case 'get-enterprise-stats':
1268 |             result = await getEnterpriseStats();
1269 |             break;
1270 | 
1271 |           default:
1272 |             throw new McpError(
1273 |               ErrorCode.MethodNotFound,
1274 |               `Unknown tool: ${name}`
1275 |             );
1276 |         }
1277 | 
1278 |         return {
1279 |           content: [
1280 |             {
1281 |               type: 'text',
1282 |               text: JSON.stringify(result, null, 2),
1283 |             },
1284 |           ],
1285 |         };
1286 |       } catch (error: any) {
1287 |         // Handle errors
1288 |         if (error instanceof McpError) {
1289 |           throw error;
1290 |         }
1291 | 
1292 |         // Convert other errors to MCP errors
1293 |         throw new McpError(
1294 |           ErrorCode.InternalError,
1295 |           `Error executing tool ${name}: ${error.message}`
1296 |         );
1297 |       }
1298 |     });
1299 |   }
1300 | 
1301 |   /**
1302 |    * Start the MCP server
1303 |    */
1304 |   async run(): Promise<void> {
1305 |     try {
1306 |       const transport = new StdioServerTransport();
1307 |       await this.server.connect(transport);
1308 |       console.error('GitHub Enterprise MCP server running on stdio');
1309 | 
1310 |       // Handle process termination
1311 |       process.on('SIGINT', async () => {
1312 |         await this.server.close();
1313 |         process.exit(0);
1314 |       });
1315 |     } catch (error: any) {
1316 |       console.error('Failed to start GitHub Enterprise MCP server:', error);
1317 |       process.exit(1);
1318 |     }
1319 |   }
1320 | }
1321 | 
```