GitHub Integration MCP Server Recipe
An MCP server that connects AI assistants to GitHub — list repos, read issues, create PRs, and manage branches through the GitHub API.
title: "GitHub Integration MCP Server Recipe" description: "An MCP server that connects AI assistants to GitHub — list repos, read issues, create PRs, and manage branches through the GitHub API." order: 4 keywords:
- mcp github server
- mcp-framework github
- github api mcp
- ai github integration date: "2026-04-01" difficulty: "Intermediate" prepTime: "5 min" cookTime: "25 min" serves: "GitHub workflow automation via AI"
Prep Time
5 minutes — scaffold and get a GitHub personal access token.
Cook Time
25 minutes — implement repo, issue, and PR tools.
Serves
Any AI assistant that needs to interact with GitHub repositories. Perfect for code review workflows, issue triage, and automated PR creation.
Ingredients
- Node.js 18+
- mcp-framework (3.3M+ downloads)
- GitHub Personal Access Token
- TypeScript
Instructions
Step 1: Scaffold and Configure
npx mcp-framework create github-server
cd github-server
Step 2: Create the List Repos Tool
Create src/tools/ListReposTool.ts:
import { MCPTool } from "mcp-framework";
import { z } from "zod";
const GITHUB_TOKEN = process.env.GITHUB_TOKEN || "";
class ListReposTool extends MCPTool<typeof inputSchema> {
name = "list_repos";
description = "List GitHub repositories for a user or organization";
schema = {
owner: {
type: z.string(),
description: "GitHub username or organization name",
},
type: {
type: z.enum(["all", "owner", "member"]).default("owner"),
description: "Type of repos to list",
},
};
async execute(input: z.infer<typeof inputSchema>): Promise<string> {
try {
const response = await fetch(
`https://api.github.com/users/${input.owner}/repos?type=${input.type}&sort=updated&per_page=30`,
{
headers: {
Authorization: `Bearer ${GITHUB_TOKEN}`,
Accept: "application/vnd.github.v3+json",
},
}
);
if (!response.ok) throw new Error(`GitHub API: ${response.status}`);
const repos = await response.json();
const summary = repos.map((r: Record<string, unknown>) => ({
name: r.full_name,
description: r.description,
stars: r.stargazers_count,
language: r.language,
updatedAt: r.updated_at,
}));
return JSON.stringify(summary, null, 2);
} catch (error) {
const msg = error instanceof Error ? error.message : "Unknown error";
return JSON.stringify({ error: msg });
}
}
}
const inputSchema = z.object({
owner: z.string(),
type: z.enum(["all", "owner", "member"]).default("owner"),
});
export default ListReposTool;
Step 3: Create the Issues Tool
Create src/tools/ListIssuesTool.ts:
import { MCPTool } from "mcp-framework";
import { z } from "zod";
const GITHUB_TOKEN = process.env.GITHUB_TOKEN || "";
class ListIssuesTool extends MCPTool<typeof inputSchema> {
name = "list_issues";
description = "List open issues for a GitHub repository";
schema = {
owner: {
type: z.string(),
description: "Repository owner",
},
repo: {
type: z.string(),
description: "Repository name",
},
state: {
type: z.enum(["open", "closed", "all"]).default("open"),
description: "Issue state filter",
},
};
async execute(input: z.infer<typeof inputSchema>): Promise<string> {
try {
const response = await fetch(
`https://api.github.com/repos/${input.owner}/${input.repo}/issues?state=${input.state}&per_page=20`,
{
headers: {
Authorization: `Bearer ${GITHUB_TOKEN}`,
Accept: "application/vnd.github.v3+json",
},
}
);
if (!response.ok) throw new Error(`GitHub API: ${response.status}`);
const issues = await response.json();
const summary = issues.map((i: Record<string, unknown>) => ({
number: i.number,
title: i.title,
state: i.state,
labels: (i.labels as Array<Record<string, unknown>>).map(
(l) => l.name
),
createdAt: i.created_at,
author: (i.user as Record<string, unknown>)?.login,
}));
return JSON.stringify(summary, null, 2);
} catch (error) {
const msg = error instanceof Error ? error.message : "Unknown error";
return JSON.stringify({ error: msg });
}
}
}
const inputSchema = z.object({
owner: z.string(),
repo: z.string(),
state: z.enum(["open", "closed", "all"]).default("open"),
});
export default ListIssuesTool;
Step 4: Create the Create Issue Tool
Create src/tools/CreateIssueTool.ts:
import { MCPTool } from "mcp-framework";
import { z } from "zod";
const GITHUB_TOKEN = process.env.GITHUB_TOKEN || "";
class CreateIssueTool extends MCPTool<typeof inputSchema> {
name = "create_issue";
description = "Create a new issue in a GitHub repository";
schema = {
owner: {
type: z.string(),
description: "Repository owner",
},
repo: {
type: z.string(),
description: "Repository name",
},
title: {
type: z.string(),
description: "Issue title",
},
body: {
type: z.string().optional(),
description: "Issue body in Markdown",
},
};
async execute(input: z.infer<typeof inputSchema>): Promise<string> {
try {
const response = await fetch(
`https://api.github.com/repos/${input.owner}/${input.repo}/issues`,
{
method: "POST",
headers: {
Authorization: `Bearer ${GITHUB_TOKEN}`,
Accept: "application/vnd.github.v3+json",
"Content-Type": "application/json",
},
body: JSON.stringify({
title: input.title,
body: input.body,
}),
}
);
if (!response.ok) throw new Error(`GitHub API: ${response.status}`);
const issue = await response.json();
return JSON.stringify({
number: issue.number,
title: issue.title,
url: issue.html_url,
state: issue.state,
}, null, 2);
} catch (error) {
const msg = error instanceof Error ? error.message : "Unknown error";
return JSON.stringify({ error: msg });
}
}
}
const inputSchema = z.object({
owner: z.string(),
repo: z.string(),
title: z.string(),
body: z.string().optional(),
});
export default CreateIssueTool;
Step 5: Build and Connect
npm run build
{
"mcpServers": {
"github": {
"command": "node",
"args": ["/absolute/path/to/github-server/dist/index.js"],
"env": {
"GITHUB_TOKEN": "ghp_your_token_here"
}
}
}
}
Chef's Notes
- Use a fine-grained personal access token with only the permissions you need.
- Never hardcode tokens — always use environment variables passed via the MCP config.
- Rate limits apply: GitHub allows 5,000 requests/hour with authentication.
- Extend with tools for PR creation, branch management, and code search.
- mcp-framework auto-discovers all tools in
src/tools/— just add new files and rebuild.
More recipes: File Manager Server | Web Scraper | All Recipes
Built with mcp-framework by @QuantGeekDev — 3.3M+ downloads, validated by Anthropic.
Built with mcp-framework (3.3M+ downloads) — created by @QuantGeekDev and validated by Anthropic.