# GitHub Project MCP Server
A Model Context Protocol (MCP) server for managing GitHub projects and issues using GitHub's GraphQL API.
## Features
- List, create, update, and delete (close) GitHub issues
- Create issues with embedded images (upload local files or use URLs)
- Add/remove labels and assignees from issues
- List GitHub projects (ProjectsV2)
- Add/remove issues from projects
- Update project item fields (move between columns/status)
- Get project items and fields
- Get repository labels and user IDs
- Full GraphQL API integration for efficient data fetching
- Easy-to-use CLI with `gps` command
## Installation
### From PyPI (Recommended)
```bash
pip install github-project-mcp
```
### From Source
```bash
git clone <your-repo-url>
cd github-project-mcp
pip install .
```
### For Development
```bash
pip install -e .
```
### From Wheel
```bash
# Install Python `build`
pip install build
# Build the wheel
python -m build
# Install the wheel
pip install dist/github_project_mcp-0.1.0-py3-none-any.whl
```
## Quick Start
1. **Configure your GitHub token:**
```bash
gps config --token YOUR_GITHUB_TOKEN
```
2. **Test the connection:**
```bash
gps test
```
3. **Start the server:**
```bash
gps start
```
## CLI Commands
The `gps` command provides a comprehensive interface for managing the MCP server:
### Server Management
```bash
# Start the server
gps start
# Start with specific token
gps start --token YOUR_TOKEN
# Start as daemon (background process)
gps start --daemon
# Stop the server
Press Ctrl + C
# Check server status
gps status
```
### Configuration
```bash
# Configure GitHub token
gps config --token YOUR_TOKEN
# Show current configuration
gps config --show
# Clear configuration
gps config --clear
```
### Utilities
```bash
# Test GitHub API connection
gps test
# List available MCP tools
gps tools
```
## Integration with AI Coding Assistants
### Setup
The `gps` cli needs to be accessible from the system PATH, which typically means it should be installed globally or
in the active environment.
Option 1: Install globally with Python >=3.10
```
pip3 install github-project-mcp
```
Option 2: Use absolute path to virtual environment (venv or miniconda)
```
{
"mcpServers": {
"github-project-server": {
"command": "/path/to/your/gps",
"args": ["start"],
}
}
}
```
### Claude Desktop
Claude Desktop supports MCP servers natively. Configure it by editing the Claude Desktop configuration:
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
**Linux**: `~/.config/Claude/claude_desktop_config.json`
Add the following configuration:
```json
{
"mcpServers": {
"github-project": {
"command": "gps",
"args": ["start", "--token", "your_github_token_here"]
}
}
}
```
Or if you've configured the token with `gps config`:
```json
{
"mcpServers": {
"github-project": {
"command": "gps",
"args": ["start"]
}
}
}
```
After adding the configuration, restart Claude Desktop. You'll see the GitHub tools available in the tools menu.
### Cursor.ai
Cursor doesn't natively support MCP yet, but you can use the server as a local API:
1. Start the server as a daemon:
```bash
gps start --daemon
```
2. Create a wrapper script `cursor-github-helper.py`:
```python
import subprocess
import json
def query_github(command, params):
# Use the MCP server via subprocess
result = subprocess.run(
["gps", "query", "--json", json.dumps({"tool": command, "params": params})],
capture_output=True,
text=True
)
return json.loads(result.stdout)
```
3. Reference this in your Cursor rules or documentation for the AI to use.
### Windsurf (Codeium)
Windsurf supports custom tools through its API integration:
1. Install the package globally:
```bash
pip install github-project-mcp
```
2. Add to Windsurf's `~/.windsurf/config.json`:
```json
{
"mcpServers": {
"github-project-server": {
"command": "$PACKAGE_BIN_PATH/gps",
"args": ["start", "--token", "YOUR_GITHUB_TOKEN"]
}
}
}
```
3. In Windsurf chat, you can now reference GitHub operations:
```
@github list issues in owner/repo
@github create issue "Bug: Application crashes"
```
### Visual Studio Code
For VS Code, you can integrate the MCP server through extensions or terminal commands:
#### Option 1: VS Code Tasks
Add to `.vscode/tasks.json`:
```json
{
"version": "2.0.0",
"tasks": [
{
"label": "Start GitHub MCP Server",
"type": "shell",
"command": "gps start --daemon",
"problemMatcher": [],
"group": "build"
},
{
"label": "List GitHub Issues",
"type": "shell",
"command": "gps",
"args": ["query", "list_issues", "${input:owner}", "${input:repo}"],
"problemMatcher": []
}
],
"inputs": [
{
"id": "owner",
"type": "promptString",
"description": "Repository owner"
},
{
"id": "repo",
"type": "promptString",
"description": "Repository name"
}
]
}
```
#### Option 2: VS Code Extension (Continue.dev)
If using Continue.dev extension for AI assistance:
1. Install Continue.dev extension
2. Add to `~/.continue/config.json`:
```json
{
"tools": [
{
"name": "github",
"command": "gps",
"args": ["start"],
"description": "GitHub project management"
}
]
}
```
#### Option 3: GitHub Copilot Chat
While GitHub Copilot doesn't directly support MCP, you can create command shortcuts:
1. Create a `.github/copilot-instructions.md` file in your project:
```markdown
You have access to a GitHub MCP server. To use it:
- Start server: Run `gps start` in terminal
- List issues: Run `gps query list_issues OWNER REPO`
- Create issue: Run `gps query create_issue OWNER REPO "TITLE" "BODY"`
```
2. Copilot will recognize these commands and can help you use them.
### General Integration Pattern
For any IDE or assistant that doesn't natively support MCP, you can:
1. **Install the package**:
```bash
pip install github-project-mcp
```
2. **Start as a daemon**:
```bash
gps start --daemon
```
3. **Use the CLI programmatically**:
```bash
# List issues
gps query list_issues octocat hello-world
# Create an issue
gps query create_issue octocat hello-world "Bug Report" "Description here"
# Update an issue
gps query update_issue ISSUE_ID --state CLOSED
```
4. **Or use it in Python scripts**:
```python
from github_project_mcp import GitHubGraphQLClient
import os
client = GitHubGraphQLClient(os.getenv("GITHUB_TOKEN"))
issues = await client.list_issues("octocat", "hello-world")
```
## Available Tools
### Issue Management
- **list_issues**: List issues in a repository
- Parameters: `owner`, `repo`, `state` (OPEN/CLOSED)
- **create_issue**: Create a new issue
- Parameters: `owner`, `repo`, `title`, `body`, `labels`, `assignees`
- **create_issue_with_images**: Create an issue with embedded images
- Parameters: `owner`, `repo`, `title`, `body`, `images` (array), `labels`, `assignees`
- Images can be: local file paths, URLs, or objects with `path`/`url` and `alt` text
- **update_issue**: Update an existing issue
- Parameters: `issue_id`, `title`, `body`, `state`, `labels`, `assignees`
- **delete_issue**: Close an issue (GitHub doesn't support deletion)
- Parameters: `issue_id`
- **add_labels_to_issue**: Add labels to an issue
- Parameters: `issue_id`, `label_ids` (array)
- **remove_labels_from_issue**: Remove labels from an issue
- Parameters: `issue_id`, `label_ids` (array)
- **add_assignees_to_issue**: Add assignees to an issue
- Parameters: `issue_id`, `assignee_ids` (array)
- **remove_assignees_from_issue**: Remove assignees from an issue
- Parameters: `issue_id`, `assignee_ids` (array)
### Project Management
- **list_projects**: List projects in a repository
- Parameters: `owner`, `repo`
- **get_project_items**: Get items in a project
- Parameters: `project_id`
- **add_issue_to_project**: Add an issue to a project
- Parameters: `project_id`, `issue_id`
- **remove_issue_from_project**: Remove an issue from a project
- Parameters: `project_id`, `item_id`
- **update_project_item_field**: Update a project item's field (advanced)
- Parameters: `project_id`, `item_id`, `field_id`, `value` (option ID for single-select fields)
- **update_project_item_status**: Update a project item's status (Todo, In Progress, Done, etc.)
- Parameters: `project_id`, `item_id`, `status` (status name as text)
- **get_project_fields**: Get available fields in a project (including status options)
- Parameters: `project_id`
### Utilities
- **get_repo_id**: Get repository ID for GraphQL mutations
- Parameters: `owner`, `repo`
- **get_repository_labels**: Get all labels in a repository with their IDs
- Parameters: `owner`, `repo`
- **get_user_id**: Get a GitHub user's ID by username
- Parameters: `username`
## Example Usage
### List Open Issues
```json
{
"tool": "list_issues",
"arguments": {
"owner": "octocat",
"repo": "hello-world",
"state": "OPEN"
}
}
```
### Create New Issue
```json
{
"tool": "create_issue",
"arguments": {
"owner": "octocat",
"repo": "hello-world",
"title": "Bug: Application crashes on startup",
"body": "The application crashes when trying to start with the --debug flag"
}
}
```
### Update Issue
```json
{
"tool": "update_issue",
"arguments": {
"issue_id": "I_kwDOBFQLEs5XB1234",
"state": "CLOSED",
"body": "Fixed in PR #123"
}
}
```
### Add Labels to Issue
```json
{
"tool": "add_labels_to_issue",
"arguments": {
"issue_id": "I_kwDOBFQLEs5XB1234",
"label_ids": ["LA_kwDOBFQLEs7XB5678", "LA_kwDOBFQLEs7XB9012"]
}
}
```
### Add Issue to Project
```json
{
"tool": "add_issue_to_project",
"arguments": {
"project_id": "PVT_kwDOBFQLEs4XB1234",
"issue_id": "I_kwDOBFQLEs5XB1234"
}
}
```
### Update Issue Status in Project Board
```json
{
"tool": "update_project_item_status",
"arguments": {
"project_id": "PVT_kwDOBFQLEs4XB1234",
"item_id": "PVTI_lADOBFQLEs5XB1234",
"status": "In Progress"
}
}
```
Common status values: "Todo", "In Progress", "Done" (varies by project configuration)
### Advanced: Update Project Field Directly
```json
{
"tool": "update_project_item_field",
"arguments": {
"project_id": "PVT_kwDOBFQLEs4XB1234",
"item_id": "PVTI_lADOBFQLEs5XB1234",
"field_id": "PVTF_lADOBFQLEs5XB5678",
"value": "PVTSSF_lADOBFQLEs5XB9999"
}
}
```
Note: For status fields, use `update_project_item_status` instead for easier usage
### Create Issue with Images
```json
{
"tool": "create_issue_with_images",
"arguments": {
"owner": "octocat",
"repo": "hello-world",
"title": "Bug: Visual regression in dashboard",
"body": "The dashboard layout is broken on mobile devices",
"images": [
"/path/to/screenshot1.png",
"https://example.com/image.jpg",
{
"path": "/path/to/screenshot2.png",
"alt": "Mobile view screenshot"
}
]
}
}
```
Images are automatically uploaded to the repository and embedded in the issue body using Markdown
## GraphQL API Benefits
This server uses GitHub's GraphQL API v4 instead of REST API v3, providing:
- **Efficient data fetching**: Request only the fields you need
- **Fewer API calls**: Get related data in a single request
- **Better performance**: Reduced network overhead
- **Type safety**: Strongly typed schema
- **Real-time capabilities**: Support for subscriptions (future enhancement)
## Requirements
- Python 3.10+
- GitHub Personal Access Token with appropriate permissions
- Dependencies listed in `requirements.txt`
## License
MIT
## Publishing to PyPI
### Prerequisites
1. **Install build tools**:
```bash
pip install build twine
```
2. **Create PyPI account**:
- Register at https://pypi.org
- Generate API token in account settings
- Optionally, create TestPyPI account at https://test.pypi.org
### Publishing Steps
1. **Update version** in `setup.py` or `pyproject.toml`
2. **Build the package**:
```bash
python -m build
```
3. **Upload to TestPyPI** (optional, for testing):
```bash
twine upload --repository testpypi dist/*
```
- Username: `__token__`
- Password: Your TestPyPI API token
4. **Upload to PyPI**:
```bash
twine upload dist/*
```
- Username: `__token__`
- Password: Your PyPI API token
5. **Verify installation**:
```bash
pip install github-project-mcp
```
### Notes
- Clean the `dist/` directory between builds: `rm -rf dist/`
- Use semantic versioning (e.g., 0.1.0, 0.1.1, 1.0.0)
- Ensure all tests pass before publishing
- Consider using GitHub Actions for automated publishing
## Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.