GitHub Projects MCP Server
- src
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import {
issueOperations,
projectOperations,
repositoryOperations,
} from "./operations/index.js";
import {
CreateIssueSchema,
GetIssueSchema,
ListIssuesSchema,
UpdateIssueSchema,
} from "./operations/issues.js";
import {
AddProjectV2DraftIssueSchema,
AddProjectV2ItemByIdSchema,
ArchiveProjectV2ItemSchema,
ClearProjectV2ItemFieldValueSchema,
ConvertProjectV2DraftIssueToIssueSchema,
CopyProjectV2Schema,
CreateProjectV2FieldSchema,
CreateProjectV2Schema,
DeleteProjectV2FieldSchema,
DeleteProjectV2ItemSchema,
DeleteProjectV2Schema,
GetProjectColumnsSchema,
GetProjectFieldsSchema,
GetProjectItemsSchema,
GetProjectSchema,
ListProjectsSchema,
MarkProjectV2AsTemplateSchema,
UnarchiveProjectV2ItemSchema,
UnmarkProjectV2AsTemplateSchema,
UpdateProjectItemFieldValueSchema,
UpdateProjectV2FieldSchema,
UpdateProjectV2ItemPositionSchema,
UpdateProjectV2Schema,
UpdateProjectV2StatusUpdateSchema,
} from "./operations/projects.js";
import {
GetRepositorySchema,
ListRepositoriesSchema,
} from "./operations/repositories.js";
type GetIssueParams = typeof GetIssueSchema;
type ListIssuesParams = typeof ListIssuesSchema;
type CreateIssueParams = typeof CreateIssueSchema;
type UpdateIssueParams = typeof UpdateIssueSchema;
type ListRepositoriesParams = typeof ListRepositoriesSchema;
type GetRepositoryParams = typeof GetRepositorySchema;
type GetProjectParams = typeof GetProjectSchema;
type ListProjectsParams = typeof ListProjectsSchema;
type GetProjectColumnsParams = typeof GetProjectColumnsSchema;
type GetProjectFieldsParams = typeof GetProjectFieldsSchema;
const server = new McpServer(
{
name: "github-mcp-server",
version: "1.0.0",
},
{
capabilities: {
prompts: {},
tools: {},
},
},
);
// Register all prompts with the server
server.prompt(
"create-sprint-project",
{
sprintName: z
.string()
.describe("Name of the sprint (e.g., 'Sprint 23', 'Q2 Sprint 1')"),
startDate: z.string().describe("Start date of the sprint (ISO format)"),
duration: z
.string()
.describe("Duration of sprint in days (typically 7, 14, or 30)"),
goals: z.string().optional().describe("Primary goals for this sprint"),
},
({ sprintName, startDate, duration, goals }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Create a new Sprint (iteration) project for Agile development with the following details:
- Sprint Name: ${sprintName}
- Start Date: ${startDate}
- Duration: ${duration} days${goals ? `\n- Goals: ${goals}` : ""}`,
},
},
],
}),
);
server.prompt(
"manage-sprint-backlog",
{
projectId: z.string().describe("GitHub Project ID to manage"),
filterStatus: z
.string()
.optional()
.describe("Filter issues by status (e.g., 'Todo', 'In Progress')"),
prioritizationStrategy: z
.string()
.optional()
.describe(
"Strategy for prioritization (e.g., 'value-based', 'effort-based')",
),
},
({ projectId, filterStatus, prioritizationStrategy }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Organize and prioritize issues in the sprint backlog:
- Project ID: ${projectId}${filterStatus ? `\n- Filter Status: ${filterStatus}` : ""}${prioritizationStrategy ? `\n- Prioritization Strategy: ${prioritizationStrategy}` : ""}`,
},
},
],
}),
);
server.prompt(
"track-sprint-progress",
{
projectId: z.string().describe("GitHub Project ID to track"),
includeBurndown: z
.string()
.optional()
.describe("Whether to include burndown metrics"),
highlightBlockers: z
.string()
.optional()
.describe("Whether to highlight blocked issues"),
},
({ projectId, includeBurndown, highlightBlockers }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Generate a status report of the current sprint progress:
- Project ID: ${projectId}${includeBurndown ? "\n- Include burndown metrics" : ""}${highlightBlockers ? "\n- Highlight blocked issues" : ""}`,
},
},
],
}),
);
server.prompt(
"prepare-sprint-retrospective",
{
completedProjectId: z
.string()
.describe("GitHub Project ID of the completed sprint"),
includeMetrics: z
.string()
.optional()
.describe("Include completion metrics and statistics"),
createNextSprint: z
.string()
.optional()
.describe("Automatically create next sprint project"),
},
({ completedProjectId, includeMetrics, createNextSprint }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Prepare a retrospective report and plan for the next sprint:
- Completed Project ID: ${completedProjectId}${includeMetrics ? "\n- Include completion metrics and statistics" : ""}${createNextSprint ? "\n- Automatically create next sprint project" : ""}`,
},
},
],
}),
);
server.prompt(
"create-project-template",
{
templateName: z.string().describe("Name for the template"),
customFields: z
.string()
.optional()
.describe("Custom fields to include (e.g., 'Story Points', 'Priority')"),
statusColumns: z
.string()
.optional()
.describe(
"Status columns to create (e.g., 'Todo,In Progress,Review,Done')",
),
},
({ templateName, customFields, statusColumns }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Create a reusable project template for future sprints:
- Template Name: ${templateName}${customFields ? `\n- Custom Fields: ${customFields}` : ""}${statusColumns ? `\n- Status Columns: ${statusColumns}` : ""}`,
},
},
],
}),
);
server.prompt(
"review-code",
{
code: z.string().describe("Code to review"),
},
({ code }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `Please review this code:\n\n${code}`,
},
},
],
}),
);
// Define tools metadata
server.tool<GetRepositoryParams>(
"get-repository",
"Get a GitHub repository by owner and name",
GetRepositorySchema,
async (params) => {
const result = await repositoryOperations.getRepository(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool<ListRepositoriesParams>(
"list-repositories",
"List repositories for a user",
ListRepositoriesSchema,
async (params) => {
const result = await repositoryOperations.listRepositories(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool<GetProjectParams>(
"get-project",
"Get a GitHub Project by ID",
GetProjectSchema,
async (input) => {
const result = await projectOperations.getProject(input);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool<ListProjectsParams>(
"list-projects",
"List GitHub Projects for a user",
ListProjectsSchema,
async (params) => {
const result = await projectOperations.listProjects(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool<GetProjectColumnsParams>(
"get-project-columns",
"Get status columns for a GitHub Project",
GetProjectColumnsSchema,
async (params) => {
const result = await projectOperations.getProjectColumns(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool<GetProjectFieldsParams>(
"get-project-fields",
"Get fields for a GitHub Project",
GetProjectFieldsSchema,
async (params) => {
const result = await projectOperations.getProjectFields(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"get-project-items",
"Get items (issues) from a GitHub Project",
GetProjectItemsSchema,
async (params) => {
const result = await projectOperations.getProjectItems(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"update-project-item-field",
"Update a field value for a project item",
UpdateProjectItemFieldValueSchema,
async (params) => {
const result = await projectOperations.updateProjectItemFieldValue(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"create-project",
"Create a new GitHub Project",
CreateProjectV2Schema,
async (params) => {
const result = await projectOperations.createProjectV2(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"update-project",
"Update an existing GitHub Project",
UpdateProjectV2Schema,
async (params) => {
const result = await projectOperations.updateProjectV2(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"delete-project",
"Delete a GitHub Project",
DeleteProjectV2Schema,
async (params) => {
const result = await projectOperations.deleteProjectV2(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"copy-project",
"Copy a GitHub Project",
CopyProjectV2Schema,
async (params) => {
const result = await projectOperations.copyProjectV2(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"add-draft-issue",
"Add a draft issue to a GitHub Project",
AddProjectV2DraftIssueSchema,
async (params) => {
const result = await projectOperations.addProjectV2DraftIssue(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"convert-draft-issue",
"Convert a draft issue to a regular issue",
ConvertProjectV2DraftIssueToIssueSchema,
async (params) => {
const result =
await projectOperations.convertProjectV2DraftIssueToIssue(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"add-item-to-project",
"Add an existing issue or PR to a GitHub Project",
AddProjectV2ItemByIdSchema,
async (params) => {
const result = await projectOperations.addProjectV2ItemById(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"update-item-position",
"Update the position of an item in a GitHub Project",
UpdateProjectV2ItemPositionSchema,
async (params) => {
const result = await projectOperations.updateProjectV2ItemPosition(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"delete-project-item",
"Remove an item from a GitHub Project",
DeleteProjectV2ItemSchema,
async (params) => {
const result = await projectOperations.deleteProjectV2Item(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"create-project-field",
"Create a new field in a GitHub Project",
CreateProjectV2FieldSchema,
async (params) => {
const result = await projectOperations.createProjectV2Field(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"update-project-field",
"Update a field in a GitHub Project",
UpdateProjectV2FieldSchema,
async (params) => {
const result = await projectOperations.updateProjectV2Field(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"delete-project-field",
"Delete a field from a GitHub Project",
DeleteProjectV2FieldSchema,
async (params) => {
const result = await projectOperations.deleteProjectV2Field(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"update-project-status",
"Update the status of a GitHub Project",
UpdateProjectV2StatusUpdateSchema,
async (params) => {
const result = await projectOperations.updateProjectV2StatusUpdate(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"archive-project-item",
"Archive an item in a GitHub Project",
ArchiveProjectV2ItemSchema,
async (params) => {
const result = await projectOperations.archiveProjectV2Item(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"unarchive-project-item",
"Unarchive an item in a GitHub Project",
UnarchiveProjectV2ItemSchema,
async (params) => {
const result = await projectOperations.unarchiveProjectV2Item(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"clear-item-field-value",
"Clear a field value for an item in a GitHub Project",
ClearProjectV2ItemFieldValueSchema,
async (params) => {
const result = await projectOperations.clearProjectV2ItemFieldValue(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"mark-project-as-template",
"Mark a GitHub Project as a template",
MarkProjectV2AsTemplateSchema,
async (params) => {
const result = await projectOperations.markProjectV2AsTemplate(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool(
"unmark-project-as-template",
"Unmark a GitHub Project as a template",
UnmarkProjectV2AsTemplateSchema,
async (params) => {
const result = await projectOperations.unmarkProjectV2AsTemplate(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool<GetIssueParams>(
"get-issue",
"Get a GitHub issue by number",
GetIssueSchema,
async (params) => {
const result = await issueOperations.getIssue(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool<ListIssuesParams>(
"list-issues",
"List issues for a repository",
ListIssuesSchema,
async (params) => {
const result = await issueOperations.listIssues(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool<CreateIssueParams>(
"create-issue",
"Create a new GitHub issue",
CreateIssueSchema,
async (params) => {
const result = await issueOperations.createIssue(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
server.tool<UpdateIssueParams>(
"update-issue",
"Update an existing GitHub issue",
UpdateIssueSchema,
async (params) => {
const result = await issueOperations.updateIssue(params);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
},
);
/**
* Start the server using stdio transport.
* This allows the server to communicate via standard input/output streams.
*/
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});