A Model Context Protocol (MCP) server for automating GitHub Projects v2 workflows - create projects, milestones, issues, and iterations programmatically.
Works with: Claude Desktop (desktop app) and Claude Code (VS Code extension)
- ✅ Create Projects: Set up new GitHub Projects v2 boards
- ✅ Create Milestones: Organize work into milestones
- ✅ Create Issues: Bulk create issues with milestones and labels
- ✅ Add to Projects: Automatically add issues to project boards
- ✅ Iteration Fields: Create weekly/sprint iteration fields
- ✅ Assign Iterations: Distribute issues across sprints
- ✅ Sub-issue Management: Add, remove, and reprioritize sub-issues
- ✅ Update Status: Change project item status with human-readable values
- ✅ Project Status Updates: Add and retrieve project-level status updates (On Track, At Risk, etc.)
- ✅ Update Project: Modify project settings like title, description, and visibility
- ✅ Get Info: Retrieve repository and project metadata
- Node.js 20+
- GitHub Personal Access Token with
repoandprojectscopes
Use npx to run the server directly without installation:
npx -y @joaodotwork/plantas-github-projects-mcpnpm install -g @joaodotwork/plantas-github-projects-mcpgit clone https://github.com/joaodotwork/plantas-github-projects-mcp.git
cd plants-github-projects-mcp
npm install
npm run buildCreate a .env file or set environment variable:
export GITHUB_TOKEN=ghp_your_token_hereFor Claude Code (CLI):
# Using npx (Recommended)
claude mcp add github-projects --env GITHUB_TOKEN=ghp_your_token_here -- npx -y @joaodotwork/plantas-github-projects-mcpFor Gemini CLI:
# Using npx
gemini mcp add github-projects npx -e GITHUB_TOKEN=ghp_your_token_here -- -y @joaodotwork/plantas-github-projects-mcpFor Claude Desktop: Add to ~/Library/Application Support/Claude/claude_desktop_config.json
See INSTALL.md for detailed platform-specific instructions.
{
"mcpServers": {
"github-projects": {
"command": "npx",
"args": ["-y", "@joaodotwork/plantas-github-projects-mcp"],
"env": {
"GITHUB_TOKEN": "ghp_your_token_here"
}
}
}
}Claude Desktop: Completely quit and reopen the app
Claude Code: Reload VS Code window (Cmd+Shift+P → "Developer: Reload Window")
Create a new GitHub Projects v2 board.
Parameters:
owner(string, required): GitHub username or organizationtitle(string, required): Project titledescription(string, optional): Project description
Example:
{
"owner": "joaodotwork",
"title": "v1.0 Production Release",
"description": "Sprint to ship v1.0"
}Returns:
{
"id": "PVT_kwHOAwJiCM4BNC20",
"number": 7,
"title": "v1.0 Production Release",
"url": "https://github.com/users/joaodotwork/projects/7"
}Create a milestone in a repository.
Parameters:
owner(string, required): Repository ownerrepo(string, required): Repository nametitle(string, required): Milestone titledescription(string, optional): Milestone descriptiondueOn(string, optional): Due date in ISO 8601 format
Example:
{
"owner": "joaodotwork",
"repo": "dpds-arkiv",
"title": "Epic 1: GitHub Metadata Workflow",
"description": "Automate GitHub Projects sync (1 issue)"
}Returns:
{
"id": "MI_kwDOPxqaGM4A3o8i",
"number": 4,
"title": "Epic 1: GitHub Metadata Workflow",
"url": "https://github.com/joaodotwork/dpds-arkiv/milestone/4"
}Create an issue with optional milestone, labels, and assignees.
Parameters:
owner(string, required): Repository ownerrepo(string, required): Repository nametitle(string, required): Issue titlebody(string, required): Issue body (markdown)milestoneNumber(number, optional): Milestone numberlabelIds(string[], optional): Array of label IDsassignees(string[], optional): Array of usernames
Example:
{
"owner": "joaodotwork",
"repo": "dpds-arkiv",
"title": "Implement GitHub Projects Sync Workflow",
"body": "**Epic:** GitHub Metadata Workflow\n...",
"milestoneNumber": 4,
"labelIds": ["LA_kwDOPxqaGM8AAAACVcj5iQ"],
"assignees": ["joaodotwork"]
}Returns:
{
"id": "I_kwDOPxqaGM6RkGzw",
"number": 80,
"title": "Implement GitHub Projects Sync Workflow",
"url": "https://github.com/joaodotwork/dpds-arkiv/issues/80"
}Add an issue to a Projects v2 board.
Parameters:
projectId(string, required): Project node IDissueId(string, required): Issue node ID
Example:
{
"projectId": "PVT_kwHOAwJiCM4BNC20",
"issueId": "I_kwDOPxqaGM6RkGzw"
}Returns:
{
"id": "PVTI_lAHOAwJiCM4BNC20zgXYZ..."
}Create an iteration field with weekly sprints.
Parameters:
projectId(string, required): Project node IDfieldName(string, required): Field name (e.g., "Sprint")duration(number, required): Duration in days (typically 7)startDate(string, required): Start date (YYYY-MM-DD)iterations(array, required): Array of iteration definitions
Example:
{
"projectId": "PVT_kwHOAwJiCM4BNC20",
"fieldName": "Sprint",
"duration": 7,
"startDate": "2026-01-20",
"iterations": [
{ "title": "Week 1", "startDate": "2026-01-20", "duration": 7 },
{ "title": "Week 2", "startDate": "2026-01-27", "duration": 7 },
{ "title": "Week 3", "startDate": "2026-02-03", "duration": 7 }
]
}Returns:
{
"id": "PVTIF_lAHOAwJiCM4BNC20zg8J544",
"name": "Sprint",
"configuration": {
"iterations": [
{
"id": "bab3ba50",
"title": "Week 1",
"startDate": "2026-01-20",
"duration": 7
},
...
]
}
}Assign an issue to a specific iteration.
Parameters:
owner(string, required): Repository ownerrepo(string, required): Repository nameprojectNumber(number, required): Project numberissueNumber(number, required): Issue numberfieldId(string, required): Iteration field IDiterationId(string, required): Iteration ID
Example:
{
"owner": "joaodotwork",
"repo": "dpds-arkiv",
"projectNumber": 7,
"issueNumber": 80,
"fieldId": "PVTIF_lAHOAwJiCM4BNC20zg8J544",
"iterationId": "bab3ba50"
}Add a sub-issue to a parent issue.
Parameters:
issueId(string, required): Node ID of the parent issuesubIssueId(string, optional): Node ID of the sub-issuesubIssueUrl(string, optional): URL of the sub-issuereplaceParent(boolean, optional): Replace parent issue if one already exists
Example:
{
"issueId": "I_kwDOPxqaGM6RkGzw",
"subIssueId": "I_kwDOPxqaGM6RkHAB"
}Returns:
{
"success": true,
"message": "Sub-issue added successfully"
}Remove a sub-issue from a parent issue.
Parameters:
issueId(string, required): Node ID of the parent issuesubIssueId(string, required): Node ID of the sub-issue to remove
Example:
{
"issueId": "I_kwDOPxqaGM6RkGzw",
"subIssueId": "I_kwDOPxqaGM6RkHAB"
}Returns:
{
"success": true,
"message": "Sub-issue removed successfully"
}Reprioritize a sub-issue within a parent issue.
Parameters:
issueId(string, required): Node ID of the parent issuesubIssueId(string, required): Node ID of the sub-issue to reprioritizeafterId(string, optional): ID of the sub-issue to be prioritized afterbeforeId(string, optional): ID of the sub-issue to be prioritized before
Note: Specify either afterId OR beforeId, not both.
Example:
{
"issueId": "I_kwDOPxqaGM6RkGzw",
"subIssueId": "I_kwDOPxqaGM6RkHAB",
"afterId": "I_kwDOPxqaGM6RkHCD"
}Returns:
{
"success": true,
"message": "Sub-issue reprioritized successfully"
}Update the status of a project item using human-readable status values.
Parameters:
projectId(string, required): Project node IDitemId(string, required): Project item node IDstatus(string, required): Human-readable status (e.g., "Todo", "In Progress", "Done")
Note: The tool automatically finds the Status field and matches the status name (case-insensitive). It will show available options if the status is not found.
Example:
{
"projectId": "PVT_kwHOAwJiCM4BNC20",
"itemId": "PVTI_lAHOAwJiCM4BNC20zgXYZ...",
"status": "In Progress"
}Returns:
{
"success": true,
"message": "Status updated to 'In Progress'",
"itemId": "PVTI_lAHOAwJiCM4BNC20zgXYZ..."
}Update project settings like title, description, README, or visibility.
Parameters:
projectId(string, required): Project node IDtitle(string, optional): New project titleshortDescription(string, optional): New short descriptionreadme(string, optional): New README content (markdown)public(boolean, optional): Set project visibility (true = public, false = private)
Example:
{
"projectId": "PVT_kwHOAwJiCM4BNC20",
"title": "Q1 2025 Product Launch",
"shortDescription": "Sprint planning for v2.0 release",
"public": true
}Returns:
{
"success": true,
"message": "Project settings updated successfully",
"project": {
"id": "PVT_kwHOAwJiCM4BNC20",
"title": "Q1 2025 Product Launch",
"shortDescription": "Sprint planning for v2.0 release",
"public": true,
"url": "https://github.com/users/joaodotwork/projects/7"
}
}Get repository ID, labels, and milestones.
Parameters:
owner(string, required): Repository ownerrepo(string, required): Repository name
Example:
{
"owner": "joaodotwork",
"repo": "dpds-arkiv"
}Returns:
{
"id": "R_kgDOPxqaGA",
"name": "dpds-arkiv",
"labels": {
"nodes": [
{ "id": "LA_kwDOPxqaGM8...", "name": "priority:high" },
...
]
},
"milestones": {
"nodes": [
{ "id": "MI_kwDOPxqaGM4...", "number": 4, "title": "Epic 1..." },
...
]
}
}Get project ID, fields, and iteration IDs.
Parameters:
owner(string, required): Project ownerprojectNumber(number, required): Project number
Example:
{
"owner": "joaodotwork",
"projectNumber": 7
}Returns:
{
"id": "PVT_kwHOAwJiCM4BNC20",
"title": "v1.0 Production Release",
"number": 7,
"fields": {
"nodes": [
{
"id": "PVTIF_lAHOAwJiCM4BNC20zg8J544",
"name": "Sprint",
"dataType": "ITERATION",
"configuration": {
"iterations": [...]
}
},
...
]
}
}Create a status update for a project board.
Parameters:
projectId(string, required): Project node IDstatus(string, required): The status level (INACTIVE,ON_TRACK,AT_RISK,OFF_TRACK,COMPLETE)body(string, optional): Status update body (markdown)startDate(string, optional): Start date (YYYY-MM-DD)targetDate(string, optional): Target date (YYYY-MM-DD)
Example:
{
"projectId": "PVT_kwHOAwJiCM4BNC20",
"status": "ON_TRACK",
"body": "Project is proceeding as planned. All milestones for this week are met."
}Get recent status updates for a project.
Parameters:
projectId(string, required): Project node IDlimit(number, optional): Number of updates to retrieve (default: 5)
Example:
{
"projectId": "PVT_kwHOAwJiCM4BNC20",
"limit": 3
}// 1. Create project
const project = await create_project({
owner: "joaodotwork",
title: "v1.0 Production Release",
description: "Sprint to ship v1.0"
});
// 2. Create milestones
const milestone1 = await create_milestone({
owner: "joaodotwork",
repo: "dpds-arkiv",
title: "Epic 1: GitHub Metadata Workflow",
description: "Automate GitHub Projects sync (1 issue)"
});
// 3. Get repository info (for label IDs)
const repoInfo = await get_repository_info({
owner: "joaodotwork",
repo: "dpds-arkiv"
});
// 4. Create issue
const issue = await create_issue({
owner: "joaodotwork",
repo: "dpds-arkiv",
title: "Implement GitHub Projects Sync Workflow",
body: "...",
milestoneNumber: milestone1.number,
labelIds: [repoInfo.labels.nodes[0].id],
assignees: ["joaodotwork"]
});
// 5. Add issue to project
await add_issue_to_project({
projectId: project.id,
issueId: issue.id
});
// 6. Create iteration field
const iterationField = await create_iteration_field({
projectId: project.id,
fieldName: "Sprint",
duration: 7,
startDate: "2026-01-20",
iterations: [
{ title: "Week 1", startDate: "2026-01-20", duration: 7 },
{ title: "Week 2", startDate: "2026-01-27", duration: 7 },
{ title: "Week 3", startDate: "2026-02-03", duration: 7 }
]
});
// 7. Assign issue to iteration
await assign_issue_to_iteration({
owner: "joaodotwork",
repo: "dpds-arkiv",
projectNumber: project.number,
issueNumber: issue.number,
fieldId: iterationField.id,
iterationId: iterationField.configuration.iterations[0].id
});const issues = [
{
title: "Issue 1",
body: "Description...",
milestoneNumber: 4
},
{
title: "Issue 2",
body: "Description...",
milestoneNumber: 5
}
];
for (const issueData of issues) {
const issue = await create_issue({
owner: "joaodotwork",
repo: "dpds-arkiv",
...issueData
});
await add_issue_to_project({
projectId: "PVT_kwHOAwJiCM4BNC20",
issueId: issue.id
});
}- Check that your
GITHUB_TOKENis set correctly - Verify the token has
repoandprojectscopes - Token format should be
ghp_...(Personal Access Token)
- Ensure the issue has been added to the project first using
add_issue_to_project - Verify the
projectNumberis correct
- Check Claude Desktop config file path
- Verify JSON syntax in config file
- Restart Claude Desktop completely
- Check logs:
~/Library/Logs/Claude/mcp*.log
# Install dependencies
npm install
# Build
npm run build
# Watch mode
npm run dev
# Test locally
node dist/index.jsMIT
João Doria de Souza (@joaodotwork)
Built with the MCP SDK - Model Context Protocol for Claude Desktop