We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Om-Shree-0709/Github-MCP-Ts-SDK'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
// src/server.ts
import { config as loadEnv } from "dotenv";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { Octokit } from "@octokit/rest";
loadEnv();
// Configuration & Types
interface GitHubConfig {
token: string;
username?: string;
}
interface RepositoryInfo {
name: string;
fullName: string;
description: string | null;
stars: number;
forks: number;
watchers: number;
language: string | null;
visibility: string;
url: string;
createdAt: string;
updatedAt: string;
}
interface IssueInfo {
number: number;
title: string;
state: string;
author: string;
url: string;
createdAt: string;
body: string | null | undefined;
labels: string[];
}
interface PullRequestInfo {
number: number;
title: string;
state: string;
author: string;
url: string;
createdAt: string;
mergedAt: string | null;
baseBranch: string;
headBranch: string;
}
interface CommitInfo {
sha: string;
message: string;
author: string;
date: string;
url: string;
additions: number;
deletions: number;
}
interface UserInfo {
login: string;
name: string | null;
email: string | null;
bio: string | null;
followers: number;
following: number;
publicRepos: number;
avatarUrl: string;
url: string;
}
// GitHub Client Setup
const config: GitHubConfig = {
token: process.env.GITHUB_TOKEN || "",
username: process.env.GITHUB_USERNAME
};
if (!config.token) {
console.error("β GITHUB_TOKEN environment variable is required!");
console.log("Please set your GitHub token:");
console.log("export GITHUB_TOKEN=your_token_here");
process.exit(1);
}
const octokit = new Octokit({
auth: config.token,
userAgent: 'GitHub-MCP-Server/1.0.0'
});
// GitHub API Helper Functions
async function getAuthenticatedUser(): Promise<UserInfo> {
const { data } = await octokit.users.getAuthenticated();
return {
login: data.login,
name: data.name,
email: data.email,
bio: data.bio,
followers: data.followers,
following: data.following,
publicRepos: data.public_repos,
avatarUrl: data.avatar_url,
url: data.html_url
};
}
async function getRepositoryInfo(owner: string, repo: string): Promise<RepositoryInfo> {
const { data } = await octokit.repos.get({ owner, repo });
return {
name: data.name,
fullName: data.full_name,
description: data.description,
stars: data.stargazers_count,
forks: data.forks_count,
watchers: data.watchers_count,
language: data.language,
visibility: data.visibility || data.private ? 'private' : 'public',
url: data.html_url,
createdAt: data.created_at,
updatedAt: data.updated_at
};
}
async function getRepositoryIssues(owner: string, repo: string, state: 'open' | 'closed' | 'all' = 'open'): Promise<IssueInfo[]> {
const { data } = await octokit.issues.listForRepo({
owner,
repo,
state,
per_page: 100
});
return data.map(issue => ({
number: issue.number,
title: issue.title,
state: issue.state,
author: issue.user?.login || 'unknown',
url: issue.html_url,
createdAt: issue.created_at,
body: issue.body || null,
labels: issue.labels.map(label => typeof label === 'string' ? label : label.name || '')
}));
}
async function getRepositoryPullRequests(owner: string, repo: string, state: 'open' | 'closed' | 'all' = 'open'): Promise<PullRequestInfo[]> {
const { data } = await octokit.pulls.list({
owner,
repo,
state,
per_page: 100
});
return data.map(pr => ({
number: pr.number,
title: pr.title,
state: pr.state,
author: pr.user?.login || 'unknown',
url: pr.html_url,
createdAt: pr.created_at,
mergedAt: pr.merged_at,
baseBranch: pr.base.ref,
headBranch: pr.head.ref
}));
}
async function getRepositoryCommits(owner: string, repo: string, limit: number = 50): Promise<CommitInfo[]> {
const { data } = await octokit.repos.listCommits({
owner,
repo,
per_page: limit
});
return data.map(commit => ({
sha: commit.sha.substring(0, 7),
message: commit.commit.message.split('\n')[0], // First line only
author: commit.commit.author?.name || commit.author?.login || 'unknown',
date: commit.commit.author?.date || '',
url: commit.html_url,
additions: 0, // Would need additional API call
deletions: 0 // Would need additional API call
}));
}
async function searchRepositories(query: string, limit: number = 20): Promise<RepositoryInfo[]> {
const { data } = await octokit.search.repos({
q: query,
per_page: limit,
sort: 'stars',
order: 'desc'
});
return data.items.map(repo => ({
name: repo.name,
fullName: repo.full_name,
description: repo.description,
stars: repo.stargazers_count,
forks: repo.forks_count,
watchers: repo.watchers_count,
language: repo.language,
visibility: repo.private ? 'private' : 'public',
url: repo.html_url,
createdAt: repo.created_at || '',
updatedAt: repo.updated_at || ''
}));
}
async function getUserRepositories(username: string, type: 'all' | 'owner' | 'member' = 'all'): Promise<RepositoryInfo[]> {
const { data } = await octokit.repos.listForUser({
username,
type,
per_page: 100,
sort: 'updated'
});
return data.map(repo => ({
name: repo.name,
fullName: repo.full_name,
description: repo.description,
stars: repo.stargazers_count || 0,
forks: repo.forks_count || 0,
watchers: repo.watchers_count || 0,
language: repo.language || null,
visibility: repo.private ? 'private' : 'public',
url: repo.html_url,
createdAt: repo.created_at || '',
updatedAt: repo.updated_at || ''
}));
}
async function getUserInfo(username: string): Promise<UserInfo> {
const { data } = await octokit.users.getByUsername({ username });
return {
login: data.login,
name: data.name,
email: data.email,
bio: data.bio,
followers: data.followers,
following: data.following,
publicRepos: data.public_repos,
avatarUrl: data.avatar_url,
url: data.html_url
};
}
// Main Server Function
async function main() {
// Create MCP server with proper configuration
const server = new McpServer({
name: "github-mcp-server",
version: "1.0.0",
description: "A comprehensive GitHub MCP server for accessing GitHub data and performing operations"
});
// Register Tools
// 1. Get Authenticated User Info
server.registerTool(
"get_my_info",
{
description: "Get information about the authenticated GitHub user",
inputSchema: {}
},
async () => {
try {
const userInfo = await getAuthenticatedUser();
return {
content: [{
type: "text",
text: `π€ **My GitHub Profile**
**Username:** ${userInfo.login}
**Name:** ${userInfo.name || 'Not set'}
**Bio:** ${userInfo.bio || 'Not set'}
**Followers:** ${userInfo.followers}
**Following:** ${userInfo.following}
**Public Repositories:** ${userInfo.publicRepos}
**Profile URL:** ${userInfo.url}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error fetching user info: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// 2. Get Repository Information
server.registerTool(
"get_repo_info",
{
description: "Get detailed information about a GitHub repository",
inputSchema: {
owner: z.string().describe("Repository owner (username or organization)"),
repo: z.string().describe("Repository name")
}
},
async (args: { owner: string; repo: string }) => {
try {
const repoInfo = await getRepositoryInfo(args.owner, args.repo);
return {
content: [{
type: "text",
text: `π **Repository: ${repoInfo.fullName}**
**Description:** ${repoInfo.description || 'No description'}
**Language:** ${repoInfo.language || 'Unknown'}
**Visibility:** ${repoInfo.visibility}
**Stars:** β ${repoInfo.stars}
**Forks:** π΄ ${repoInfo.forks}
**Watchers:** π ${repoInfo.watchers}
**Created:** ${new Date(repoInfo.createdAt).toLocaleDateString()}
**Updated:** ${new Date(repoInfo.updatedAt).toLocaleDateString()}
**URL:** ${repoInfo.url}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error fetching repository info: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// 3. List Repository Issues
server.registerTool(
"list_repo_issues",
{
description: "List issues in a GitHub repository",
inputSchema: {
owner: z.string().describe("Repository owner"),
repo: z.string().describe("Repository name"),
state: z.enum(['open', 'closed', 'all']).optional().describe("Issue state filter")
}
},
async (args: { owner: string; repo: string; state?: 'open' | 'closed' | 'all' }) => {
try {
const issues = await getRepositoryIssues(args.owner, args.repo, args.state || 'open');
const issueList = issues.map(issue =>
`#${issue.number}: ${issue.title} (${issue.state}) - ${issue.author}`
).join('\n');
return {
content: [{
type: "text",
text: `π **Issues in ${args.owner}/${args.repo}** (${args.state || 'open'})
${issues.length === 0 ? 'No issues found.' : issueList}
**Total:** ${issues.length} issues`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error fetching issues: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// 4. List Repository Pull Requests
server.registerTool(
"list_repo_prs",
{
description: "List pull requests in a GitHub repository",
inputSchema: {
owner: z.string().describe("Repository owner"),
repo: z.string().describe("Repository name"),
state: z.enum(['open', 'closed', 'all']).optional().describe("PR state filter")
}
},
async (args: { owner: string; repo: string; state?: 'open' | 'closed' | 'all' }) => {
try {
const prs = await getRepositoryPullRequests(args.owner, args.repo, args.state || 'open');
const prList = prs.map(pr =>
`#${pr.number}: ${pr.title} (${pr.state}) - ${pr.author}`
).join('\n');
return {
content: [{
type: "text",
text: `π **Pull Requests in ${args.owner}/${args.repo}** (${args.state || 'open'})
${prs.length === 0 ? 'No pull requests found.' : prList}
**Total:** ${prs.length} pull requests`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error fetching pull requests: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// 5. List Repository Commits
server.registerTool(
"list_repo_commits",
{
description: "List recent commits in a GitHub repository",
inputSchema: {
owner: z.string().describe("Repository owner"),
repo: z.string().describe("Repository name"),
limit: z.number().min(1).max(100).optional().describe("Number of commits to fetch (1-100)")
}
},
async (args: { owner: string; repo: string; limit?: number }) => {
try {
const commits = await getRepositoryCommits(args.owner, args.repo, args.limit || 20);
const commitList = commits.map(commit =>
`${commit.sha}: ${commit.message} - ${commit.author} (${new Date(commit.date).toLocaleDateString()})`
).join('\n');
return {
content: [{
type: "text",
text: `π **Recent Commits in ${args.owner}/${args.repo}**
${commitList}
**Total:** ${commits.length} commits`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error fetching commits: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// 6. Search Repositories
server.registerTool(
"search_repositories",
{
description: "Search for repositories on GitHub",
inputSchema: {
query: z.string().describe("Search query (e.g., 'language:typescript', 'user:octocat')"),
limit: z.number().min(1).max(100).optional().describe("Number of results (1-100)")
}
},
async (args: { query: string; limit?: number }) => {
try {
const repos = await searchRepositories(args.query, args.limit || 20);
const repoList = repos.map(repo =>
`${repo.fullName} β ${repo.stars} - ${repo.description || 'No description'}`
).join('\n');
return {
content: [{
type: "text",
text: `π **Search Results for: "${args.query}"**
${repoList}
**Total:** ${repos.length} repositories found`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error searching repositories: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// 7. Get User Information
server.registerTool(
"get_user_info",
{
description: "Get information about a GitHub user",
inputSchema: {
username: z.string().describe("GitHub username")
}
},
async (args: { username: string }) => {
try {
const userInfo = await getUserInfo(args.username);
return {
content: [{
type: "text",
text: `π€ **User: ${userInfo.login}**
**Name:** ${userInfo.name || 'Not set'}
**Bio:** ${userInfo.bio || 'Not set'}
**Followers:** ${userInfo.followers}
**Following:** ${userInfo.following}
**Public Repositories:** ${userInfo.publicRepos}
**Profile URL:** ${userInfo.url}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error fetching user info: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// 8. List User Repositories
server.registerTool(
"list_user_repos",
{
description: "List repositories belonging to a user",
inputSchema: {
username: z.string().describe("GitHub username"),
type: z.enum(['all', 'owner', 'member']).optional().describe("Repository type filter")
}
},
async (args: { username: string; type?: 'all' | 'owner' | 'member' }) => {
try {
const repos = await getUserRepositories(args.username, args.type || 'all');
const repoList = repos.map(repo =>
`${repo.fullName} β ${repo.stars} - ${repo.visibility}`
).join('\n');
return {
content: [{
type: "text",
text: `π **Repositories by ${args.username}** (${args.type || 'all'})
${repoList}
**Total:** ${repos.length} repositories`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error fetching user repositories: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// 9. My Repositories
server.registerTool(
"get_my_repos",
{
description: "Get repositories belonging to the authenticated user",
inputSchema: {
type: z.enum(['all', 'owner', 'member']).optional().describe("Repository type filter")
}
},
async (args: { type?: 'all' | 'owner' | 'member' }) => {
try {
const userInfo = await getAuthenticatedUser();
const repos = await getUserRepositories(userInfo.login, args.type || 'all');
const repoList = repos.map(repo =>
`${repo.fullName} β ${repo.stars} - ${repo.visibility}`
).join('\n');
return {
content: [{
type: "text",
text: `π **My Repositories** (${args.type || 'all'})
${repoList}
**Total:** ${repos.length} repositories`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error fetching my repositories: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// 10. GitHub Statistics
server.registerTool(
"get_github_stats",
{
description: "Get comprehensive GitHub statistics for a user",
inputSchema: {
username: z.string().describe("GitHub username")
}
},
async (args: { username: string }) => {
try {
const userInfo = await getUserInfo(args.username);
const repos = await getUserRepositories(args.username, 'all');
const totalStars = repos.reduce((sum, repo) => sum + repo.stars, 0);
const totalForks = repos.reduce((sum, repo) => sum + repo.forks, 0);
const languages = repos.reduce((acc, repo) => {
if (repo.language) {
acc[repo.language] = (acc[repo.language] || 0) + 1;
}
return acc;
}, {} as Record<string, number>);
const topLanguages = Object.entries(languages)
.sort(([, a], [, b]) => b - a)
.slice(0, 5)
.map(([lang, count]) => `${lang}: ${count}`)
.join(', ');
return {
content: [{
type: "text",
text: `π **GitHub Statistics for ${args.username}**
**User Info:**
β’ Followers: ${userInfo.followers}
β’ Following: ${userInfo.following}
β’ Public Repos: ${userInfo.publicRepos}
**Repository Stats:**
β’ Total Repositories: ${repos.length}
β’ Total Stars Received: β ${totalStars}
β’ Total Forks: π΄ ${totalForks}
β’ Average Stars per Repo: ${repos.length > 0 ? (totalStars / repos.length).toFixed(1) : 0}
**Top Languages:**
${topLanguages || 'No language data available'}`
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `β Error fetching GitHub statistics: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
);
// Register Resources
// Repository Resource
server.resource(
"repository",
"github://repository/{owner}/{repo}",
{
name: "GitHub Repository",
description: "Access repository information and data",
mimeType: "application/json"
},
async (uri: URL) => {
try {
const uriString = uri.toString();
const match = uriString.match(/github:\/\/repository\/([^\/]+)\/([^\/]+)/);
if (!match) {
throw new Error("Invalid repository URI format");
}
const [, owner, repo] = match;
const repoInfo = await getRepositoryInfo(owner, repo);
return {
contents: [{
uri: uriString,
mimeType: "application/json",
text: JSON.stringify(repoInfo, null, 2)
}]
};
} catch (error) {
throw new Error(`Failed to fetch repository: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
);
// User Resource
server.resource(
"user",
"github://user/{username}",
{
name: "GitHub User",
description: "Access user profile information",
mimeType: "application/json"
},
async (uri: URL) => {
try {
const uriString = uri.toString();
const match = uriString.match(/github:\/\/user\/([^\/]+)/);
if (!match) {
throw new Error("Invalid user URI format");
}
const [, username] = match;
const userInfo = await getUserInfo(username);
return {
contents: [{
uri: uriString,
mimeType: "application/json",
text: JSON.stringify(userInfo, null, 2)
}]
};
} catch (error) {
throw new Error(`Failed to fetch user: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
);
// Register Prompts
// GitHub Query Prompt
server.prompt(
"github_query",
"Ask natural language questions about GitHub data",
{
query: z.string().describe("Natural language question about GitHub (e.g., 'Show me my most starred repositories')")
},
async (args: { query: string }) => {
const query = args.query.toLowerCase();
try {
if (query.includes('my') && (query.includes('repo') || query.includes('repository'))) {
const userInfo = await getAuthenticatedUser();
const repos = await getUserRepositories(userInfo.login, 'all');
const sortedRepos = repos.sort((a, b) => b.stars - a.stars).slice(0, 10);
const repoList = sortedRepos.map(repo =>
`${repo.fullName} β ${repo.stars} - ${repo.visibility}`
).join('\n');
return {
messages: [{
role: "user",
content: {
type: "text",
text: args.query
}
}, {
role: "assistant",
content: {
type: "text",
text: `π **Your Most Starred Repositories:**
${repoList}
**Total:** ${repos.length} repositories`
}
}]
};
}
if (query.includes('search') && query.includes('repo')) {
const searchQuery = query.replace(/search|repo|repositories?/gi, '').trim();
if (searchQuery) {
const repos = await searchRepositories(searchQuery, 10);
const repoList = repos.map(repo =>
`${repo.fullName} β ${repo.stars} - ${repo.description || 'No description'}`
).join('\n');
return {
messages: [{
role: "user",
content: {
type: "text",
text: args.query
}
}, {
role: "assistant",
content: {
type: "text",
text: `π **Search Results for "${searchQuery}":**
${repoList}
**Total:** ${repos.length} repositories found`
}
}]
};
}
}
return {
messages: [{
role: "user",
content: {
type: "text",
text: args.query
}
}, {
role: "assistant",
content: {
type: "text",
text: `I can help you with GitHub data! Here are some things I can do:
β’ Show your repositories and stats
β’ Search for repositories
β’ Get information about specific users or repos
β’ List issues and pull requests
β’ Show commit history
Try asking something like:
- "Show me my most starred repositories"
- "Search for TypeScript repositories"
- "What are the open issues in microsoft/vscode?"`
}
}]
};
} catch (error) {
return {
messages: [{
role: "user",
content: {
type: "text",
text: args.query
}
}, {
role: "assistant",
content: {
type: "text",
text: `β Error processing query: ${error instanceof Error ? error.message : 'Unknown error'}`
}
}]
};
}
}
);
// Start Server
const transport = new StdioServerTransport();
await server.connect(transport);
console.log("π GitHub MCP Server is running!");
console.log("π Available tools:");
console.log(" β’ get_my_info - Get authenticated user info");
console.log(" β’ get_repo_info - Get repository details");
console.log(" β’ list_repo_issues - List repository issues");
console.log(" β’ list_repo_prs - List pull requests");
console.log(" β’ list_repo_commits - List recent commits");
console.log(" β’ search_repositories - Search for repositories");
console.log(" β’ get_user_info - Get user information");
console.log(" β’ list_user_repos - List user repositories");
console.log(" β’ get_my_repos - List my repositories");
console.log(" β’ get_github_stats - Get user statistics");
console.log("π Available resources:");
console.log(" β’ github://repository/{owner}/{repo} - Repository data");
console.log(" β’ github://user/{username} - User profile data");
console.log("π¬ Available prompts:");
console.log(" β’ github_query - Natural language GitHub queries");
}
// Error Handling
main().catch((err) => {
console.error("β Server crashed:", err);
process.exit(1);
});